ChatGPT解决这个技术问题 Extra ChatGPT

java.time.LocalDateTime 和 java.util.Date 之间的转换

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

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


M
Mike Fikes

简短的回答:

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

解释:(基于 this question 关于 LocalDate

尽管它的名字,java.util.Date 表示时间线上的一个瞬间,而不是“日期”。存储在对象中的实际数据是自 1970-01-01T00:00Z(1970 GMT/UTC 开始时的午夜)以来的 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.DateLocalDateTime 并返回到 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
1
19 revs, 2 users 99%

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

往返:日期 <<->> LocalDateTime

给定:Date date = [some date]

(1) LocalDateTime << Instant<< Date

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

(2) Date << Instant << LocalDateTime

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

例子:

鉴于:

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

(1) LocalDateTime << Instant<< 日期:

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) 日期 << 即时 << 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()) 更容易了。
b
bernard paulus

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

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

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

LocalDateTime 最快的方法 -> Date 是:

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


j
joe-mojo

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

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

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

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


仅当您在秋季的 1 小时内执行此操作时,LocalDateTime 的两次时间重叠。弹簧向前移动不会引起问题。大多数时候会正确转换两个方向。
d
dizzy

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

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

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

希望能帮助到你...


h
humazed

如果您使用的是 android 并使用 threetenbp,则可以改用 DateTimeUtils

前任:

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

您不能使用 Date.from,因为它仅在 api 26+ 上受支持


K
Knut Arne Vedaa

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

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 旨在取代
A
Akhil S Kamath

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

    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