时间戳:互联网上的日期和时间

简介: 日期和时间格式在 Internet 上引起了很多混乱和互操作性问题。本文档解决了在 Internet 协议中表示和使用日期和时间时遇到的许多问题,并提出了改进一致性和互操作性的建议。

640.gif


RFC3339:Date and Time on the Internet: Timestamps,July 2002


本备忘录的状态


本文档为 Internet 社区指定了 Internet 标准跟踪协议,并请求讨论和改进建议。本协议的标准化状态和现状请参考当前版本的《互联网官方协议标准》(STD 1)。本备忘录的分发不受限制。


版权声明


版权所有 (C) 互联网协会 (2002)版权所有。


梗概


本文档定义了用于 Internet 协议的日期和时间格式,该格式是 ISO 8601 标准的配置文件,用于使用公历表示日期和时间。


1、 简介


日期和时间格式在 Internet 上引起了很多混乱和互操作性问题。本文档解决了在 Internet 协议中表示和使用日期和时间时遇到的许多问题,并提出了改进一致性和互操作性的建议。


本文档包含 ISO 8601 [ISO8601] 标准的 Internet 配置文件,用于使用公历表示日期和时间。


日期和时间值可能以多种方式出现在 Internet 协议中:本文档仅关注一种常见用法,即。Internet 协议事件的时间戳。这种有限的考虑具有以下后果:


** 所有日期和时间都假定为“当前时代”,介于 0000AD 和 9999AD 之间。


** 表示的所有时间都与协调世界时 (Coordinated Universal Time,UTC) 有规定的关系(偏移)。(这与在调度应用程序中可能知道本地时间和位置的某些用法不同,但与 UTC 的实际关系可能取决于政客或管理员的未知或不可知的行为。UTC 时间对应于2005 年 3 月23 日 17:00在纽约可能取决于有关夏令时的行政决定。本规范完全避免了此类考虑。)


** 时间戳可以表示在引入 UTC 之前发生的时间。此类时间戳是相对于通用时间表示的,使用规定时间的最佳可用实践。


** 日期和时间表达式表示一个瞬间。此处不包括时间段或间隔的描述。


2、 定义


本文档中的关键词“必须”、“不得”、“需要”、“应该”、“不应”、“应该”、“不应该”、“推荐”、“可以”和“可选”是按照 RFC 2119 [RFC2119] 中的描述进行解释。


世界标准时间/ UTC


由国际测量局 (Bureau International des Poids et Mesures,BIPM) 维护的协调世界时。


秒/ second


国际单位制中时间计量的基本单位。它被定义为 9,192,631,770 个微波光循环的持续时间,由处于基态的铯 133 原子在不受外部场干扰的情况下的超精细跃迁吸收或发射。


分钟/ minute


一段 60 秒的时间。但是,另请参阅第 5.7 节和附录 D 中的限制,了解如何在分钟内表示闰秒。


小时/ hour


一段60分钟的时间。


日/ day


一段 24 小时的时间。


闰年/ leap year


在公历中,一年有 366 天。闰年是指能被四整除的年份,如果是整百年的年份,需要能被四百整除。


ABNF


Augmented Backus-Naur Form,一种用于表示协议或语言中允许的字符串的格式,如 [ABNF] 中所定义。


电子邮件日期/时间格式


Internet 邮件使用的日期/时间格式,由 RFC 2822 [IMAIL-UPDATE] 定义。


互联网日期/时间格式


本文档第 5 节中定义的日期格式。


时间戳/ Timestamp


该术语在本文档中用于指代某个时刻的明确表示。


Z


一个后缀,当应用于时间时,表示 UTC 偏移量 00:00;经常说的“祖鲁语/ Zulu”来自国际民航组织字母“Z”的拼音字母表。


有关时间标度的更多信息,请参见 [NTP] 的附录 E、[ISO8601] 的第 3 节和相应的 ITU 文件 [ITU-R-TF]。


3、 两位数的年份


以下要求是为了解决2位数年份的歧义问题:


** 互联网协议必须在日期中生成四位数年份。


