【问题标题】:Kotlin date parsing is not working properlyKotlin 日期解析无法正常工作
【发布时间】:2021-11-25 15:33:45
【问题描述】:

所以我一直在寻找如何正确解析传入的日期时间,问题是这个字符串也包含显然由于某种原因无法解析的区域。 举一个传入日期时间字符串的例子: 2021-10-05T10:00:00.0000000

现在我尝试了以下操作:

var dateTimeString = "2021-10-05T10:00:00.0000000"
var formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd'T'HH:mm:ss.SSSZ")
var date = LocalDate.parse(dateTimeString, formatter)

我尝试用空和 ZZZZ 替换 Z,但这不起作用我认为它不起作用,因为加号或减号不存在。仅供参考,由于 Microsoft Graph API 在检索日历事件时,我会收到这种格式的日期。

知道如何正确格式化此日期吗?

编辑:这来自 Microsoft Graph。基本上他们把日期当作一个对象:

"start": {
    "dateTime": "2021-10-05T10:00:00.0000000",
    "timeZone": "UTC"
}

这是解释此日期对象的文档页面:dateTimeTimeZone resource type

更新:

我终于能够解决这个日期问题,我所做的是:

var inputDateTime = "2021-10-05T10:00:00.0000000"
var inputTimeZone = "UTC"
var zonedDateTime = ZonedDateTime.parse(
    inputDateTime,
    DateTimeFormatter.ISO_DATE_TIME.withZone(ZoneId.of(inputTimeZone))
).withZoneSameInstant(ZoneId.systemDefault()).toLocalDateTime()

这样,日期将正确转换为正确的时区和正确的日期/时间。

【问题讨论】:

  • 您到底想用Z 解析什么?我在"2021-10-05T10:00:00.0000000" 中看不到任何区域偏移量。
  • 时区是最后 4 位
  • 没有加号也没有减号,这将如何解析为有效时区?好的,0000 将是 UTC,但其他区域呢?
  • 我知道,但这来自 Microsoft Graph,这不是我编造的。基本上他们把日期当作一个对象:"start": {"dateTime": "2021-10-05T10:00:00.0000000", "timeZone": "UTC"}
  • 这是解释此日期对象的文档页面:docs.microsoft.com/en-us/graph/api/resources/…

标签: java kotlin java-time localdatetime datetimeformatter


【解决方案1】:

从您提供的文档中可以看出https://docs.microsoft.com/en-us/graph/api/resources/datetimetimezone?view=graph-rest-1.0

dateTime    String  A single point of time in a combined date and time representation ({date}T{time}; for example, 2017-08-29T04:00:00.0000000).
timeZone    String  Represents a time zone, for example, "Pacific Standard Time". See below for more possible values.

dateTime 对象没有区域编码。并且所有 7 个零都代表几分之一秒。在这种情况下,它是常规的ISO_DATE_TIME,您不需要创建自己的格式化程序。

下面的代码应该可以工作

var dateTimeString = "2021-10-05T10:00:00.0000000"
var date = LocalDate.parse(dateTimeString, DateTimeFormatter.ISO_DATE_TIME)

【讨论】:

  • 谢谢你,第一次遇到这样的格式,通常日期只有3位数字表示秒的小数部分,所以我假设其他4位必须是时区偏移量跨度>
【解决方案2】:

如果您的示例中的最后 4 位数字 String 不代表时区:

在没有格式化程序的情况下解析它(没有必要,因为如果最后 4 位数字只是秒的附加分数,这将是完美的 ISO 标准),但也考虑时区您使用 JSON:

import java.time.LocalDateTime
import java.time.ZoneId
import java.time.ZonedDateTime
import java.time.format.DateTimeFormatter

fun main() {
    // the time String from JSON
    var dateTimeString = "2021-10-05T10:00:00.0000000"
    // the zone from JSON (may not always work, but does with UTC)
    var timeZone = "UTC"
    // create the zone from the timezone String
    var zoneId = ZoneId.of(timeZone)
    // then parse the time String using plain LocalDateTime and add the zone afterwards
    var zdt: ZonedDateTime = LocalDateTime.parse(dateTimeString).atZone(zoneId)
    // print some results
    println("Full ZonedDateTime: ${zdt.format(DateTimeFormatter.ISO_ZONED_DATE_TIME)}")
    println("(Local)Date only:   ${zdt.toLocalDate()}")
}
Full ZonedDateTime: 2021-10-05T10:00:00Z[UTC]
(Local)Date only:   2021-10-05

