【问题标题】:Converting between java.time.LocalDateTime and java.util.Datejava.time.LocalDateTime 和 java.util.Date 之间的转换
【发布时间】:2013-10-17 15:40:54
【问题描述】:

Java 8 有一个全新的日期和时间 API。此 API 中最有用的类之一是 LocalDateTime,用于保存与时区无关的日期与时间值。

可能有数百万行代码使用遗留类java.util.Date 用于此目的。因此,在连接新旧代码时,需要在两者之间进行转换。由于似乎没有直接的方法可以做到这一点,如何做到这一点?

【问题讨论】:

标签: java datetime java-8 java-time


【解决方案1】:

简答:

Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());
Date out = Date.from(ldt.atZone(ZoneId.systemDefault()).toInstant());

说明: (基于this questionLocalDate

尽管有它的名字,java.util.Date 代表时间线上的一个瞬间,而不是“日期”。对象中存储的实际数据是自 1970-01-01T00:00Z(格林威治标准时间/UTC 1970 开始时的午夜)以来的long 毫秒计数。

JSR-310 中java.util.Date 的等价类是Instant,因此有方便的方法来提供来回转换:

Date input = new Date();
Instant instant = input.toInstant();
Date output = Date.from(instant);

java.util.Date 实例没有时区概念。如果您在java.util.Date 上调用toString(),这可能看起来很奇怪,因为toString 是相对于时区的。然而,该方法实际上使用 Java 的默认时区来提供字符串。时区不是java.util.Date 实际状态的一部分。

Instant 也不包含有关时区的任何信息。因此,要将Instant 转换为本地日期时间,必须指定时区。这可能是默认时区 - ZoneId.systemDefault() - 或者它可能是您的应用程序控制的时区,例如用户首选项中的时区。 LocalDateTime 有一个方便的工厂方法,可以同时获取即时和时区:

Date in = new Date();
LocalDateTime ldt = LocalDateTime.ofInstant(in.toInstant(), ZoneId.systemDefault());

反过来,LocalDateTime 时区是通过调用atZone(ZoneId) 方法来指定的。然后可以将ZonedDateTime 直接转换为Instant

LocalDateTime ldt = ...
ZonedDateTime zdt = ldt.atZone(ZoneId.systemDefault());
Date output = Date.from(zdt.toInstant());

请注意,从LocalDateTimeZonedDateTime 的转换可能会引入意外行为。这是因为由于夏令时,并非每个本地日期时间都存在。在秋季/秋季,本地时间线存在重叠,相同的本地日期时间出现两次。在春天,有一个间隙,一个小时消失了。请参阅atZone(ZoneId) 的 Javadoc,了解有关转换将做什么的更多定义。

总结,如果您将java.util.Date 往返于LocalDateTime 并返回到java.util.Date,由于夏令时,您最终可能会得到不同的瞬间。

附加信息:还有一个差异会影响非常旧的日期。 java.util.Date 使用在 1582 年 10 月 15 日更改的日历,在此之前的日期使用儒略历而不是公历。相比之下,java.time.* 一直使用 ISO 日历系统(相当于公历)。在大多数用例中,您需要 ISO 日历系统,但在比较 1582 年之前的日期时,您可能会看到奇怪的效果。

【讨论】:

  • 非常感谢您的清晰解释。特别是为什么java.util.Date 不包含时区,而是在toString() 期间打印。事件official documentation 在您发布时并没有清楚地说明这一点。
  • 警告: LocalDateTime.ofInstant(date.toInstant()... 的行为并不像人们天真期望的那样。例如new Date(1111-1900,11-1,11,0,0,0); 将使用这种方法变为1111-11-17 23:53:28。如果您需要上一个示例中的结果为1111-11-11 00:00:00,请查看java.sql.Timestamp#toLocalDateTime() 的实现。
  • 我添加了一个关于非常古老的日期(1582 年之前)的部分。 FWIW,您建议的修复很可能是错误的,因为 java.util.Date 中的 1111-11-11 与 java.time 中的 1111-11-18 历史上的实际日期相同,因为日历系统不同(6.5 分钟的差异发生在 1900 年之前的许多时区)
  • 另外值得注意的是java.sql.Date#toInstant 会抛出一个UnsupportedOperationException。所以不要在java.sql.ResultSet#getDate 上的RowMapper 中使用toInstant
【解决方案2】:

一切都在这里:http://blog.progs.be/542/date-to-java-time

“往返”的答案并不准确:当你这样做时

LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

如果您的系统时区不是 UTC/GMT,请更改时间!

【讨论】:

  • 仅当您在每年 1 小时内执行此操作时,在秋季,当 LocalDateTime 的两次重叠时。弹簧向前移动不会引起问题。大多数时候都会正确转换两个方向。
【解决方案3】:

这是我想出的(和所有日期时间难题一样,它可能会因一些奇怪的时区-闰年-日光调整而被推翻:D)

往返:Date > LocalDateTime

给定:Date date = [some date]

(1)LocalDateTimeInstantDate

    Instant instant = Instant.ofEpochMilli(date.getTime());
    LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);

(2)DateInstantLocalDateTime

    Instant instant = ldt.toInstant(ZoneOffset.UTC);
    Date date = Date.from(instant);

例子:

给定:

Date date = new Date();
System.out.println(date + " long: " + date.getTime());

(1)LocalDateTimeInstantDate:

Date创建Instant

Instant instant = Instant.ofEpochMilli(date.getTime());
System.out.println("Instant from Date:\n" + instant);

Instant 创建Date(不是必需的,只是为了说明):

date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());