** 不推荐使用两位数的年份。如果收到 2 位数的年份,则只有在不正确的解释不会导致协议或处理失败(例如,如果仅用于记录或跟踪目的)时才应接受。


** 使用两位数年份的程序可能会将 1999 年之后的年份表示为三位数字。如果程序只是从年份中减去 1900 并且不检查位数,就会发生这种情况。希望稳健地处理由此类损坏的软件生成的日期的程序可能会将 1900 年增加到三位数。


** 使用两位数年份的程序可能会将 1999 年后的年份表示为 ":0", ":1", ... ":9", ";0", ...从年份中减去 1900 并将十年添加到 US-ASCII 字符零。希望稳健地处理由此类损坏软件生成的日期的程序应检测非数字十年并进行适当解释。


两位数年份的问题充分说明了为什么 Internet 协议中使用的所有日期和时间都必须完全限定。


4、 本地时间


4.1、 协调世界时 (UTC)


由于当地时区的夏令时规则非常复杂,并且可以在不可预测的时间根据当地法律进行更改,因此最好通过使用协调世界时 (UTC) 来实现真正的互操作性。此规范不符合本地时区规则。


4.2、 局部偏移


本地时间和 UTC 之间的偏移量通常是有用的信息。例如,在电子邮件(RFC2822,[IMAIL-UPDATE])中,本地偏移量提供了一种有用的启发式方法来确定快速响应的概率。在过去 [IMAIL]、[HOST-REQ] 尝试用字母字符串标记本地偏移量导致互操作性较差。因此,RFC2822 [IMAIL-UPDATE] 强制使用数字偏移量。


数字偏移计算为“本地时间减去 UTC”。因此可以通过从本地时间中减去偏移量来确定 UTC 中的等效时间。例如,18:50:00-04:00 与 22:50:00Z 是同一时间。(此示例显示了通过添加偏移量的绝对值来处理的负偏移量。)


注意:遵循 ISO 8601,数字偏移量仅表示与 UTC 相差整数分钟的时区。但是,许多历史时区与 UTC 之间存在非整数分钟数的差异。要准确表示此类历史时间戳,应用程序必须将它们转换为可表示的时区。


4.3、 未知的本地抵消约定


如果 UTC 时间已知,但与本地时间的偏移量未知,则可以用偏移量“-00:00”表示。这在语义上不同于“Z”或“+00:00”的偏移量,这意味着 UTC 是指定时间的首选参考点。RFC2822 [IMAIL-UPDATE] 描述了一个类似的电子邮件约定。


4.4、 不合格的当地时间


当前连接到 Internet 的许多设备以本地时间运行其内部时钟,并且不知道 UTC。虽然互联网在创建规范时确实有接受现实的传统,但这不应该以牺牲互操作性为代价。由于在全球大约 23/24 地区无法解释不合格的本地时区,因此不合格的本地时间的互操作性问题被认为是 Internet 无法接受的。使用本地时间配置的系统,不知道相应的 UTC 偏移量,并依赖于与其他 Internet 系统的时间同步,必须使用确保与 UTC 正确同步的机制。一些合适的机制是:


** 使用网络时间协议 [Network Time Protocol,NTP] 获取 UTC 时间。

** 使用同一本地时区的另一台主机作为互联网的网关。该主机必须更正传输到其他主机的不合格本地时间。

** 提示用户本地时区和夏令时规则设置。


5、 日期和时间格式


本节讨论日期和时间格式的理想特性,并定义用于 Internet 协议的 ISO 8601 配置文件。


5.1、 排序


如果日期和时间组件按从最不精确到最精确的顺序排列,则可以获得有用的属性。假设日期和时间的时区相同(例如,全部为UTC),使用相同的字符串表示(例如,全部为“Z”或全部为“+00:00”),并且所有时间具有相同的数字小数秒数字,那么日期和时间字符串可以作为字符串进行排序(例如,使用 C 中的 strcmp() 函数)并且将产生按时间排序的序列。可选标点符号的存在将违反此特性。


5.2、 人类可读性


