将JavaScript日期转换为Java日期

4

我在 Angular 6 应用程序中有一个日期值:

expires_at: Date;

在Java的getter方法中,我使用字符串类型来获取值:
private String expires_at;

但是当我使用以下代码进行转换时:

从字符串到LocalDateTime

terminals.setExpires_at(LocalDateTime.parse(terminalDTO.getExpires_at()));

从LocalDateTime到String:

terminalNewDTO.setExpires_at(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(terminals.getExpires_at()));

我收到了这个错误:
create] due to exception [Text '2019-02-19T01:00:00.000Z' could not be parsed, unparsed text found at index 23]
20:55:43,494 INFO  [stdout] (default task-1) java.time.format.DateTimeParseException: Text '2019-02-19T01:00:00.000Z' could not be parsed, unparsed text found at index 23
20:55:43,494 INFO  [stdout] (default task-1)    at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2049)
20:55:43,494 INFO  [stdout] (default task-1)    at java.base/java.time.format.DateTimeFormatter.parse(DateTimeFormatter.java:1948)
20:55:43,495 INFO  [stdout] (default task-1)    at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:492)
20:55:43,495 INFO  [stdout] (default task-1)    at java.base/java.time.LocalDateTime.parse(LocalDateTime.java:477)
20:55:43,496 INFO  [stdout] (default task-1)    at deployment.datalis_admin.war//org.datalis.admin.backend.mapper.TerminalsMapperImpl.map(TerminalsMapperImpl.java:98)
20:55:43,496 INFO  [stdout] (default task-1)    at deployment.datalis_admin.war//org.datalis.admin.backend.restapi.TerminalController.create(TerminalController.java:104)
20:55:43,496 INFO  [stdout] (default task-1)    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
20:55:43,497 INFO  [stdout] (default task-1)    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
20:55:43,497 INFO  [stdout] (default task-1)    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
20:55:43,497 INFO  [stdout] (default task-1)    at java.base/java.lang.reflect.Method.invoke(Method.java:566)
20:55:43,497 INFO  [stdout] (default task-1)    at deployment.datalis_admin.war//org.springframework.web.method.support.InvocableHandlerMethod.doInvoke(InvocableHandlerMethod.java:189)

你知道我怎么解决这个问题吗?

这是日期值:2/20/19, 3:00 AM,我试图发送它。


你在使用“Z”表示“Zulu时间”/UTC吗? - Roddy of the Frozen Peas
我不知道。这是来自日期选择器的。 - Peter Penzov
你可以使用 OffsetDateTime date = OffsetDateTime.parse("2019-02-19T01:00:00.000Z", DateTimeFormatter.ISO_OFFSET_DATE_TIME); - Daniele
我不相信你在最近的编辑中添加了第二行代码后会获得相同的堆栈跟踪。请为每个堆栈跟踪引用精确文本(或发布一个新问题来处理新问题)。 - Ole V.V.
如果您将日期作为java.util.Date类型而不是字符串,则它可以正常工作。 - Harish Barma
显示剩余2条评论
2个回答

13

UTC

Z表示偏移量为零,也就是UTC本身。Z的发音是“Zulu”。

使用Instant类进行解析,该类表示UTC中的一个时刻。

Instant instant = Instant.parse( "2019-02-19T01:00:00.000Z" ) ;

那个输入字符串符合标准的ISO 8601格式。在解析和生成字符串时,java.time类默认使用ISO 8601格式。

instant.toString() // generates a ISO 8601 string.

UTC+2时区

你在问题中提到:

这是日期值:2/20/19,上午3:00

如果原始值为上午3点,则显然处于比协调世界时早两个小时的时区。这可能是几个不同的时区之一,你可以在维基百科的列表中看到(该列表可能不是最新的,请注意)。

我将任意选择 Africa/Tripoli

ZoneId z = ZoneId.of( "Africa/Tripoli" ) ;
ZonedDateTime zdt = instant.atZone( z ) ;  // Adjust from UTC to a time zone. Same moment, same point on the timeline, different wall-clock time.
instantzdt都代表同一时刻,时间线上的同一点。这一瞬间通过两个不同地区人民使用的挂钟时间来观察。

想象一下,利比亚的某个人打电话给冰岛的某个人。在聊天时,他们同时看着各自墙上的时钟。冰岛时钟使用与UTC偏移量为零,而利比亚时钟比UTC提前两个小时。因此,利比亚的凌晨3点在冰岛是凌晨1点。

在IdeOne.com上查看代码运行实例

LocalDateTime类不适用于表示时刻

LocalDateTime到字符串:

跟踪时刻时永远不要使用LocalDateTimeLocalDateTime仅仅是日期和时间,并且没有其他信息。没有时区或UTC偏移量的情况下,LocalDateTime无法表示时刻。它代表了大约26-27小时范围内的潜在时刻,即全球时区的当前范围。

一个LocalDateTime就像是说“今年1月23日中午”。你是指日本东京的中午还是印度加尔各答的中午?或者是法国巴黎的中午?魁北克蒙特利尔的中午?这些不同地方的中午发生的时间不同,之间有几个小时的时间差。 LocalDateTime中的“Local”表示任何地区,或每个地区,但不表示任何特定地区。

关于java.time

java.time框架内置于Java 8及更高版本中。这些类替代了老旧的legacy日期时间类,例如java.util.DateCalendarSimpleDateFormat

想要了解更多信息,请参阅Oracle教程。在Stack Overflow上搜索可获得许多示例和解释。规范为JSR 310

Joda-Time项目现处于maintenance mode维护模式,建议迁移到java.time类。

您可以直接使用符合JDBC 4.2或更高版本的JDBC驱动程序,无需使用字符串或java.sql.*类,即可直接与数据库交换java.time对象。

如何获取java.time类?

ThreeTen-Extra 项目通过增加额外的类来扩展 java.time。该项目是 java.time 可能未来添加内容的试验场。您可能会在这里找到一些有用的类,例如 IntervalYearWeekYearQuartermore

1
@PeterPenzov 我添加了一个代码示例。此外,这个问题在 Stack Overflow 上已经被解决了很多次。在发帖之前先搜索一下,并搜索更详细的讨论和更多的示例。 - Basil Bourque
请问您能否在我的代码中演示如何使用您的解决方案? - Peter Penzov
我想快速解决的方法是 terminals.setExpires_at(LocalDateTime.parse(terminalDTO.getExpires_at(), DateTimeFormatter.ISO_OFFSET_DATE_TIME));。但请考虑在您的应用程序中是否可以使用 InstantOffsetDateTime 替代 LocalDateTime - Ole V.V.
原来我还有一个案例。代码已经更新。 - Peter Penzov
1
我认为这里还有更多需要考虑的地方。最好在Angular TS中使用DatePipe,并将日期转换为应用程序始终期望的标准格式。仅对ISO_INSTANT格式(您当前的情况)使用Instant.parse()。如果UI中的格式发生任何变化,代码将在后端中断。处理此问题的最佳方法是始终使用UTC时间戳来处理服务数据。 - Kris
@PeterPenzov 我在你的问题中没有看到其他的“case”。我的回答展示了如何解析一个ISO 8601字符串并将其调整为某个时区。你还需要什么? - Basil Bourque

1

你也可以使用:

String expires_at = "2/20/19, 3:00 AM";
SimpleDateFormat parser = new SimpleDateFormat("MM/dd/yyyy, h:mm a"); 
Date date = parser.parse(expires_at); 

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接