【问题标题】:Convert String to LocalDateTime in Java在 Java 中将字符串转换为 LocalDateTime
【发布时间】:2020-08-06 16:37:55
【问题描述】:

我有这个字符串

2020 年 8 月 7 日星期五 18:00:00 +0000

我需要将其转换为 LocalDateTime

我尝试了几种方法:

创建一个 DateTimeFormatter 并解析字符串

String dateString = "Fri, 07 Aug 2020 18:00:00 +0000";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, dd MMM yyyy HH:mm:ss", Locale.getDefault());
LocalDateTime parsedDate = LocalDateTime.parse(publishedString, formatter);

使用 SimpleDateFormat 将其转换为 Date,然后将 resultDate 转换为 LocalDateTime

String dateString = "Fri, 07 Aug 2020 18:00:00 +0000";
SimpleDateFormat dateFormatter = new SimpleDateFormat("E, dd MMM yyyy HH:mm:ss Z");
Date date = dateFormatter.parse(publishedString);
LocalDateTime localDateTime = date.toInstant().atZone(ZoneId.systemDefault()).toLocalDateTime();

两种解决方案都给了我同样的例外:

java.time.format.DateTimeParseException: Text 'Fri, 07 Aug 2020 18:00:00 +0000' could not be parsed 
at index 0 at java.base/java.time.format.DateTimeFormatter.parseResolved0(DateTimeFormatter.java:2046)

如何转换该字符串?

【问题讨论】:

  • 你的电脑能“理解”英语吗?你对那里的 Locale 有什么要求?
  • DateTimeFormatter.ofPattern("E, dd MMM yyyy HH:mm:ss Z", Locale.ROOT) 应该可以工作。 1. 问题是您的系统语言环境可能不是英语,因此“Fri”未正确解析和 2. 问题是您最后忘记了模式中的偏移部分
  • 我猜,你的语言环境是Locale.ITALY。尝试将其更改为Locale.ENGLISH。另外,图案不完整。尝试使用DateTimeFormatter.ofPattern("E, dd MMM yyyy HH:mm:ss Z", Locale.ENGLISH)。在此之后它应该工作。另见:java.time DateTimeFormatter pattern for timezone offset

标签: java date localdatetime


【解决方案1】:

tl;博士

OffsetDateTime.parse( 
    "Fri, 07 Aug 2020 18:00:00 +0000" , 
    DateTimeFormatter.RFC_1123_DATE_TIME 
)

看到这个code run live at IdeOne.com

LocalDateTime 是错误的类

您的输入字符串包含 +0000,表示与 UTC 的偏移量。

所以你不应该使用LocalDateTime。该类故意缺少任何时区或偏移量的概念。使用 LocalDateTime,您的字符串 Fri, 07 Aug 2020 18:00:00 +0000 将在 2020 年 8 月 7 日变为 6M,但我们不知道那是日本东京下午 6 点,法国图卢兹下午 6 点,还是美国俄亥俄州托莱多下午 6 点——所有不同的时刻相隔几个小时。

OffsetDateTime

相反,这个值应该被解析为OffsetDateTime

解析

您输入的格式是RFC 1123。该特定格式是在 java.time 中预定义的。

String input = "Fri, 07 Aug 2020 18:00:00 +0000";
DateTimeFormatter f = DateTimeFormatter.RFC_1123_DATE_TIME;
OffsetDateTime odt = OffsetDateTime.parse( input , f );

odt.toString(): 2020-08-07T18:00Z

【讨论】:

  • LocalDateTime is the wrong class - 没错!我昨天想写一个类似的答案,但没时间这样做。也许,我不会写得像你写得那么漂亮。不错的答案!
  • 这不是错误的类,我有一个带有 LocalDateTime 字段的对象,我必须将 String 转换的结果放入该字段中
  • @IvanNotarstefano 您是否会在放弃指示哪种货币的情况下将金额存储为BigDecimal?这类似于您在丢弃输入中指示的偏移量时所做的事情。 LocalDateTime 不能代表您输入中的值,并且确实是此处使用的错误类。
  • 真是个比喻,@BasilBourque!我以后肯定会用这个比喻来解释ZonedDateTimeOffsetDateTime的重要性。