人类可读性已被证明是互联网协议的一个有价值的特征。人类可读的协议大大降低了调试成本,因为 telnet 通常足以作为测试客户端,并且网络分析器不需要在了解协议的情况下进行修改。另一方面,人类可读性有时会导致互操作性问题。例如,日期格式“10/11/1996”完全不适合全球互换,因为它在不同国家的解释不同。此外,[IMAIL] 中的日期格式导致了互操作性问题,当人们假设允许任何文本字符串并将三个字母的缩写翻译成其他语言或替换更容易生成的日期格式(例如 C 函数使用的格式)时间)。因此,必须在人类可读性和互操作性之间取得平衡。


因为根据所有国家的惯例,没有日期和时间格式是可读的,所以 Internet 客户端应该准备将日期转换为适合当地的显示格式。这可能包括将 UTC 转换为本地时间。


5.3、 很少使用的选项


包含很少使用的选项的格式可能会导致互操作性问题。这是因为很少使用的选项在 alpha 或 beta 测试中不太可能使用,因此解析中的错误不太可能被发现。为了互操作性,应尽可能强制或省略很少使用的选项。


下面定义的格式仅包括一个很少使用的选项:几分之一秒。预计这将仅用于需要对日期/时间戳进行严格排序或具有不寻常精度要求的应用程序。


5.4、 冗余信息


如果日期/时间格式包含冗余信息,则会引入冗余信息不相关的可能性。例如,在日期/时间格式中包含星期几可能会导致星期几不正确但日期正确,反之亦然。由于根据日期计算星期几并不困难(请参阅附录 B),因此不应将星期几包含在日期/时间格式中。


5.5、 简洁


ISO 8601 [ISO8601] 中指定的完整日期和时间格式集非常复杂,试图提供多种表示和部分表示。附录 A 尝试将 ISO 8601 的完整语法转换为 ABNF。互联网协议有一些不同的要求,简单性已被证明是一个重要的特征。此外,Internet 协议通常需要完整的数据规范才能实现真正的互操作性。因此,对于大多数 Internet 协议来说,ISO 8601 的完整语法被认为过于复杂。


以下部分定义了用于 Internet 的 ISO 8601 配置文件。它是符合 ISO 8601 扩展格式的子集。通过强制大多数字段和标点符号来实现简单性。


5.6、 互联网日期/时间格式


