For AI agents: the complete documentation index is available at /tc39-atlas/llms.txt, the full documentation bundle is available at /tc39-atlas/llms-full.txt, and this page is available as Markdown at /tc39-atlas/proposals/proposal-uniform-interchange-date-parsing.md.
  • 简体中文
  • uniform parsing of quasi-standard Date.parse input S1

    中文标题:准标准 Date.parse 输入的统一解析

    提案概览
    提案速览

    该提案将 Date.parse 的行为标准化,以支持更广泛的准标准输入,接受更多 RFC 3339 格式,同时要求拒绝包含无效元素组合或数字超出范围的输入。它通过正则表达式定义了当前交换格式的超集,并在回退到实现定义的行为之前指定标准化的接受/拒绝。

    Note

    以下 README 来自上游仓库,其中的阶段或状态标注可能滞后;当前信息以提案概览为准。

    准标准 Date.parse 输入的统一解析

    一项对 Date.parse 行为进行标准化的提案,以支持更广泛的输入范围,接受更多 RFC 3339 格式,并要求拒绝包含无效元素组合或数字超出界限的输入,同时仍允许对其他_输入_进行实现定义的回退。

    2019-03 幻灯片

    状态

    本提案处于 TC39 流程 的第一阶段。

    champions

    • Richard Gibson
    • Mathias Bynens

    动机

    ECMAScript 日期时间字符串格式 定义了一个 [ ±YY ] YYYY [ -MM [ -DD ] ] [ THH:mm [ :ss [ .sss ] ] [ Z ] ] "日期时间的字符串交换格式",该格式基于 ISO 8601 扩展格式的日历日期和时间表示,特别是 RFC 3339 描述。它本质上是一个 ISO 8601-2 profile,但存在一些例外,例如允许小时为"24"(ISO 8601 仅允许在时间_间隔_内使用)以及在组合的日期和时间表示中使用降低精度的日期表示(例如,省略日期中的日或月和日元素),如 "2018-07T10:23"。 Date.prototype.toISOString 返回符合该格式的字符串,而 Date.parse 则被要求接受符合格式的字符串,并正确地将它们解释为 ISO 8601。

    这表面上_似乎_支持了开发者的期望信心,即任何声称是该格式实例的字符串都将被任何正确的 Date.parse 实现以相同方式处理,但事实并非如此,因为该函数被允许对所有不完全符合的输入回退到特定于实现的行为——即使差异在于超出范围的字段值/组合等("格式字符串中的非法值(超出范围以及语法错误)意味着该格式字符串不是该格式的有效实例")。 因此,在对待这种"不太正确"的输入时,各种实现存在不应有的差异。可以在 https://jsbin.com/kuyubexitu 探索行为(近似于此仓库),但这里有一个摘要:

    • Chrome、Edge 和 Safari 接受数字位数错误的带符号年份(例如,"+2018-06-29" 和 "+0002018-06-29")。
    • Chrome、Edge 和 Safari 接受超过四位的无符号年份(例如,"123456-10-12")。
    • Chrome 和 Edge 将超出范围的日期解释为下个月的日子(例如,"2018-02-30" 表示下个月的一天)。
    • Chrome 接受小写的日期和时间指示符及/或时区偏移量(例如,"2018-06-29t15:00z")。
    • Edge 在小时为 24 时接受非零的分钟和/或秒(例如,"2018-06-28T24:01:01Z")。
    • Safari 接受秒值为 60 的情况(例如,"2015-06-30T23:59:60Z"),解释为一分钟的末尾。
    • Chrome 和 Edge 在没有时间部分的情况下接受 "Z" 偏移量。Edge 还接受其他字母(例如,"2018-06-29E"),解释为军事时区.
    • Edge 在没有时间部分的情况下接受 hh:mm 格式的时区偏移量(例如,"2018-06-29-04:00")。
    • Edge 接受仅小时制的时区偏移量,但仅当日期部分完整指定时(例如,"2018-06-29T11:00-04")。
    • Edge 和 Safari 接受超出范围的时区偏移量(例如,"2018-06-28T15:00-24:00")。
    • Chrome、Edge、Firefox 和 Safari 接受过少或过多的分数秒数字位数(例如,"2018-06-29T11:00:12.3456")。

    简而言之,开发者只有在对输入进行繁琐且容易出错的与交换格式匹配的验证之后,才能信任 Date.parse——这首先破坏了使用 Date.parse 的好处!

    提议的解决方案

    更新 Date.parse,使其针对一个格式检查输入,该格式涵盖当前的 日期时间字符串格式 及其微小变化——特别是那些有效的 ISO 8601 日期和时间表示,并在回退到实现定义的行为之前标准化对符合该格式的输入的处理。

    注意:标准处理包括接受_和_(在适当情况下)拒绝输入,如下所述。

    背景

    我相信交换格式完全由以下正则表达式描述(忽略空格,这些空格仅是为了提高可读性而添加的):

    /^
      (?<year> [0-9]{4} | [+-][0-9]{6} )
      (
        -(?<month> 0[1-9] | 1[0-2] )
        (
          -(?<day>
            0[1-9] | 1[0-9] | 2[0-8] |
            (?<! ( [13579] | [02468][^048] | [13579][^26] )(00)?-02- ) 29 |
            (?<! 02- ) 30 |
            (?<! (02|04|06|09|11)- ) 31
          )
        )?
      )?
      (
        T(?<hour> [01][0-9] | 2[0-4] ):(?<minute> [0-5][0-9] )
        (
          :(?<second> [0-5][0-9] )
          (?<fraction> \.[0-9]{3} )?
        )?
        (?<! T24(:00)?:[0-9]*[1-9] | T24:00:00.[0-9]*[1-9] )
        (?<offset> Z | [+-] (?! 24 )( [01][0-9] | 2[0-4] ):( [0-5][0-9] ) )?
      )?
    $/

    我能找到的每个当前实现都能正确解释符合此格式的输入(这在意料之中),以及一个包含少于或多于三位分数秒数字的超集(例如,"2018-07-05T20:57:01.42Z")。然而,对于不符合规范的边缘情况,其解释并不_统一_,这些情况主要是超出范围的字段值(例如,"2018-02-30"、"2018-06-28T24:01:01Z" 和 "2018-06-28T15:00-24:00")。

    变更

    指定 Date.parse 的行为,不仅针对符合交换格式的输入,还要针对其超集,该超集涵盖更多的 ISO 8601 格式,以及合法输入的无效邻居,这些邻居可能失败于例如边界检查、十进制本地化或大写规则(特别是指那些仅数字不同就能成为合法输入的):

    /^
      (?<yearish> [0-9]{4} | [+-][0-9]{4,} )
      (
        -(?<monthish> [0-9]{2} )
        (
          -(?<dayish> [0-9]{2} )
        )?
      )?
      (
        T(?<hourish> [0-9]{2} ):(?<minuteish> [0-9]{2} )
        (
          :(?<secondish> [0-9]{2} )
          (?<fraction> [.,][0-9]+ )?
        )?
      )?
      (?<offsetish> Z | [+-] [0-9]{2}:[0-9]{2} )?
    $/i

    仅对明显不旨在或基于标准交换格式的输入允许特定于实现的解析。

    有关详细信息,请参阅 草稿规范.

    其他可能的扩展(目前不建议)

    • 接受无符号的长年份(例如,"002018-07-03")。
      • 这类年份在 ISO 8601 中无效。
    • 接受分数分钟或小时(例如,"2018-07-03T18:20.5Z")。
      • 这类值在 ISO 8601 中有效,但属于高级功能。
    • 为非 Z 的"军事"字符时区偏移量指定统一处理(例如,"2018-07-03T14:20Q")。
      • 这类偏移量在 ISO 8601 中无效。
    • 为没有冒号的四位时区偏移量指定统一处理(例如,"2018-07-12T09:27-0400")。
      • 此类偏移量出现在 RFC 5322 中,并且在 ISO 8601 基本 格式表示中有效,但 ECMAScript 交换格式否则仅限于 扩展 格式的 profile。可以提出接受它们的理由——RFC 3339 指出"ISO 8601 对于基本和扩展格式的混合是否允许并不明确。"——但 ISO 8601:2004(E) 至少建议混合格式无效(第 4.3.3 节 要求降低精度的日期和时间表示 "要么完全使用基本格式…… 要么完全使用扩展格式")。
    • 为任何以四位数字开头并可选地带有符号的字符串指定统一处理。
      • 这将不允许实现接受 ISO 8601 序数日期(例如,"2018-186")或周日期(例如,"2018-W27-4"),更不用说 ISO 8601 基本格式或不符合 ISO 8601 的输入了。

    讨论

    向后兼容性

    这从设计上是一个实现之间存在较大差异的领域,但所提议的更改都不会要求任何当前所有实现都接受的输入开始被拒绝。 Firefox 已经实现了所有提议的拒绝,以及许多妥协。

    暴露字符串符合性

    ECMAScript 是否应该暴露一种测试字符串是否符合日期时间交换格式的方法? 类似 Date.isPortableString 的东西可能有助于在将输入_发送_到 Date.parse _之前_验证输入,因为该函数保留了接受不符合格式的字符串的能力。 最好不只是暴露一个布尔分类器,而是暴露实际的日期时间字段(类似于 RegExp.prototype.exec 相对于 RegExp.prototype.test 提供的增强功能),但在此时点上,要正确实现这样的接口似乎过于困难,尤其是在考虑时区偏移量时(这降低了仅仅返回一个 [year, month, …] 数组以便与例如 new Date 一起使用的可能性)。

    相关工作

    Morgan Phillips 的 proposal-date-time-string-format 有一个类似的目标,即标准化 Date.parse 在更广泛输入范围内的行为,但侧重于使更多输入可接受——特别包括不是有效 ISO 8601 表示的字符串,其中一些使用区域设置特定的特征,例如年、月、日字段的相对顺序。 相比之下,本提案的重点是当输入通过微小的错误(例如超出范围的值、不正确的指示符或分隔符以及字段组合不当)偏离交换格式时,_拒绝_这些输入。与 proposal-date-time-string-format 不同,特别重要的是,这里提议的每一项更改都与 ISO 8601 日历日期时间有关,因为这是 ECMAScript 交换格式的基础。 本质上,本提案旨在使解析 ISO 8601 日历日期时间的有限子集成为 Date.parse 的主要功能,将特定于实现的行为限制在其他日期时间格式上。

    @maggiepint@mj1856 和 @bterlson 的 temporal 旨在引入用于处理日期和时间的新类型,使其规模显著大于本提案。然而,无论其进展如何,Date 仍将是 ECMAScript 的一部分,并应尽可能改进。从解析的 ISO 8601 字符串中根据时间字段的存在与否推断本地或零 UTC 偏移量不能更改,但可以像这里描述的那样使解析本身更具可预测性。