是否可以指定带时区(即 GMT 偏移量)但没有时间的 ISO 日期?

Is it possible to specify an ISO Date with a timezone (i.e. GMT offset) but without a time?

我想看看您是否可以在 ISO 中指定带时区的日期,但又不指定时间。

在没有实际时间的情况下询问时区可能看起来很奇怪,但从技术上讲,日期代表两个时间之间的范围......从午夜到午夜的 24 小时时间段,'midnight' 必须在时区。

在我们的例子中,我们有一个 API 想要说 'Filter things on-or-before date X and on-or-after date Y' 并且我们希望用户指定 'April 9th' (在他们的时区)以便两者都得到所有东西那天发生的事情。

当然,我们通过在第一个日期上添加一天,然后将其更改为纯 'before' 来解决此问题,但需要前端进行计算。我们不能在后端执行此操作,因为必须发送带时间的日期意味着我们将在 4 月 9 日午夜发送,然后在后端添加一天,但如果有人在下午 4 点通过怎么办?

如果日期不是午夜时间,我们可能会失败,但我们会回到为什么首先要通过它。

再说一次,你可以有一个带时区但没有时间分量的日期吗?

如果 时区 是指 UTC 偏移量(与 ISO 8601 日期和时间一起使用),这没有问题。如果按时区你指的是一个真实的时区与 UTC 的历史、现在和已知的未来偏移量,包括例如夏季 time/DST,如 America/New_York 或北美东部时间,那么 ISO 8601 不支持,无论是有时间的日期还是没有时间的日期。

2020-04-25-04:00

这是今年 4 月 25 日偏移量 -04:00 处完全有效的 ISO 8601。所以你可以用它来表示从 2020-04-25T00:00-04:00(含)到 2020-04-26T00:00-04:00(不含)的时间间隔。这相当于 2020-04-25T04:00Z 到 2020-04-26T04:00Z(Z 表示 UTC)。

Java 示例代码

我不知道任何 Swift,所以无法告诉您如何格式化或解析 Swift 中的此类字符串。在 Java 格式中它还不错。示例:

    LocalDate date = LocalDate.of(2020, Month.APRIL, 25);
    String isoOffsetDateString = date
            .atStartOfDay(ZoneId.of("America/New_York"))
            .format(DateTimeFormatter.ISO_OFFSET_DATE);
    System.out.println(isoOffsetDateString);

输出:

2020-04-25-04:00

我正在使用 Java 的内置 ISO_OFFSET_DATE 格式化程序。文档告诉我们这个格式是:

The ISO date formatter that formats or parses a date with an offset, such as '2011-12-03+01:00'.

解析字符串并生成一天的开始和结束需要更多时间:

    TemporalAccessor parsed
            = DateTimeFormatter.ISO_OFFSET_DATE.parse(isoOffsetDateString);
    Instant start = LocalDate.from(parsed)
            .atStartOfDay(ZoneOffset.from(parsed))
            .toInstant();
    Instant end = start.plus(1, ChronoUnit.DAYS);

    System.out.println("From " + start + " inclusive to " + end + " exclusive");

From 2020-04-25T04:00:00Z inclusive to 2020-04-26T04:00:00Z exclusive

我选择转换为 Instant,class 暂时独立于偏移量或时区。 Instants 以 UTC 打印,正如每个人的尾随 Z 所说。在您的 Java 代码中,您可能不希望进行此转换或进行其他转换,具体取决于具体情况。

Link

解码 ISO8601日期只有年月日和时区,设置ISO8601DateFormatter

的适当formatOptions
let isoFormatter = ISO8601DateFormatter()
isoFormatter.formatOptions = [.withFullDate, .withTimeZone]