JavaScript新日期时间API Temporal简介 - 2ality


JavaScript 的当前日期时间 API 是出了名的难以使用ECMAScript 提案“Temporal”是一个新的更好的日期时间 API,目前处于第 3 阶段。
这篇博文有两个目标:

  • 让您了解 Temporal 的工作原理
  • 帮助您开始使用

Temporal 日期时间 API 可通过全局变量访问:
  • 所有对象都是不可变的。改变它们会产生新的值,类似于字符串在 JavaScript 中的工作方式。
  • 支持时区和非公历。
  • 时间值有几个专门的类(带时区的日期时间值、不带时区的日期时间值、不带时区的日期值等)。这有几个好处:
    • 值的上下文(时区与否等)更容易理解。
    • 如何完成给定的任务通常更为明显。
    • .toString() 可以用更少的考虑。
  • January是第 1 个月。

 
时区
Temporal 的时区基于IANA 时区数据库(简称:tz 数据库)。IANA 代表互联网号码分配机构。在该数据库中,每个时区都有一个标识符和定义 UTC 时间偏移的规则。如果时区具有标准时间和夏令时,则偏移量在一年中会发生变化:
const standardTime = Temporal.ZonedDateTime.from({
  timeZone: 'Europe/Zurich',
  year: 1995,
  month: 11,
  day: 30,
  hour: 3,
  minute: 24,
});
assert.equal(
  standardTime.toString(),
  '1995-11-30T03:24:00+01:00[Europe/Zurich]'); // (A)

const daylightSavingTime = Temporal.ZonedDateTime.from({
  timeZone: 'Europe/Zurich',
  year: 1995,
  month: 5,
  day: 30,
  hour: 3,
  minute: 24,
});
assert.equal(
  daylightSavingTime.toString(),
  '1995-05-30T03:24:00+02:00[Europe/Zurich]');
// (B)

在标准时间中,Europe/Zurich时区的时间偏移为+1:00(A 线)。在夏令时,时间偏移为 +2:00(B 行)。
 
日历
Temporal 支持的日历基于标准的Unicode Unicode Common Locale Data Repository (CLDR) ——其中包括

  • buddhist: 泰国佛历
  • chinese: 中国传统日历
  • coptic:科普特日历
  • dangi: 韩国传统日历
  • ethiopic:埃塞俄比亚历法,Amote Mihret(约公元 8 年)
  • gregory: 公历
  • hebrew: 传统的希伯来历
  • indian: 印度历
  • islamic: 伊斯兰历
  • iso8601:ISO 日历(使用 ISO 8601 日历周规则的公历)
  • japanese: 日本皇历
  • persian: 波斯历
  • roc: 民国历

iso8601是多数西方国家都使用的,并通过诸如Temporal.now.zonedDateTimeISO()(返回系统时区和 ISO-8601 日历中的当前日期和挂钟时间)等方法在 Temporal 中获得额外支持。
 
ECMAScript 扩展 ISO-8601/RFC 3339 字符串
标准 ISO-8601 和 RFC 3339 指定了如何在字符串中表示日期。目前,Temporal增加这些功能:
  • 将月日数据表示为字符串
  • 在日期时间字符串中表示 IANA 时区名称
  • 在日期时间字符串中表示日历系统

目标是最终使这些添加标准化(超越 ECMAScript)。
 
> Temporal.PlainMonthDay.from('12-24').toString()
'12-24'

以下代码显示了完整的日期时间字符串的样子。在实践中,其中许多部分经常会丢失:

const zdt = Temporal.ZonedDateTime.from({
  timeZone: 'Africa/Nairobi',
  year: 2019,
  month: 11,
  day: 30,
  hour: 8,
  minute: 55,
  second: 0,
  millisecond: 123,
  microsecond: 456,
  nanosecond: 789,
});

assert.equal(
  zdt.toString({calendarName: 'always', smallestUnit: 'nanosecond'}),
  '2019-11-30T08:55:00.123456789+03:00[Africa/Nairobi][u-ca=iso8601]');

上例中日期时间字符串的部分内容:

  • 日期: '2019-11-30'
    • 年'-'月'-'日
  • 日期和时间之间的分隔符: 'T'
  • 时间: '08:55:00.123456789'
    • 小时':'分':'秒
    • '.' (秒和几分之一秒之间的分隔符)
    • 毫秒(3 位数)
    • 微秒(3 位)
    • 纳秒(3 位数字)
  • 相对于 UTC 的时间偏移: '+03:00'
    • 替代:'Z'这意味着'+0:00'
  • 时区: '[Africa/Nairobi]'
  • 日历: '[u-ca=iso8601]'

 
Temporal API 概述
本节概述了 Temporal API 的类。它们都可以通过全局变量访问Temporal(Temporal.Instant,Temporal.ZonedDateTime,等)。
  • 挂钟时间与精确时间  

时间区分两种时间。给定全局时间:
  • 挂钟时间(也称为本地时间或时钟时间)在全球范围内变化,具体取决于时钟的时区。
  • 精确时间(也称为UTC 时间)在任何地方都是相同的。

纪元时间是表示精确时间的一种方式:它是在Unix 纪元(1970 年 1 月 1 日午夜 UTC )之前或之后计算时间单位(例如纳秒)的数字。
Temporal 中的所有日期和/或时间类都支持两种直接创建方式。
一方面,构造函数接受完全指定日期时间值所需的最少数据量。例如,在精确时间Instant和两个类的情况下ZonedDateTime,时间本身是通过纪元纳秒指定的。
const epochNanoseconds = 6046761644163000000n;
const timeZone = 'America/Los_Angeles'; // San Francisco
const zdt1 = new Temporal.ZonedDateTime(epochNanoseconds, timeZone);
assert.equal(
  zdt1.toString(),
  '2161-08-12T09:00:44.163-07:00[America/Los_Angeles]');

静态工厂方法.from()被重载。大多数类支持其参数的三种值。
首先,如果参数是同一类的实例,则克隆该实例:

const zdt2 = Temporal.ZonedDateTime.from(zdt1);
assert.equal(
  zdt2.toString(),
  '2161-08-12T09:00:44.163-07:00[America/Los_Angeles]');

其次,所有其他对象都被解释为指定具有时间相关信息的各种字段:

const zdt3 = Temporal.ZonedDateTime.from({
  timeZone: 'America/Los_Angeles',
  year: 2161,
  month: 8,
  day: 12,
  hour: 9,
  minute: 0,
  second: 44,
  millisecond: 163,
  microsecond: 0,
  nanosecond: 0,
});
assert.equal(
  zdt3.toString(),
  '2161-08-12T09:00:44.163-07:00[America/Los_Angeles]');

第三,所有原始值都被强制转换为字符串并解析:

const zdt4 = Temporal.ZonedDateTime.from(
  '2161-08-12T09:00:44.163[America/Los_Angeles]'); // (A)
assert.equal(
  zdt4.toString(),
  '2161-08-12T09:00:44.163-07:00[America/Los_Angeles]');
// (B)

请注意,我们不需要在 A 行中指定偏移量,而是在 B 行中。

更多点击标题,黑客新闻讨论