在 HTML 中使用日期和时间格式

某些 HTML 元素使用日期和/或时间值。本文将介绍指定这些值的字符串格式。

使用此类格式的元素包括某些形式的 <input> 元素,它们允许用户选择或指定日期、时间或两者,以及 <ins><del> 元素,它们的 datetime 属性指定内容插入或删除的日期或日期和时间。

对于 <input>,其 value 包含表示日期和/或时间的字符串的输入 type 值是:

示例

在深入探讨 HTML 中日期和时间字符串的编写和解析的复杂性之前,这里有一些示例,它们应该能让你很好地了解更常用的日期和时间字符串格式是什么样的。

HTML 日期和时间字符串示例
String 日期和/或时间
2005-06-07 2005 年 6 月 7 日 [详情]
08:45 上午 8:45 [详情]
08:45:25 上午 8:45 25 秒 [详情]
0033-08-04T03:40 33 年 8 月 4 日上午 3:40 [详情]
1977-04-01T14:00:30 1977 年 4 月 1 日下午 2:00 30 秒后 [详情]
1901-01-01T00:00Z 1901 年 1 月 1 日 UTC 午夜 [详情]
1901-01-01T00:00:01-04:00 1901 年 1 月 1 日东部标准时间 (EST) 午夜过 1 秒 [详情]

基础知识

在查看 HTML 元素使用的各种日期和时间相关字符串格式之前,了解一些关于它们定义方式的基本事实会很有帮助。HTML 对其日期和时间字符串使用 ISO 8601 标准的变体。值得回顾一下你正在使用的格式的描述,以确保你的字符串实际上与 HTML 兼容,因为 HTML 规范包含了比 ISO 8601 更精确的解析这些字符串的算法,因此日期和时间字符串的外观可能存在细微差异。

字符集

HTML 中的日期和时间始终是使用 ASCII 字符集的字符串。

年份数字

