为什么Temporal.Now.zonedDateTimeISO() 在Intl.DateFormat中不起作用?

前端开发 2026-07-11

我在为一个关于Javascript中 i18n的演示做示例,具体是Vue 3.x,我想展示Intl.DateFormat可以同时与旧的Date对象和新的Temporal对象一起工作。

到目前为止,Temporal.Now.plainDateISO() 和Temporal.Now.plainTimeISO() 在 Intl.DateFormat 上工作正常,但当我尝试把Temporal.Now.zonedDateTimeISO() 与 Intl.DateFormat 一起使用时,会出现运行时错误;而 Temporal.Now.zonedDateTimeISO() 在不把它与 Intl.DateFormat 一起使用时工作正常。

下面的代码是我执行的:

<script setup>
import { ref, computed } from "vue";
import { useI18n } from "vue-i18n";
const { t, locale, availableLocales } = useI18n();

/* Lists */
const calendars = Intl.supportedValuesOf("calendar");
const collations = Intl.supportedValuesOf("collation");
const currencies = Intl.supportedValuesOf("currency");
const numberingSystems = Intl.supportedValuesOf("numberingSystem");
const timeZones = Intl.supportedValuesOf("timeZone");
const units = Intl.supportedValuesOf("unit");

/* Date/Time/Timezones (Date/Temporal) */
const dateFormatter1 = computed(
  () => new Intl.DateTimeFormat(locale.value, { weekday: "long", day: "numeric", year: "numeric", month: "long", era: "short" }),
);
const todayDate = computed(() => dateFormatter1.value.format(new Date()));
const dateFormatter2 = computed(() => new Intl.DateTimeFormat(locale.value, { timeStyle: "long" }));
const todayTime = computed(() => dateFormatter2.value.format(new Date()));
const dateFormatter3 = computed(() => new Intl.DateTimeFormat(locale.value, { dateStyle: "long", timeStyle: "long"}));
const todayTimestamp = computed(() => dateFormatter3.value.format(new Date()));

const temporalFormatter1 = computed(() => new Intl.DateTimeFormat(locale.value, { weekday: "long", day: "numeric", year: "numeric", month: "long", era: "short"}));
const todayTemporalDate = computed(() => temporalFormatter1.value.format(Temporal.Now.plainDateISO()));
const temporalFormatter2 = computed(() => new Intl.DateTimeFormat(locale.value, { timeStyle: "long"}));
const todayTemporalTime = computed(() => temporalFormatter2.value.format(Temporal.Now.plainTimeISO()));
const temporalFormatter3 = computed(() => new Intl.DateTimeFormat(locale.value, { dateStyle: "long", timeStyle: "long"}));
console.log(Temporal.Now.zonedDateTimeISO());
const todayTemporalTimestamp = computed(() => temporalFormatter3.value.format(Temporal.Now.zonedDateTimeISO()));

</script>

<template>
  <header>
    <nav>
      <div>
        <a href="#">{{ t("home") }}</a>
        <a href="#">{{ t("about") }}</a>
      </div>
      <span>
        <select v-model="locale" class="localeList">
          <option
            v-for="oneLocale in availableLocales"
            :key="oneLocale"
            :value="oneLocale"
          >
            {{ oneLocale }}
          </option>
        </select>
      </span>
    </nav>
  </header>

  <main>
    <h1>{{ t("greeting") }}</h1>
    <h2>Date/Time Formats using Date</h2>
    <p>{{ todayDate }}</p>
    <p>{{ todayTime }}</p>
    <p>{{ todayTimestamp }}</p>
    <h2>Date/Time Formats using Temporal</h2>
    <p>{{ todayTemporalDate }}</p>
    <p>{{ todayTemporalTime }}</p>
    <p>{{ todayTemporalTimestamp }}</p>
    <h2>Calendars</h2>
    <p>These are the available calendars:</p>
    <ul>
      <li v-for="oneCalendar in calendars" :key="oneCalendar">{{ oneCalendar }}</li>
    </ul>
    <h2>Collations</h2>
    <p>These are the available collations:</p>
    <ul>
      <li v-for="oneCollation in collations" :key="oneCollation">{{ oneCollation }}</li>
    </ul>
    <h2>Currencies</h2>
    <p>These are the available currencies:</p>
    <ul v-for="oneCurrency in currencies" :key="oneCurrency">
      <li>{{ oneCurrency }}</li>
    </ul>
    <h2>Numbering Systems</h2>
    <p>These are the available numbering systems:</p>
    <ul>
      <li v-for="oneNumberingSystem in numberingSystems" :key="oneNumberingSystem">{{ oneNumberingSystem }}</li>
    </ul>
    <h2>Timezones</h2>
    <p>These are the available timezones:</p>
    <ul>
      <li v-for="oneTimeZone in timeZones" :key="oneTimeZone">{{ oneTimeZone }}</li>
    </ul>
    <h2>Units</h2>
    <p>These are the available units:</p>
    <ul>
      <li v-for="oneUnit in units" :key="oneUnit">{{ oneUnit }}</li>
    </ul>

  </main>