请注意,解析time zones currently supported by Windows 不会那么容易(UTC 除外),但time zones supported by the calendar API(大部分)足以创建java.time.ZoneId

【讨论】:

    【解决方案3】:

    您的日期时间字符串没有时区信息

    您的日期时间字符串 2021-10-05T10:00:00.0000000 没有时区信息。 .0000000 代表秒的分数,到目前为止,java.time 能够处理多达 9 位的精度(即纳秒精度)。

    由于它没有时区信息,它代表本地日期时间,因此应解析为LocalDateTime

    您的日期时间字符串不需要DateTimeFormatter

    现代日期时间 API 基于 ISO 8601,只要日期时间字符串符合 ISO 8601 标准,就不需要明确使用 DateTimeFormatter 对象。您的日期时间字符串已采用 ISO 8601 格式。

    演示:

    import java.time.LocalDateTime;
    
    public class Main {
        public static void main(String args[]) {
            var dateTimeString = "2021-10-05T10:00:00.0000000";
            var ldt = LocalDateTime.parse(dateTimeString);
            System.out.println(ldt);
        }
    }
    

    输出:

    2021-10-05T10:00
    

    ONLINE DEMO

    如何从LocalDateTime 实例中取出ZonedDateTime

    您可以使用LocalDateTime#atZone 将附加ZoneId 转换为LocalDateTime,从而生成ZonedDateTime

    ZonedDateTime zdt = ldt.atZone(ZoneId.of("Etc/UTC"));
    

    注意:如果您需要Instant,您可以从ZonedDateTime 的这一瞬间获得它,例如

    Instant instant = zdt.toInstant();
    

    Instant 表示时间线上的一个瞬时点,通常以UTC 时间表示。输出中的Z 是零时区偏移的timezone designator。它代表 Zulu 并指定 Etc/UTC 时区(时区偏移量为 +00:00 小时)。

    通过 Trail: Date Time 了解有关 modern Date-Time API* 的更多信息。


    * 如果您正在为一个 Android 项目工作,并且您的 Android API 级别仍然不符合 Java-8,请检查 Java 8+ APIs available through desugaring。请注意,Android 8.0 Oreo 已经提供了support for java.time

    【讨论】:

      【解决方案4】:

      作为补充:您的文档提到 Pacific Standard Time 作为一个时区字符串,它可能是您的 MS Graph dateTimeZone 的一部分。我不认为其他答案可以处理那个,所以我想展示你如何在 Java 中处理它。

      private static final DateTimeFormatter ZONE_FORMATTER
              = DateTimeFormatter.ofPattern("zzzz", Locale.ENGLISH);
      
      static ZoneId parseZone(String timeZoneString) {
          try {
              return ZoneId.from(ZONE_FORMATTER.parse(timeZoneString));
          } catch (DateTimeParseException dtpe) {
              return ZoneId.of(timeZoneString);
          }
      }
      

      我相信这可以处理文档中提到的字符串以及您的问题中提到的UTC。我只展示了三个不同的:

          String[] msTimeZoneStrings = { "UTC", "Pacific Standard Time", "Pacific/Honolulu" };
          for (String zoneString : msTimeZoneStrings) {
              ZoneId zid = parseZone(zoneString);
              System.out.format("%-21s parsed to %s%n", zoneString, zid);
          }
      

      输出:

      UTC                   parsed to UTC
      Pacific Standard Time parsed to America/Los_Angeles
      Pacific/Honolulu      parsed to Pacific/Honolulu
      

      在我使用的格式化程序中,zzzz 用于时区名称,例如Pacific Standard Time。我的 parse 方法首先尝试这个格式化程序。 UTCPacific/Honolulu 失败。当DateTimeParseException 被捕获时,该方法接下来会尝试其他答案中也使用的ZoneId.of 方法。它处理 UTC 和文档中提到的所有时区 ID sin region/city 格式。

      将从我的方法中获得的ZoneId 与解析的LocalDateTime 结合起来,得到ZonedDateTime,deHaar 已经在他们的答案中显示了这种方式。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-03-20
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多