5. Date and Time format (日期和时间格式)
本节讨论日期和时间格式的理想特性,并定义用于互联网协议的ISO 8601配置文件。
5.1. Ordering (排序)
如果日期和时间组件从最不精确到最精确排序,则可以实现一个有用的属性。假设日期和时间的时区相同(例如,全部为UTC),使用相同的字符串表示(例如,全部为"Z"或全部为"+00:00"),并且所有时间具有相同数量的小数秒位数,则日期和时间字符串可以作为字符串排序(例如,在C中使用strcmp()函数),并将产生时间有序序列。可选标点符号的存在会违反此特性。
示例:
正确排序(年-月-日 时:分:秒):
2002-01-15T10:00:00Z
2002-07-20T15:30:00Z
2002-12-31T23:59:59Z
错误格式(月/日/年)无法正确排序:
01/15/2002 10:00:00
12/31/2002 23:59:59 ← 字符串排序会在7月之前
07/20/2002 15:30:00
5.2. Human Readability (人类可读性)
人类可读性已被证明是互联网协议的一个有价值的特性。人类可读的协议大大降低了调试成本,因为telnet通常足以作为测试客户端,并且网络分析器无需修改协议知识。另一方面,人类可读性有时会导致互操作性问题。
问题示例:
❌ "10/11/1996" 完全不适合全球交换
美国: 1996年10月11日
欧洲: 1996年11月10日
❌ 翻译月份缩写
英文: "Jan", "Feb", "Mar"
法文: "Jan", "Fév", "Mar" ← 破坏互操作性
因为没有一种日期和时间格式能够按照所有国家的惯例可读,互联网客户端应该 (SHOULD) 准备将日期转换为适合本地的显示格式。这可能包括将UTC转换为本地时间。
5.3. Rarely Used Options (很少使用的选项)
包含很少使用的选项的格式可能导致互操作性问题。这是因为很少使用的选项不太可能在alpha或beta测试中使用,因此不太可能发现解析中的错误。为了互操作性,应该尽可能将很少使用的选项设为强制性或省略。
下面定义的格式仅包括一个很少使用的选项:秒的小数部分 (Fractions of a Second)。预计这仅由需要日期时间戳严格排序或具有不寻常精度要求的应用程序使用。
5.4. Redundant Information (冗余信息)
如果日期时间格式包含冗余信息,则会引入冗余信息可能不相关的可能性。例如,在日期时间格式中包含星期几会引入星期几不正确但日期正确的可能性,反之亦然。由于从日期计算星期几并不困难(参见附录B),因此不应在日期时间格式中包含星期几。
问题示例:
❌ "Monday, 2002-07-16T10:00:00Z"
问题: 2002年7月16日实际是星期二,不是星期一
如果星期和日期不一致,应该相信哪个?
✅ "2002-07-16T10:00:00Z"
解决: 省略星期,需要时可计算得出
5.5. Simplicity (简洁性)
ISO 8601 [ISO8601] 中指定的完整日期和时间格式集非常复杂,试图提供多种表示和部分表示。附录A包含将ISO 8601的完整语法翻译为ABNF的尝试。互联网协议有些不同的要求,简洁性已被证明是一个重要特性。此外,互联网协议通常需要完整的数据规范以实现真正的互操作性。因此,ISO 8601的完整语法被认为对于大多数互联网协议过于复杂。
以下部分定义了用于互联网的ISO 8601配置文件。它是ISO 8601扩展格式的一致子集。通过使大多数字段和标点符号成为强制性来实现简洁性。
5.6. Internet Date/Time Format (互联网日期时间格式)
以下ISO 8601 [ISO8601] 日期配置文件应该 (SHOULD) 用于互联网上的新协议。这使用[ABNF]中定义的语法描述符号指定。
ABNF语法定义
date-fullyear = 4DIGIT
date-month = 2DIGIT ; 01-12
date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on
; month/year
time-hour = 2DIGIT ; 00-23
time-minute = 2DIGIT ; 00-59
time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second
; rules
time-secfrac = "." 1*DIGIT
time-numoffset = ("+" / "-") time-hour ":" time-minute
time-offset = "Z" / time-numoffset
partial-time = time-hour ":" time-minute ":" time-second
[time-secfrac]
full-date = date-fullyear "-" date-month "-" date-mday
full-time = partial-time time-offset
date-time = full-date "T" full-time
注意事项
大小写: 根据[ABNF]和ISO8601,此语法中的"T"和"Z"字符也可以分别使用小写"t"或"z"。
在区分大小写的环境(如XML)中使用此格式的规范可以 (MAY) 进一步限制日期时间语法,使得日期时间语法中使用的字母'T'和'Z'必须始终大写。生成此格式的应用程序应该 (SHOULD) 使用大写字母。
分隔符: ISO 8601定义由"T"分隔的日期和时间。使用此语法的应用程序可以 (MAY) 选择,为了可读性,指定由(例如)空格字符分隔的full-date和full-time。
格式示例
标准格式:
2002-07-15T10:30:00Z
2002-07-15T10:30:00.123Z
2002-07-15T10:30:00+08:00
2002-07-15T10:30:00-04:00
带小数秒:
2002-07-15T10:30:00.123456Z
2002-07-15T10:30:00.52Z
可读变体(非标准但允许):
2002-07-15 10:30:00Z
2002-07-15t10:30:00z
5.7. Restrictions (限制)
语法元素date-mday表示当前月份内的日期数。最大值根据月份和年份而变化:
| 月份编号 | 月份/年份 | date-mday最大值 |
|---|---|---|
| 01 | January (一月) | 31 |
| 02 | February, normal (二月,平年) | 28 |
| 02 | February, leap year (二月,闰年) | 29 |
| 03 | March (三月) | 31 |
| 04 | April (四月) | 30 |
| 05 | May (五月) | 31 |
| 06 | June (六月) | 30 |
| 07 | July (七月) | 31 |
| 08 | August (八月) | 31 |
| 09 | September (九月) | 30 |
| 10 | October (十月) | 31 |
| 11 | November (十一月) | 30 |
| 12 | December (十二月) | 31 |
闰秒
语法元素time-second可以 (MAY) 在发生闰秒的月份末尾具有值"60"。闰秒不是在一个小时的末尾计算,而是在月份的末尾计算,并且在发生时是即时的,而不是全天平滑分布的。有关闰秒的更多信息,请参见附录D。
闰秒示例:
1990-12-31T23:59:60Z ✅ 有效(1990年12月31日有闰秒)
1990-12-31T23:59:61Z ❌ 无效(最多60秒)
1990-06-15T23:59:60Z ❌ 无效(闰秒仅在月末)
5.8. Examples (示例)
以下是有效RFC 3339日期时间戳的示例:
1985-04-12T23:20:50.52Z
表示: 1985年4月12日 23:20:50.52 UTC
1996-12-19T16:39:57-08:00
表示: 1996年12月19日 16:39:57 太平洋标准时间(PST)
等同于UTC: 1996-12-20T00:39:57Z
1990-12-31T23:59:60Z
表示: 1990年12月31日的闰秒
1990-12-31T15:59:60-08:00
表示: 1990年12月31日的闰秒,以PST表示
等同于UTC: 1990-12-31T23:59:60Z
1937-01-01T12:00:27.87+00:20
表示: 1937年1月1日 12:00:27.87,UTC+00:20
(历史时区示例)
无效示例
❌ 1985-04-12 (缺少时间)
❌ 23:20:50.52Z (缺少日期)
❌ 1985-04-12 23:20:50.52Z (应使用'T'而非空格,虽然某些实现允许)
❌ 1985-04-32T23:20:50.52Z (日期无效:4月没有32日)
❌ 1985-02-29T23:20:50.52Z (日期无效:1985年不是闰年)
实施建议: 始终生成标准格式(使用'T'分隔符和大写'Z'),但要宽容地解析(接受't', 'z'和可能的空格分隔符)。