Instant创建LocalDateTime

LocalDateTime ldt = LocalDateTime.ofInstant(instant, ZoneOffset.UTC);
System.out.println("LocalDateTime from Instant:\n" + ldt);

(2) Date Instant LocalDateTime

LocalDateTime 创建Instant

instant = ldt.toInstant(ZoneOffset.UTC);
System.out.println("Instant from LocalDateTime:\n" + instant);

Instant创建Date

date = Date.from(instant);
System.out.println("Date from Instant:\n" + date + " long: " + date.getTime());

输出为:

Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

Instant from Date:
2013-11-01T14:13:04.574Z

Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

LocalDateTime from Instant:
2013-11-01T14:13:04.574

Instant from LocalDateTime:
2013-11-01T14:13:04.574Z

Date from Instant:
Fri Nov 01 07:13:04 PDT 2013 long: 1383315184574

【讨论】:

  • @scottb 您是在向我还是向提出问题的人提出这个问题?至于我的想法,如果在 jdk 8 API 中明确说明转换,那将是很好——他们至少可以做到这一点。无论如何,有很多库将被重构以包含新的 Java-8 功能,并且知道如何去做很重要。
  • @scottb 为什么第三方案件不常见?太他妈常见了。一个例子:JDBC 4 或更少(希望不是 5)。
  • 作为一般的 JSR-310 规则,不需要使用 epoch-millis 进行类型之间的转换。使用对象存在更好的选择,请参阅下面的完整答案。上面的答案也只有在使用像 UTC 这样的区域偏移时才完全有效 - 答案的某些部分不适用于像 America/New_York 这样的完整时区。
  • 代替Instant.ofEpochMilli(date.getTime())date.toInstant()
  • @goat toInstant() 看起来不错,除了 java.sql.Date 失败,arggggh!所以终于用Instant.ofEpochMilli(date.getTime())更方便了。
【解决方案4】:

如果您确定需要默认时区,则更方便:

Date d = java.sql.Timestamp.valueOf( myLocalDateTime );

【讨论】:

  • 当然,这更容易,但我不喜欢将 jdbc 相关的东西与简单的日期处理混合在一起,至少恕我直言。
  • 对不起,这是一个简单问题的糟糕解决方案。这就像定义 5 等于奥林匹克标志中的环数一样。
  • @Madbreaks 你能提供一个使用 Java 版本 7 的解决方案吗?
【解决方案5】:

我不确定这是否是最简单或最好的方法,或者是否有任何陷阱,但它有效:

static public LocalDateTime toLdt(Date date) {
    GregorianCalendar cal = new GregorianCalendar();
    cal.setTime(date);
    ZonedDateTime zdt = cal.toZonedDateTime();
    return zdt.toLocalDateTime();
}

static public Date fromLdt(LocalDateTime ldt) {
    ZonedDateTime zdt = ZonedDateTime.of(ldt, ZoneId.systemDefault());
    GregorianCalendar cal = GregorianCalendar.from(zdt);
    return cal.getTime();
}

【讨论】:

  • LocalDateTimeDate 肯定有一个陷阱。在夏令时转换时,LocalDateTime 可能不存在,或出现两次。你需要弄清楚在每种情况下你想发生什么。
  • 顺便说一句,GregorianCalendar 属于旧的尴尬 API,新的 java.time API 旨在取代
【解决方案6】:

LocalDateTime -> Date 最快的方法是:

Date.from(ldt.toInstant(ZoneOffset.UTC))

【讨论】:

    【解决方案7】:

    如果您在 Android 上使用 threetenbp,则可以改用 DateTimeUtils

    例如:

    Date date = DateTimeUtils.toDate(localDateTime.atZone(ZoneId.systemDefault()).toInstant());
    

    你不能使用Date.from,因为它只支持 api 26+

    【讨论】:

      【解决方案8】:

      从新 API LocalDateTime 转换为 java.util.date 时,以下似乎有效:

      Date.from(ZonedDateTime.of({time as LocalDateTime}, ZoneId.systemDefault()).toInstant());
      

      可以(希望)以类似的方式实现反向转换...

      希望对你有帮助……

      【讨论】:

        【解决方案9】:

        我认为以下方法将在不考虑时区的情况下解决转换问题。 如有不足请评论。

            LocalDateTime datetime //input
            public static final DateTimeFormatter yyyyMMddHHmmss_DATE_FORMAT = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
            String formatDateTime = datetime.format(yyyyMMddHHmmss_DATE_FORMAT);
            Date outputDate = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").parse(formatDateTime); //output
        

        【讨论】:

          猜你喜欢
          • 1970-01-01
          • 2021-10-16
          • 1970-01-01
          • 2011-04-10
          • 2021-05-30
          • 1970-01-01
          • 2017-07-13
          • 2016-07-13
          • 2016-01-09
          相关资源
          最近更新 更多