【解决方案2】:

我会说使用 Locale.ROOT 并且不要忘记 DateTimeFormatter 类中的 Z

String dateString = "Fri, 07 Aug 2020 18:00:00 +0000";
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("E, dd MMM yyyy HH:mm:ss Z", Locale.ROOT);
LocalDateTime parsedDate = LocalDateTime.parse(dateString, formatter);

【讨论】:

  • LocalDateTime 是此处使用的错误类。您正在丢弃有价值的信息:与 UTC 的偏移量。
  • 虽然它在某些或其他情况下可能会或可能不会给出预期的结果,如@BasilBourque 所示,但无需编写您自己的格式模式字符串。格式内置为DateTimeFormatter.RFC_1123_DATE_TIME
【解决方案3】:

我了解您需要一个 API 的 LocalDateTime,但由于您无法控制的设计问题正在尝试使用 LocalDateTime 一段时间。

如果外部合同规定要理解 LocalDateTime 在哪个时区或哪个 UTC 偏移,则可以使 LocalDateTime 工作,至少在 99.977 % 的情况下。在某些同事程序员不阅读合同的那一天,您仍然会等待发生编程错误,这是我们无法在代码中解决的问题,只能尝试通过良好的注释来缓解。

例如,如果合同显示 UTC,那么我们需要确保将时间转换为 UTC。为此,我们需要字符串的偏移量。

    ZoneOffset contractualOffset = ZoneOffset.UTC;
    String stringWeveGot = "Fri, 07 Aug 2020 18:00:00 +0000";
    LocalDateTime convertedDateTime = OffsetDateTime
            .parse(stringWeveGot, DateTimeFormatter.RFC_1123_DATE_TIME)
            .withOffsetSameInstant(contractualOffset)
            .toLocalDateTime();
    System.out.println(convertedDateTime);

输出:

2020-08-07T18:00

如果要求字符串中的偏移量已经为 0,则需要验证它是否为 0,否则错误会被忽视,用户会得到错误的结果。例如:

    OffsetDateTime parsedOdt = OffsetDateTime
            .parse(stringWeveGot, DateTimeFormatter.RFC_1123_DATE_TIME);
    if (! parsedOdt.getOffset().equals(contractualOffset)) {
        throw new IllegalStateException("Offset must be " + contractualOffset
                + ", was " + parsedOdt.getOffset());
    }
    LocalDateTime convertedDateTime = parsedOdt.toLocalDateTime();

如果合同提及某个时区,则转换为该时区。我以澳大利亚/维多利亚为例。

    ZoneId contractualZone = ZoneId.of("Australia/Victoria");
    String stringWeveGot = "Fri, 07 Aug 2020 18:00:00 +0000";
    LocalDateTime convertedDateTime = OffsetDateTime
            .parse(stringWeveGot, DateTimeFormatter.RFC_1123_DATE_TIME)
            .atZoneSameInstant(contractualZone)
            .toLocalDateTime();
    System.out.println(convertedDateTime);

2020-08-08T04:00

您将在时钟倒转的时间异常中得到模棱两可的结果,例如在夏令时 (DST) 结束时倒退

你的代码出了什么问题?

这里解释了您的异常原因:

【讨论】:

  • 在 Basil Bourque 的好回答之上,您添加的任何内容都是金色的。
  • 谢谢你,@ArvindKumarAvinash,太好了!我当然同意,我的回答只不过是对 Basil Bourque 提供的基本和重要信息的补充。
猜你喜欢
  • 1970-01-01
  • 2019-11-27
  • 2019-08-07
  • 1970-01-01
  • 2021-01-20
  • 2017-01-30
  • 2017-08-03
  • 2021-05-28
相关资源
最近更新 更多