Kotlin 日期解析无法正常工作

Kotlin date parsing is not working properly

所以我一直在寻找如何正确解析传入的日期时间,问题是这个字符串也包含区域,显然由于某种原因无法解析。 举一个传入日期时间字符串的例子: 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 和 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()

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

正如我从您提供的文档中看到的那样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)

如果您示例中的最后 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 won't work this easy (except from UTC), but the time zones supported by the calendar API(大部分)足以创建 java.time.ZoneId

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

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

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

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

现代日期时间 API 基于 ISO 8601 并且不需要明确使用 DateTimeFormatter 对象,只要日期时间字符串符合 ISO 8601 标准.您的日期时间字符串已经是 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 represents an instantaneous point on the timeline, normally represented in UTC time. The Z in the output is the timezone designator。它代表祖鲁语并指定 Etc/UTC 时区(时区偏移量为 +00:00 小时)。

详细了解 modern Date-Time API* from Trail: Date Time


* 如果您正在为 Android 项目工作,并且您的 Android API 级别仍然不符合 Java-8,请检查Java 8+ APIs available through desugaring. Note that Android 8.0 Oreo already provides support for java.time

作为补充:您的文档提到 Pacific Standard Time 作为时区字符串,可能作为 MS Graph dateTimeTimeZone 的一部分出现。我不认为其他答案可以解决这个问题,所以我想在 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。我的解析方法首先尝试这个格式化程序。 UTCPacific/Honolulu 失败。当 DateTimeParseException 被捕获时,该方法接下来会尝试其他答案中也使用的 ZoneId.of 方法。它处理文档中提到的 UTC 和所有时区 ID sin region/city 格式。

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