</template>

<style scoped>
header {
  padding: 1rem;
  background: #333;
  width: 100%;
}
h2 {
  margin-top: 1rem;
}
nav {
  display: flex;
  justify-content: space-between;
}
.localeList {
  font-size: 1.5rem;
}
a {
  text-decoration: none;
  color: #fff;
  font-size: 1.5rem;
  padding: 0 1rem;
  margin: 0 1rem;
}
main {
  padding: 2rem;
}
</style>

这是控制台输出:

ZonedDateTime {timeZoneId: 'America/Toronto', calendarId: 'iso8601', era: undefined, eraYear: undefined, year: 2026, …}calendarId: "iso8601"day: 19dayOfWeek: 4dayOfYear: 78daysInMonth: 31daysInWeek: 7daysInYear: 365epochMilliseconds: 1773963013444epochNanoseconds: 1773963013444030029nhour: 19hoursInDay: 24microsecond: 30millisecond: 444minute: 30month: 3monthCode: "M03"monthsInYear: 12nanosecond: 29offset: "-04:00"offsetNanoseconds: -14400000000000second: 13timeZoneId: "America/Toronto"weekOfYear: 12year: 2026yearOfWeek: 2026[[Prototype]]: Temporal.ZonedDateTime
main.js:22 [Vue warn]: Unhandled error during execution of render function 
  at <App>
warn$1 @ chunk-6PFJYSIW.js?v=cc6eb311:2386
logError @ chunk-6PFJYSIW.js?v=cc6eb311:2597
handleError @ chunk-6PFJYSIW.js?v=cc6eb311:2589
renderComponentRoot @ chunk-6PFJYSIW.js?v=cc6eb311:6930
componentUpdateFn @ chunk-6PFJYSIW.js?v=cc6eb311:8451
run @ chunk-6PFJYSIW.js?v=cc6eb311:689
setupRenderEffect @ chunk-6PFJYSIW.js?v=cc6eb311:8586
mountComponent @ chunk-6PFJYSIW.js?v=cc6eb311:8358
processComponent @ chunk-6PFJYSIW.js?v=cc6eb311:8310
patch @ chunk-6PFJYSIW.js?v=cc6eb311:7814
render2 @ chunk-6PFJYSIW.js?v=cc6eb311:9125
mount @ chunk-6PFJYSIW.js?v=cc6eb311:6579
app.mount @ chunk-6PFJYSIW.js?v=cc6eb311:12813
(anonymous) @ main.js:22
App.vue:30 Uncaught TypeError: Invalid argument for Temporal [object Temporal.ZonedDateTime]
    at ComputedRefImpl.fn (App.vue:30:72)
    at refreshComputed (chunk-6PFJYSIW.js?v=cc6eb311:843:29)
    at get value (chunk-6PFJYSIW.js?v=cc6eb311:2098:5)
    at unref (chunk-6PFJYSIW.js?v=cc6eb311:1953:30)
    at Object.get (chunk-6PFJYSIW.js?v=cc6eb311:1959:64)
    at Proxy._sfc_render (App.vue:64:47)
    at renderComponentRoot (chunk-6PFJYSIW.js?v=cc6eb311:6894:17)
    at ReactiveEffect.componentUpdateFn [as fn] (chunk-6PFJYSIW.js?v=cc6eb311:8451:46)
    at ReactiveEffect.run (chunk-6PFJYSIW.js?v=cc6eb311:689:19)
    at setupRenderEffect (chunk-6PFJYSIW.js?v=cc6eb311:8586:5)