为了简化 HTML 中日期字符串的基本格式,规范要求所有年份都使用现代(或推算公历。虽然用户界面可能允许使用其他日历输入日期,但底层值始终使用公历。

虽然公历直到公元 1582 年才创建(取代了类似的儒略历),但出于 HTML 的目的,公历一直延伸到公元 1 年。请确保任何更早的日期都考虑了这一点。

对于 HTML 日期,年份始终至少有四位数字;公元 1000 年之前的年份用前导零 (0) 填充,因此 72 年写为 0072。不支持公元 1 年之前的年份,因此 HTML 不支持公元前 1 年或更早的年份。

一年通常为 365 天,但闰年除外。

闰年

闰年是指能被 400 整除的年份,能被 4 整除但不能被 100 整除的年份。尽管日历年通常为 365 天,但地球绕太阳公转一圈实际上需要大约 365.2422 天。闰年有助于调整日历,使其与地球在轨道上的实际位置保持同步。每四年在一年中增加一天,基本上使平均年份长度为 365.25 天,这接近正确。

对算法的调整(当年份能被 400 整除时为闰年,当年份能被 100 整除时跳过闰年)有助于使平均值更接近正确的日数(365.2425 天)。科学家偶尔会向日历中添加闰秒(真的),以处理剩余的万分之三的天数,并补偿地球自转逐渐自然发生的减速。

虽然 02 月,即二月,通常有 28 天,但在闰年中它有 29 天。

一年中的月份

一年有 12 个月,从 1 到 12 编号。它们总是由两位 ASCII 字符串表示,其值范围从 0112。有关月份数字及其对应的名称(和天数长度),请参阅月份中的天数部分的表格。

月份中的天数

月份数字 1、3、5、7、8、10 和 12 为 31 天。月份 4、6、9 和 11 为 30 天。月份 2,即二月,在大多数年份为 28 天,但在闰年中为 29 天。这在下表中详述。

一年中的月份及其天数长度
月份编号 名称(英语) 天数长度
01 一月 31
02 二月 28(闰年为 29)
03 三月 31
04 四月 30
05 可能 31
06 六月 30
07 七月 31
08 八月 31
09 九月 30
10 十月 31
11 十一月 30
12 十二月 31

周字符串

周字符串指定特定年份内的某个周。一个有效周字符串由一个有效的年份数字,后跟一个连字符(-,或 U+002D),然后是大写字母 W(U+0057),再后跟一个两位数的年份周数值组成。

年份周是一个介于 0153 之间的两位数字字符串。每周从周一开始,到周日结束。这意味着一月份的前几天可能被认为是上一个周年的组成部分,而十二月份的最后几天可能被认为是下一个周年的组成部分。一年中的第一个周是包含当年第一个星期四的周。例如,1953 年的第一个星期四是 1 月 1 日,所以那个周——从 12 月 29 日星期一开始——被认为是当年的第一个周。因此,1952 年 12 月 30 日发生在 1953-W01 周。

如果出现以下情况,则一年有 53 周:

  • 日历年(1 月 1 日)的第一天是星期四
  • 当年的第一天(1 月 1 日)是星期三,并且该年是闰年

所有其他年份有 52 周。

周字符串 周和年份(日期范围)
2001-W37 2001 年第 37 周(2001 年 9 月 10-16 日)
1953-W01 1953 年第 1 周(1952 年 12 月 29 日-1953 年 1 月 4 日)
1948-W53 1948 年第 53 周(1948 年 12 月 27 日-1949 年 1 月 2 日)
1949-W01 1949 年第 1 周(1949 年 1 月 3-9 日)
0531-W16 531 年第 16 周(531 年 4 月 13-19 日)
0042-W04 42 年第 4 周(42 年 1 月 21-27 日)

请注意,年份和周数都用前导零填充,年份填充到四位数字,周数填充到两位数字。

月字符串

月字符串表示时间上的特定月份,而不是一年中的通用月份。也就是说,HTML 月字符串不表示“一月”,而是表示月份和年份的组合,例如“1972 年一月”。

有效月字符串由一个有效的年份数字(至少四位数字的字符串),后跟一个连字符(-,或 U+002D),再后跟一个两位数的数字月份数字组成,其中 01 代表一月,12 代表十二月。

月字符串 月份和年份
17310-09 17310 年 9 月
2019-01 2019 年 1 月
1993-11 1993 年 11 月
0571-04 571 年 4 月
0001-07 公元 1 年 7 月

请注意,所有年份至少是四位数字;少于四位数字的年份用前导零填充。

日期字符串

一个有效的日期字符串由一个月份字符串,后跟一个连字符(-,或 U+002D),再后跟一个两位数的月份中的日期组成。

日期字符串 完整日期
1993-11-01 1993 年 11 月 1 日
1066-10-14 1066 年 10 月 14 日
0571-04-22 571 年 4 月 22 日
0062-02-05 62 年 2 月 5 日

时间字符串

时间字符串可以指定精确到分钟、秒或毫秒的时间。不允许只指定小时或分钟。一个有效时间字符串最少由一个两位数的小时,后跟一个冒号(:,U+003A),然后是一个两位数的分钟组成。分钟后面可以选择再跟一个冒号和两位数的秒数。毫秒可以可选地通过添加一个小数点字符(.,U+002E),后跟一、二或三位数字来指定。

还有一些额外的基本规则:

  • 小时始终使用 24 小时制指定,其中 00 表示午夜,晚上 11 点表示 23。不允许使用 00 - 23 范围之外的值。
  • 分钟必须是介于 0059 之间的两位数。不允许使用该范围之外的值。
  • 如果省略秒数(以指定仅精确到分钟的时间),则分钟数后不应有冒号。
  • 如果指定,秒数的整数部分必须介于 0059 之间。您不能通过使用 6061 等值来指定闰秒。
  • 如果指定了秒数并且是整数,则其后不得有小数点。
  • 如果包含秒的小数部分,它可能是一到三位数字长,表示毫秒数。它位于时间字符串的秒组件后的小数点之后。
时间字符串 时间
00:00:30.75 上午 12:00:30.75(午夜后 30.75 秒)
12:15 下午 12:15
13:44:25 下午 1:44:25(下午 1:44 后 25 秒)

本地日期和时间字符串

有效的 datetime-local 字符串由一个 date 字符串和一个 time 字符串通过字母 T 或空格字符分隔连接而成。字符串中不包含时区信息;日期和时间被假定为用户的本地时区。

当您设置 datetime-local 输入的 value 时,字符串会标准化为标准形式。标准化的 datetime 字符串始终使用字母 T 分隔日期和时间,并且字符串的时间部分尽可能短。这是通过在秒组件值为 :00 时省略它来实现的。

有效的 datetime-local 字符串示例
日期/时间字符串 标准化日期/时间字符串 实际日期和时间
1986-01-28T11:38:00.01 1986-01-28T11:38:00.01 1986 年 1 月 28 日上午 11:38:00.01
1986-01-28 11:38:00.010

1986-01-28T11:38:00.01

请注意,标准化后,此字符串与上一个 datetime-local 字符串相同。空格已被 T 字符替换,并且秒小数部分中的尾随零已被删除,以使字符串尽可能短。

1986 年 1 月 28 日上午 11:38:00.01
0170-07-31T22:00:00

0170-07-31T22:00

请注意,此日期的标准化形式会省略 :00,表示秒数为零,因为当秒数为零时是可选的,并且标准化字符串会最小化字符串的长度。

170 年 7 月 31 日晚上 10:00

全球日期和时间字符串

全局日期和时间字符串指定日期和时间以及它发生的时区。有效全局日期和时间字符串的格式与本地日期和时间字符串相同,只是在时间之后附加了一个时区字符串。

时区偏移字符串

时区偏移字符串指定与标准时间基准相差的小时和分钟数的正负偏移量。有两个标准时间基准,它们非常接近,但不完全相同:

  • 对于 20 世纪 60 年代初协调世界时 (UTC) 建立后的日期,时间基准是 Z,偏移量表示特定时区与本初子午线 0º 经度(穿过英国格林威治皇家天文台)时间的偏移量。
  • 对于 UTC 之前的日期,时间基准则以UT1表示,即本初子午线上的当代地球太阳时间。

时区字符串紧跟在日期和时间字符串中的时间之后。您可以将 Z 指定为时区偏移字符串,以表示时间以 UTC 指定。否则,时区字符串的构造方式如下:

  1. 表示偏移符号的字符:对于本初子午线以东的时区,为加号字符(+,或 U+002B);对于本初子午线以西的时区,为减号字符(-,或 U+002D)。
  2. 时区与本初子午线的偏移小时数的两位数。此值必须介于 0023 之间。
  3. 一个可选的冒号(:)字符。
  4. 超过小时的两位分钟数;此值必须介于 0059 之间。

虽然这种格式允许 -23:59 到 +23:59 之间的时区,但当前时区偏移范围是 -12:00 到 +14:00,并且目前没有时区与小时的偏移量不是 003045 分钟。这种情况可能会随时发生变化,因为各国可以随时以他们希望的任何方式改变他们的时区。

有效全球日期和时间字符串示例
全球日期和时间字符串 实际全球日期和时间 本初子午线的日期和时间
2005-06-07T00:00Z 2005 年 6 月 7 日 UTC 午夜 2005 年 6 月 7 日午夜
1789-08-22T12:30:00.1-04:00 1789 年 8 月 22 日东部夏令时 (EDT) 下午 12:30 过十分之一秒 1789 年 8 月 22 日下午 4:30 过十分之一秒
3755-01-01 00:00+10:00 3755 年 1 月 1 日澳大利亚东部标准时间 (AEST) 午夜 3754 年 12 月 31 日下午 2:00

日期问题

由于数据存储和精度问题,您可能需要注意一些客户端和服务器端问题。

Y2K38 问题(通常在服务器端)

JavaScript 使用双精度浮点数来存储日期,就像所有数字一样,这意味着 JavaScript 代码不会受到 Y2K38 问题的影响,除非使用整数强制/位操作,因为所有 JavaScript 位运算符都使用 32 位带符号的 2 补码整数。

问题在于服务器端:存储大于 2^31 - 1 的日期。为了解决这个问题,您必须在服务器上使用无符号 32 位整数、带符号 64 位整数或双精度浮点数来存储所有日期。如果您的服务器是用 PHP 编写的,修复可能需要将您的 PHP 升级到更新版本,并将您的硬件升级到 x86_64 或 IA64。如果您仍然使用其他硬件,您可以尝试在 32 位虚拟机中模拟 64 位硬件,但大多数 VM 不支持这种虚拟化,因为稳定性可能会受到影响,并且性能肯定会大大降低。

Y10k 问题(通常在客户端)

在许多服务器中,日期存储为数字而不是字符串——固定大小的数字,与格式无关(除了字节序)。在 10,000 年之后,这些数字只会比以前大一点,所以许多服务器不会在 10,000 年之后提交的表单中看到问题。

问题在于客户端:解析年份中包含超过 4 位数字的日期。

html
<!--midnight of January 1st, 10000: the exact time of Y10K-->
<input type="datetime-local" value="+010000-01-01T05:00" />

我们需要为任意位数的年份准备我们的代码——而不仅仅是 5 位。以下 JavaScript 函数以编程方式设置值

js
function setValue(element, date) {
  const isoString = date.toISOString();
  element.value = isoString.substring(0, isoString.indexOf("T") + 6);
}

如果 Y10K 问题将在您去世几个世纪后发生,为什么要担心它呢?正是因为您将已经去世,所以使用您的软件的公司将不得不继续使用您的软件,而没有其他程序员足够了解系统来修复它。

另见