以下 ISO 8601 [ISO8601] 日期配置文件应该用于 Internet 上的新协议。这是使用 [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”。


此日期/时间格式可用于区分大小写字母“A”-“Z”和“a”-“z”的某些环境或上下文(例如 XML)。在此类环境中使用此格式的规范可能会进一步限制日期/时间语法,以便日期/时间语法中使用的字母“T”和“Z”必须始终为大写。生成这种格式的应用程序应该使用大写字母。


注意:ISO 8601 定义了用“T”分隔的日期和时间。使用这种语法的应用程序可能会选择,为了可读性,指定一个完整的日期和完整的时间,由(比如)一个空格字符分隔。


5.7、 限制


语法元素 date-mday 表示当月内的天数。最大值因月份和年份而异,如下所示:


640.png


附录 C 包含示例 C 代码,用于确定年份是否为闰年。


语法元素 time-second 在发生闰秒的月末可能具有值“60”——迄今为止:六月 (XXXX-06-30T23:59:60Z) 或十二月 (XXXX-12-31T23: 59:60Z);有关闰秒表,请参见附录 D。还可以减去闰秒,此时 time-second 的最大值为“58”。在所有其他时间,time-second 的最大值为“59”。此外,在“Z”以外的时区中,闰秒点会移动区域偏移量(因此它发生在全球同一时刻)。


闰秒无法预测到遥远的未来。国际地球自转服务发布公告 [International Earth Rotation Service,IERS],宣布闰秒并发出几周警告。在宣布闰秒之前,应用程序不应生成涉及插入的闰秒的时间戳。


尽管 ISO 8601 允许小时为“24”,但 ISO 8601 的此配置文件只允许小时的值介于“00”和“23”之间,以减少混淆。


5.8、 举例


以下是 Internet 日期/时间格式的一些示例。

1985-04-12T23:20:50.52Z


这表示 UTC 时间 1985 年 4 月 12 日第 23 小时后的 20 分 50.52 秒。

1996-12-19T16:39:57-08:00


这表示 1996 年 12 月 19 日第 16 小时之后的 39 分 57 秒,与 UTC(太平洋标准时间)的偏移量为 -08:00。请注意,这等效于 UTC 中的 1996-12-20T00:39:57Z。

1990-12-31T23:59:60Z


这代表了 1990 年底插入的闰秒。

1990-12-31T15:59:60-08:00


这代表太平洋标准时间的同一闰秒,比 UTC 晚 8 小时。

1937-01-01T12:00:27.87+00:20


这表示与荷兰时间 1937 年 1 月 1 日中午相同的时刻。根据法律,从 1909 年 5 月 1 日到 1937 年 6 月 30 日,荷兰的标准时间比 UTC 早 19 分 32.13 秒。无法使用 HH:MM 格式准确表示此时区,并且此时间戳使用最接近的可表示的 UTC 偏移量。


6、 参考文献

[ZELLER] Zeller, C., "Kalender-Formeln", Acta Mathematica, Vol. 9, Nov 1886.
[IMAIL] Crocker, D., "Standard for the Format of Arpa Internet Text Messages", STD 11, RFC 822, August 1982.
[IMAIL-UPDATE] Resnick, P., "Internet Message Format", RFC 2822, April 2001.
[ABNF] Crocker, D. and P. Overell, "Augmented BNF for Syntax Specifications: ABNF", RFC 2234, November 1997.
[ISO8601] "Data elements and interchange formats -- Information interchange -- Representation of dates and times", ISO 8601:1988(E), International Organization for Standardization, June, 1988.
[ISO8601:2000] "Data elements and interchange formats -- Information interchange -- Representation of dates and times", ISO 8601:2000, International Organization for Standardization, December, 2000.
[HOST-REQ] Braden, R., "Requirements for Internet Hosts -- Application and Support", STD 3, RFC 1123, October 1989.
[IERS] International Earth Rotation Service Bulletins, <http://hpiers.obspm.fr/eop- pc/products/bulletins.html>.
[NTP] Mills, D, "Network Time Protocol (Version 3) Specification, Implementation and Analysis", RFC 1305, March 1992.
[ITU-R-TF] International Telecommunication Union Recommendations for Time Signals and Frequency Standards Emissions. <http://www.itu.ch/publications/itu-r/iturtf.htm>
[RFC2119] Bradner, S, "Key words for use in RFCs to Indicate Requirement Levels", BCP 14, RFC 2119, March 1997.


7、 安全考虑


由于站点的本地时区可能有助于确定系统不太可能被监视并且可能更容易受到安全探测影响的时间,因此某些站点可能希望仅以 UTC 时间发送时间。其他人可能认为这是由于偏执狂而失去了有用的功能。


附录 A、 ISO 8601 收集的 ABNF


此信息基于 1988 版 ISO 8601,2000 版中可能会有一些更改。


ISO 8601 没有为其定义的日期和时间格式指定正式的语法。以下是根据 ISO 8601 创建正式语法的尝试。这仅供参考,可能包含错误。ISO 8601 仍然是权威参考。


请注意,由于 ISO 8601 中的歧义,必须做出一些解释。首先,ISO 8601 不清楚是否允许混合使用基本格式和扩展格式。这种语法允许混合。ISO 8601 并不清楚仅当分和秒为 0 时是否允许 24 小时。这假设在任何情况下都允许 24 小时。第 5.7 节中对 date-mday 的限制适用。ISO 8601 规定在某些情况下可以省略“T”。该语法需要“T”以避免歧义。ISO 8601 还要求(在第 5.3.1.3 节中)如果小数小于 1,则小数前加“0”。ISO 8601 的附录 B.2 给出了十进制小数前没有“0”的例子。该语法假设 5.3.1.3 节是正确的,而附件 B.2 是错误的。


date-century    = 2DIGIT  ; 00-99
date-decade     =  DIGIT  ; 0-9
date-subdecade  =  DIGIT  ; 0-9
date-year       = date-decade date-subdecade
date-fullyear   = date-century date-year
date-month      = 2DIGIT  ; 01-12
date-wday       =  DIGIT  ; 1-7  ; 1 is Monday, 7 is Sunday
date-mday       = 2DIGIT  ; 01-28, 01-29, 01-30, 01-31 based on month/year
date-yday       = 3DIGIT  ; 001-365, 001-366 based on year
date-week       = 2DIGIT  ; 01-52, 01-53 based on year
datepart-fullyear = [date-century] date-year ["-"]
datepart-ptyear   = "-" [date-subdecade ["-"]]
datepart-wkyear   = datepart-ptyear / datepart-fullyear
dateopt-century   = "-" / date-century
dateopt-fullyear  = "-" / datepart-fullyear
dateopt-year      = "-" / (date-year ["-"])
dateopt-month     = "-" / (date-month ["-"])
dateopt-week      = "-" / (date-week ["-"])
datespec-full     = datepart-fullyear date-month ["-"] date-mday
datespec-year     = date-century / dateopt-century date-year
datespec-month    = "-" dateopt-year date-month [["-"] date-mday]
datespec-mday     = "--" dateopt-month date-mday
datespec-week     = datepart-wkyear "W" (date-week / dateopt-week date-wday)
datespec-wday     = "---" date-wday
datespec-yday     = dateopt-fullyear date-yday
date              = datespec-full / datespec-year


时间:


time-hour         = 2DIGIT ; 00-24
time-minute       = 2DIGIT ; 00-59
time-second       = 2DIGIT ; 00-58, 00-59, 00-60 based on leap-second rules
time-fraction     = ("," / ".") 1*DIGIT
time-numoffset    = ("+" / "-") time-hour [[":"] time-minute]
time-zone         = "Z" / time-numoffset
timeopt-hour      = "-" / (time-hour [":"])
timeopt-minute    = "-" / (time-minute [":"])
timespec-hour     = time-hour [[":"] time-minute [[":"] time-second]]
timespec-minute   = timeopt-hour time-minute [[":"] time-second]
timespec-second   = "-" timeopt-minute time-second
timespec-base     = timespec-hour / timespec-minute / timespec-second
time              = timespec-base [time-fraction] [time-zone]
iso-date-time     = date "T" time


持续时间:


dur-second        = 1*DIGIT "S"
dur-minute        = 1*DIGIT "M" [dur-second]
dur-hour          = 1*DIGIT "H" [dur-minute]
dur-time          = "T" (dur-hour / dur-minute / dur-second)
dur-day           = 1*DIGIT "D"
dur-week          = 1*DIGIT "W"
dur-month         = 1*DIGIT "M" [dur-day]
dur-year          = 1*DIGIT "Y" [dur-month]
dur-date          = (dur-day / dur-month / dur-year) [dur-time]
duration          = "P" (dur-date / dur-time / dur-week)


期间:


period-explicit   = iso-date-time "/" iso-date-time
period-start      = iso-date-time "/" duration
period-end        = duration "/" iso-date-time
period            = period-explicit / period-start / period-end


附录 B、 星期几


以下是一个基于 Zeller's Congruence [Zeller] 的示例 C 子程序,可用于获取 0000-03-01 或之后日期的星期几:


char *day_of_week(int day, int month, int year)
{
  int cent;
  char *dayofweek[] = {
  "Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday"
  };
/* 调整月份,所以二月是最后一个 */
  month -= 2;
  if (month < 1) {
    month += 12;
    --year;
  }
/* 按世纪划分 */
  cent = year / 100;
  year %= 100;
  return (dayofweek[((26 * month - 2) / 10 + day + year + year / 4 + cent / 4 + 5 * cent) % 7]);
}


附录 C、 闰年


下面是一个示例 C 子程序,用于计算一年是否为闰年:


/* 如果年份是闰年,则返回非零值。必须使用 4 位数字年份。*/
int leap_year(int year)
  {
    return (year % 4 == 0 && (year % 100 != 0 || year % 400 == 0));
  }


附录 D、 闰秒


有关闰秒的信息可以在以下位置找到:<http://tycho.usno.navy.mil/leapsec.html>。它特别指出:


在 UTC 中引入闰秒的决定是国际地球自转服务 (International Earth Rotation Service,IERS) 的责任。根据CCIR建议,优先选择12月底和6月底的机会,其次选择3月底和9月底的机会。


需要时,闰秒的插入会在 UTC 的一天结束时作为额外的秒发生,由格式为 YYYY-MM-DDT23:59:60Z 的时间戳表示。闰秒在所有时区同时发生,因此时区关系不受影响。有关闰秒的一些示例,请参阅第 5.8 节。


下表摘自美国海军天文台维护的表格。源数据位于:

<ftp://maia.usno.navy.mil/ser7/tai-utc.dat>


该表显示了闰秒的日期,以及该闰秒之后的时间标准 TAI(Temps Atomique International,是基于 SI 秒连续计数的国际原子时标。未通过闰秒调整)与 UTC 之间的差异。


640.png


致谢


以下人员为本文档的早期版本提供了有用的建议:Ned Freed、Neal McBurnett、David Keegel、Markus Kuhn、Paul Eggert 和 Robert Elz。还要感谢 IETF 日历/调度工作组邮件列表的参与者以及时区邮件列表的参与者。


以下审稿人为本次修订提供了有益的建议:Tom Harsch、Markus Kuhn、Pete Resnick、Dan Kohn。Paul Eggert 提供了许多关于闰秒和时区偏移的微妙之处的仔细观察。以下人员指出了对早期草案的更正和改进:John Stockton 博士、Jutta Degener、Joe Abley 和 Dan Wing。


完整的版权声明


版权所有 (C) 互联网协会 (2002)。版权所有。


本文件及其译文可能会被复制和提供给他人,并且可以全部或部分地准备、复制、出版和分发对其进行评论或以其他方式解释或协助其实施的衍生作品,不受任何限制,前提是上述版权声明和本段包含在所有此类副本和衍生作品中。但是,不得以任何方式修改本文档本身,例如通过删除版权声明或对 Internet 协会或其他 Internet 组织的引用,除非出于制定 Internet 标准的需要,在这种情况下,版权程序定义在必须遵循 Internet 标准流程,或按照要求将其翻译成英语以外的语言。


上述授予的有限权限是永久性的,不会被互联网协会或其继任者或受让人撤销。


本文档和其中包含的信息按“原样”提供,互联网协会和互联网工程工作队不提供所有明示或暗示的保证,包括但不限于任何保证,即使用此处的信息不会侵犯任何有关适销性或特定用途适用性的权利或任何默示保证。


致谢


RFC 编辑器功能的资金目前由互联网协会提供。

相关文章
|
6月前
|
存储 C语言 C++
c++日期和时间
c++日期和时间
41 0
|
6月前
|
C语言 C++
c++日期&时间
c++日期&时间
54 1
|
6月前
|
弹性计算 运维 Shell
解析日期和时间
【4月更文挑战第29天】
45 1
|
6月前
|
小程序
获取本月1号0时间 获取本周一的0点时间
获取本月1号0时间 获取本周一的0点时间
|
C语言 C++
C++ 如果设置日期 & 时间基础篇
C++ 如果设置日期 & 时间基础篇
|
存储 Java 程序员
实战:求年月日时间前后遇到的坑和解决方式
这周接到一个时间转换任务需要处理,本来没什么问题,后来完成后发现时间有偏差,又重写了一遍代码,感觉很有记录必要性,希望看过的小伙伴可以避坑。
实战:求年月日时间前后遇到的坑和解决方式
|
C语言 C++
C++ 如果设置日期 & 时间基础篇
C++ 标准库没有提供所谓的日期类型。C++ 继承了 C 语言用于日期和时间操作的结构和函数。为了使用日期和时间相关的函数和结构,需要在 C++ 程序中引用 <ctime> 头文件。
|
存储 Linux 编译器
C++ 日期和时间的相关函数
C++ 日期和时间的相关函数
293 0
|
Java
记录一次时间戳、夏令时、时区线上问题分析
记录一次时间戳、夏令时、时区线上问题分析
667 0
记录一次时间戳、夏令时、时区线上问题分析