如何在ISO 8601日期中添加时区但不带时间组件?

11

带有时区的ISO 8601 datetime格式如下:

2018-09-07T05:28:42Z

然而,我需要在我的系统中表示一些日期,其精度为天而非秒,这意味着它将是一个ISO 8601日历日期。日历日期的格式如下:
2018-09-07

在维基百科关于标准的文章中(我没有获得标准本身的访问权限,因为您必须付费才能使用),在讨论日期时没有提到时区。它确实谈到了省略时间的部分:
“基本或扩展时间格式可以省略秒或分钟和秒以缩短长度但减少精度:[hh]:[mm]、[hh][mm] 和 [hh] 是相应的缩短后的时间格式。”
从这个角度来看,似乎不能省略小时,所以我无法写出像这样的日历日期:
2018-09-07TZ

看起来我能做的最好的就是将其截断到小时:

2018-09-07T00Z

我不想这样做,如果可以避免的话,因为我在日期上添加了比实际更精确的信息。该日期的意思是“2018年9月7日UTC时区的任意时间”,而不是“2018年9月7日UTC时区的午夜时刻”。
有什么建议吗?

日期没有时区(因为没有需要映射到时区的时间)。 - user330315
1
尽管使用仍有争议,但类似于“2011-12-03+01:00”或“2018-09-07Z”(没有“T”)的格式已经出现,在这里可以看到示例(https://docs.oracle.com/javase/10/docs/api/java/time/format/DateTimeFormatter.html#ISO_OFFSET_DATE)。你需要它做什么?如果你有一个好的用例并且相关方同意,我认为我会使用它,不必太担心它是否符合ISO 8601。 - Ole V.V.
1
我提供的链接是Java内置的ISO_OFFSET_DATE格式化程序。引用:“ISO日期格式化程序,用于格式化或解析带有偏移量的日期,例如'2011-12-03 + 01:00'。这将返回一个不可变的格式化程序,能够格式化和解析ISO-8601扩展偏移日期格式。”因此,他们似乎认为它与ISO 8601一致。无论他们是否误读了来源,还是你误读了,我都不敢说。 - Ole V.V.
这里有另一个例子:https://stackoverflow.com/questions/48418975/how-to-convert-string-to-date-with-timezone - Ole V.V.
1
你能解释一下带有时区的日期对你有什么用处吗? - PowerStat
4
在某些情况下,带有时区的日期是必要的,因为时区可以增加日期本身无法提供的精度。如果我们知道事件发生的地点,但只知道当天是哪一天,这将非常有用。例如,在日本的时区中,“2020年1月1日的某个时间”包含的时间子集与加利福尼亚州的“2020年1月1日的某个时间”不同,因为这些天的开始和结束时间不同。 - Ted Phillips
3个回答

15

ISO 8601规范中要求提供时间时区信息,否则日期不能包含时区信息。因此,如果您希望保持ISO 8601兼容性,对于仅包含年、月和日的日期值,您不能提供额外的信息(除非使用下面提到的时间间隔)。

允许这种信息的一种格式是XML Schema(也称为“XSD”),在xs:date类型中,可以选择提供时间信息,直接跟在日期之后,例如2018-09-07Z。XSD规范在D.3 异常状况与ISO 8601格式不符中指出:

D.3.4 允许时区
数据类型日期、gYearMonth、gMonthDay、gDay、gMonth和gYear的词法表示允许可选的尾随时区说明。

我知道你没有询问关于XSD的内容,但这是我所知道的唯一一个自由在线可用的规范参考,它把日期上的时区标注为ISO 8601的一种偏差。

正如其他人指出的那样,考虑带有时区的日期是一件棘手的事情。在许多方面,仅包含日期值是不明确的。请考虑我向您展示的这个日历:

日历图片

在这个日历上标记一个日期并不能告诉我任何关于时区的信息。我可以把这个日历交给不同时区的人,他们仍然能谈论日期。只是如果我们同时指着“今天”,我们可能不是指着同一天。因此,时区只有在应用时间上下文(无论是“现在”还是特定的时间)时才适用。

然而,我们往往会根据某一天的所有时间点来进行理性思考,以某个时区为基准,比如你举的例子,“UTC Day”。我们的意思是它从一天的T00:00Z开始,到下一天的T00:00Z之前结束。这通常是像xs:date允许使用时区偏移量的原因。

如果我们想要严格遵守ISO 8601标准并表示相同的含义,我们必须提供一系列的日期+时间值,称为ISO 8601规范第4.4节中的“时间间隔”,并用斜杠(/)字符分隔。这样的值示例为:2018-09-07T00:00Z/2018-09-08T00:00Z。但要小心,因为ISO 8601没有说明结束日期应该如何被解释(是包括还是不包括在内)。详见此处

另一种ISO 8601表示间隔的方式允许一个开始时间和一个持续时间组件,例如2018-09-07T00:00Z/P1D。对我来说,这似乎是最接近完全符合ISO 8601标准的以UTC解释整个日期的方式。

不过,就我个人而言,如果我需要在日期中传达时区信息,即使它不严格符合规范,我也会使用2018-09-07Z。只要确保数据的所有消费者都同意这种格式。如果你无法做到这一点,只需传递2018-09-07并将字段命名为utcDate之类的名称。


0
这里的主要问题(在我看来)是你正在使用什么来输出日期/时间。
例如在C++中,你可以这样做:
time_t n = std::time(NULL);

tm *now = std::localtime(&n);

std::cout << std::put_time(now, "%F %z") << '\n';

这将产生类似以下的输出:

2018-09-07 -0700

如果你的真正问题是这是否对应于ISO 8601中指定的格式,我认为答案是否定的。但是,它仍然很容易生成。

哦,至少在我看来,只有带有时区的日期也可以完全有意义。即使你只关心日期,时区也告诉你该日期相对于地球上其他地方何时开始/结束,因此如果我说“2019年1月1日PDT”,伦敦的某个人知道所涉及的日期与他当地日期相差7小时。


-6

带有时区的日期没有意义,因为时区会调整一天内的小时/分钟。也许您有一个日期和一个单独的时区,需要用于其他事情?如果您需要国际日期线的时区,则还需要以小时:分钟的格式提供时间,否则时区将毫无意义。

顺便说一下,W3C也有一些关于ISO8601 日期和时间格式 的示例。 最后但并非最不重要的是,您始终可以编写自己的解析器,例如使用Antlr

还要记住,时区可能会随着时间而改变 - 例如普通时间与夏令时。


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