(anonymous) @ App.vue:30
refreshComputed @ chunk-6PFJYSIW.js?v=cc6eb311:843
get value @ chunk-6PFJYSIW.js?v=cc6eb311:2098
unref @ chunk-6PFJYSIW.js?v=cc6eb311:1953
get @ chunk-6PFJYSIW.js?v=cc6eb311:1959
_sfc_render @ App.vue:64
renderComponentRoot @ chunk-6PFJYSIW.js?v=cc6eb311:6894
componentUpdateFn @ chunk-6PFJYSIW.js?v=cc6eb311:8451
run @ chunk-6PFJYSIW.js?v=cc6eb311:689
setupRenderEffect @ chunk-6PFJYSIW.js?v=cc6eb311:8586
mountComponent @ chunk-6PFJYSIW.js?v=cc6eb311:8358
processComponent @ chunk-6PFJYSIW.js?v=cc6eb311:8310
patch @ chunk-6PFJYSIW.js?v=cc6eb311:7814
render2 @ chunk-6PFJYSIW.js?v=cc6eb311:9125
mount @ chunk-6PFJYSIW.js?v=cc6eb311:6579
app.mount @ chunk-6PFJYSIW.js?v=cc6eb311:12813
(anonymous) @ main.js:22

如果把polyfill包含进来,错误仍然会出现,仿佛没有使用它一样。我的浏览器是Brave:

Brave已是最新版

Brave 1.88.134 (Official Build) (64-bit)

Chromium: 146.0.7680.153

这是浏览器尚未完全实现Temporal对象的情况,还是还有其他问题?

解决方案

为了避免两个对象的时区冲突,不能用 Intl.DateTimeFormat 来格式化一个 Temporal.ZonedDateTime。你应该执行下列任一操作:

  • 如果你只是想在外部进行简单格式化,且不打算格式化大量时间戳,请对那个 Temporal.ZonedDateTime 对象调用 toLocaleString。这个方法接受与 Intl.DateTimeFormat 构造函数相同的参数,尽管因为每次都需要创建一个新的格式化器,所以速度会慢一些。
  • 如果你需要不同的格式(例如 formatToParts)或要格式化大量时间戳,考虑使用 Intl.DateTimeFormat,像这样:

js zdt = Temporal.ZonedDateTime.from('2026-03-19T00:00[America/Los_Angeles]'); new Intl.DateTimeFormat({ timeZone: zdt.timeZoneId }).format(zdt.toInstant());

Temporal.ZonedDateTimeIntl.DateTimeFormat 一起格式化时的难点在于它们都可能携带时区。

zdt = Temporal.ZonedDateTime.from('2026-03-19T00:00[America/Los_Angeles]');
new Intl.DateTimeFormat({ timeZone: 'Asia/Tokyo' }).format(zdt);

因此你需要决定如果它们冲突时应该怎样处理。我们(设计Temporal API的 TC39成员)就这个问题争论了很长时间,但未能就具体行为达成一致。

https://github.com/js-temporal/proposal-temporal-v2/issues/38 对迄今为止所做工作的总结相当全面。由于当前Temporal的解决方案是抛出异常,这为未来的TC39提案留出了空间,以提出一种不抛出异常的解决方案。

站内所有文章版权归属LeftHeroAI导航站,无授权禁止任何主体转载、抄袭、复制内容,亦不得私自架设镜像站点。一经侵权,本站将通过法律途径追责。

相关文章