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-intl-DateTimeFormat-formatRange.md.
  • 简体中文
  • Intl.DateFormat.prototype.formatRange S4

    中文标题:Intl.DateTimeFormat.prototype.formatRange

    提案概览
    提案速览

    该提案向 Intl.DateTimeFormat 添加 formatRange()formatRangeToParts() 方法,以简洁且符合区域设置的方式格式化日期范围,解决冗余字段和不正确分隔符等问题。它基于 ICU 的 DateIntervalFormat 和 CLDR 规范。

    Note

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

    Intl.DateTimeFormat.prototype.formatRange

    概述

    动机

    网站经常显示日期区间或日期范围以展示事件的跨度,例如酒店预订、服务的计费周期或其他类似用途。为了实现这一点,网站通常使用本地化库(如 Google Closure)来格式化日期范围,或者可能简单地分别格式化两个日期。

    如果采用第二种方式,Web 开发者可能会遇到问题,例如重复显示两个日期之间的公共字段、日期顺序不符合区域设置,或使用了不正确的分隔符。

    例如:

    let date1 = new Date(Date.UTC(2007, 0, 10)); // "Jan 10, 2007"
    let date2 = new Date(Date.UTC(2007, 0, 20)); // "Jan 20, 2007"
    
    let fmt = new Intl.DateTimeFormat('en', {
        year: 'numeric',
        month: 'long',
        day: 'numeric'
    });
    
    // The second date's 'month' and 'year' calendar fields are redundant, only
    // 'day' provides new information.
    console.log(`${fmt.format(date1)}${fmt.format(date2)}`);
    // > 'January 10, 2007 – January 20, 2007'

    以简洁且符合区域设置的方式格式化日期范围需要大量的原始或编译数据:对于所有可能的显示日历字段(例如 monthdayhour)、它们的长度(例如 2-digitnumeric)以及范围的两个日期之间最大的不同日历字段,都需要本地化模式。

    例如,使用相同的选项({year: 'numeric', month: 'short', day: 'numeric'})格式化以下范围需要两种不同的模式:

    2017年1月10日2017年1月20日 的日期范围:

    • 'Jan 10 – 20, 2007'
    • 最大的不同日历字段:'day'

    2017年1月10日2017年2月20日 的日期范围:

    • 'Jan 10 – Feb 20, 2007'
    • 最大的不同日历字段:'month'

    将此功能引入平台将提高 Web 的性能和开发者的生产力,因为他们将能够在没有额外区域数据大小和开销的情况下正确、灵活且简洁地格式化日期范围。

    状态

    第 4 阶段

    Polyfill: @formatjs/intl-datetimeformat

    提案

    Intl.DateTimeFormat 添加 formatRange(date1, date2)formatRangeToParts(date1, date2),以实现日期范围格式化。

    该提案基于 ICU 日期区间格式化器和 Unicode CLDR TR-35 规范中的日期区间部分。

    API

    Intl.DateTimeFormat.prototype.formatRange(date1, date2)

    此方法接收两个 Date 对象,并根据实例化 Intl.DateTimeFormat 时提供的 localeoptions 以最简洁的方式格式化日期范围。

    Intl.DateTimeFormat.prototype.formatRangeToParts(date1, date2)

    此方法接收两个 Date 对象,并返回一个对象数组,包含表示格式化日期范围各个部分的区域设置特定标记。

    参见: Intl.DateTimeFormat.prototype.formatToParts

    示例用法

    Intl.DateTimeFormat.prototype.formatRange(date1, date2)
    let date1 = new Date(Date.UTC(2007, 0, 10, 10, 0, 0));
    let date2 = new Date(Date.UTC(2007, 0, 10, 11, 0, 0));
    let date3 = new Date(Date.UTC(2007, 0, 20, 10, 0, 0));
    // > 'Wed, 10 Jan 2007 10:00:00 GMT'
    // > 'Wed, 10 Jan 2007 11:00:00 GMT'
    // > 'Sat, 20 Jan 2007 10:00:00 GMT'
    
    let fmt1 = new Intl.DateTimeFormat("en", {
        year: '2-digit',
        month: 'numeric',
        day: 'numeric',
        hour: 'numeric',
        minute: 'numeric'
    });
    console.log(fmt1.format(date1));
    console.log(fmt1.formatRange(date1, date2));
    console.log(fmt1.formatRange(date1, date3));
    // > '1/10/07, 10:00 AM'
    // > '1/10/07, 10:00 – 11:00 AM'
    // > '1/10/07, 10:00 AM – 1/20/07, 10:00 AM'
    
    let fmt2 = new Intl.DateTimeFormat("en", {
        year: 'numeric',
        month: 'short',
        day: 'numeric'
    });
    console.log(fmt2.format(date1));
    console.log(fmt2.formatRange(date1, date2));
    console.log(fmt2.formatRange(date1, date3));
    // > 'Jan 10, 2007'
    // > 'Jan 10, 2007'
    // > 'Jan 10 – 20, 2007'
    Intl.DateTimeFormat.prototype.formatRangeToParts(date1, date2)
    let date1 = new Date(Date.UTC(2007, 0, 10, 10, 0, 0));
    let date2 = new Date(Date.UTC(2007, 0, 10, 11, 0, 0));
    // > 'Wed, 10 Jan 2007 10:00:00 GMT'
    // > 'Wed, 10 Jan 2007 11:00:00 GMT'
    
    let fmt = new Intl.DateTimeFormat("en", {
        hour: 'numeric',
        minute: 'numeric'
    });
    
    console.log(fmt.formatRange(date1, date2));
    // > '10:00 – 11:00 AM'
    
    fmt.formatRangeToParts(date1, date2);
    // return value:
    // [
    //   { type: 'hour',      value: '10',  source: "startRange" },
    //   { type: 'literal',   value: ':',   source: "startRange" },
    //   { type: 'minute',    value: '00',  source: "startRange" },
    //   { type: 'literal',   value: ' – ', source: "shared"     },
    //   { type: 'hour',      value: '11',  source: "endRange"   },
    //   { type: 'literal',   value: ':',   source: "endRange"   },
    //   { type: 'minute',    value: '00',  source: "endRange"   },
    //   { type: 'literal',   value: ' ',   source: "shared"     },
    //   { type: 'dayPeriod', value: 'AM',  source: "shared"     }
    // ]

    其他方案

    Intl.DateIntervalFormat

    除了向 Intl.DateTimeFormat 添加 .formatRange().formatRangeToParts() 之外,还可以添加一个单独的日期区间格式化器。API 如下:

    • new Intl.DateIntervalFormat(locale, options)optionsIntl.DateTimeFormat 当前使用的选项相同。
    • Intl.DateIntervalFormat.prototype.format(date1, date2)
    • Intl.DateIntervalFormat.prototype.formatToParts(date1, date2)

    优点:

    • 与其他国际化库(如 ICU)一致,它们提供了单独的日期区间格式化器。

    缺点:

    • 可以通过简单地提供 .formatRange() 方法将范围支持添加到其他格式化器(例如 Intl.NumberFormat),避免为每个常规格式化器创建额外的范围格式化器。
    • 用于实例化 Intl.DateIntervalFormatoptions 与用于Intl.DateTimeFormat的选项相同。任何添加到 Intl.DateTimeFormat 的选项都需要在 Intl.DateIntervalFormat 中复制。

    先例