1. 概述

Java 8 引入了全新的日期时间 API java.time,但实际开发中我们仍需处理新旧 API 的转换问题。本文将系统介绍 java.util.Datejava.time.LocalDate/LocalDateTime 之间的转换方法,以及 java.sql.Date 的处理技巧。

2. 将java.util.Date转换为java.time.LocalDate

2.1. 使用toInstant()和toLocalDate()

Java 8 为 Date 新增了 toInstant() 方法,这是转换的核心。注意:**Instant 对象与时区无关**,转换时必须指定时区:

public LocalDate convertToLocalDateViaInstant(Date dateToConvert) {
    return dateToConvert.toInstant()
      .atZone(ZoneId.systemDefault())
      .toLocalDate();
}

⚠️ 处理1582年10月10日之前的日期时,需设置格里高利历:

GregorianCalendar calendar = new GregorianCalendar();
calendar.setGregorianChange(new Date(Long.MIN_VALUE));
Date dateToConvert = calendar.getTime();

2.2. 使用ofEpochMilli()和toLocalDate()

另一种创建 Instant 的方式,效果与上节相同:

public LocalDate convertToLocalDateViaMilisecond(Date dateToConvert) {
    return Instant.ofEpochMilli(dateToConvert.getTime())
      .atZone(ZoneId.systemDefault())
      .toLocalDate();
}

2.3. 使用ofInstant()(Java 9+)

Java 9 提供了更简洁的转换方法:

public LocalDate convertToLocalDate(Date dateToConvert) {
    return LocalDate.ofInstant(
      dateToConvert.toInstant(), ZoneId.systemDefault());
}

3. 将java.util.Date转换为java.time.LocalDateTime

3.1. 使用toInstant()和toLocalDateTime()

通过 ZonedDateTime 中转转换:

public LocalDateTime convertToLocalDateTimeViaInstant(Date dateToConvert) {
    return dateToConvert.toInstant()
      .atZone(ZoneId.systemDefault())
      .toLocalDateTime();
}

3.2. 使用ofEpochMilli()和toLocalDateTime()

等价实现:

public LocalDateTime convertToLocalDateTimeViaMilisecond(Date dateToConvert) {
    return Instant.ofEpochMilli(dateToConvert.getTime())
      .atZone(ZoneId.systemDefault())
      .toLocalDateTime();
}

3.3. 使用ofInstant()(Java 9+)

推荐写法:

public LocalDateTime convertToLocalDateTime(Date dateToConvert) {
    return LocalDateTime.ofInstant(
      dateToConvert.toInstant(), ZoneId.systemDefault());
}

4. 将java.time类型转换为java.util.Date

4.1. LocalDate转Date

两种常用方式:

方式一:使用java.sql.Date(最简单)

public Date convertToDateViaSqlDate(LocalDate dateToConvert) {
    return java.sql.Date.valueOf(dateToConvert);
}

方式二:通过Instant转换

public Date convertToDateViaInstant(LocalDate dateToConvert) {
    return java.util.Date.from(dateToConvert.atStartOfDay()
      .atZone(ZoneId.systemDefault())
      .toInstant());
}

4.2. LocalDateTime转Date

推荐使用 java.sql.Timestamp

public Date convertToDateViaSqlTimestamp(LocalDateTime dateToConvert) {
    return java.sql.Timestamp.valueOf(dateToConvert);
}

替代方案(通过Instant):

Date convertToDateViaInstant(LocalDateTime dateToConvert) {
    return java.util.Date
      .from(dateToConvert.atZone(ZoneId.systemDefault())
      .toInstant());
}

5. 将java.sql.Date转换为java.time.LocalDate

5.1. 直接使用toLocalDate()

Java 8+ 的 java.sql.Date 已内置转换方法:

public LocalDate convertToLocalDateViaSqlDate(Date dateToConvert) {
    return new java.sql.Date(dateToConvert.getTime()).toLocalDate();
}

5.2. 转换为LocalDateTime

使用 java.sql.Timestamp

public static LocalDateTime convertToLocalDateTimeViaSqlTimestamp(Date dateToConvert) {
    return new java.sql.Timestamp(
      dateToConvert.getTime()).toLocalDateTime();
}

6. 总结

本文系统梳理了 Java 日期转换的常见场景:

  • DateLocalDate/LocalDateTime:优先使用 ofInstant()(Java 9+)
  • LocalDate/LocalDateTimeDate:推荐 java.sql.Date/TimestampvalueOf()
  • java.sql.DateLocalDate:直接调用 toLocalDate()

核心要点:

  1. 所有转换都需考虑时区(ZoneId
  2. 处理历史日期(1582年前)需特殊设置
  3. Java 9+ 的 ofInstant() 是最佳实践
  4. JDBC 类型转换优先使用 java.sql.Date/Timestamp

原始标题:Convert Date to LocalDate or LocalDateTime and Back | Baeldung