LoginSignup
3
0

More than 1 year has passed since last update.

TemporalでのPlainYearMonthやPlainMonthDateのフォーマット方法について

Last updated at Posted at 2022-03-06

はじめに

Temporalは現状 TC39 proposal stage 3 のものを使用しています
今後APIが変わる可能性もあるのでご注意ください

Temporalのpolyfill1に実装されているIntlを拡張したモジュールを使ってPlainMonthDayのフォーマットをしようとした時に、以下のようなエラーが出てハマった部分があったので説明します。

intl.js:400 Uncaught RangeError: cannot format PlainMonthDay with calendar iso8601 in locale with calendar Gregory
    at extractOverrides (intl.js:400:1)
    at DateTimeFormatImpl.format (intl.js:143:1)
    at App (App.tsx:76:1)

Temporalとは

TC39 proposalのTemporalについてはこちらの記事で説明していますので、ご覧ください。

Intlについて

IntlはES20172で導入された、国際API 名前空間オブジェクトで、Temporalのpolyfillで実装されているIntlは、これを拡張してTemporalの日時を扱えるようにしているものです。

ECMAScriptのIntlには、Intl.ListFormat()Intl.NumberFormat()などがありますが、TemporalではIntl.DateTimeFormat()が拡張されています。

Intl.DateTimeFormat()の基本的な使い方について

基本的なIntl.DateTimeFormat()の使い方はこちらをご確認ください。

一部引用すると、以下のように使えます。

const date = new Date(Date.UTC(2020, 11, 20, 3, 23, 16, 738));
// Results below assume UTC timezone - your results may vary

console.log(new Intl.DateTimeFormat('en-US').format(date));
// expected output: "12/20/2020"

console.log(new Intl.DateTimeFormat('en-GB', { dateStyle: 'full', timeStyle: 'long' }).format(date));
// Expected output "Sunday, 20 December 2020 at 14:23:16 GMT+11"

PlainYearMonthやPlainMonthDayのフォーマットについて

Intl.DateTimeFormat().format()でフォーマットできるオブジェクトは以下のように型定義されています。

type Formattable =
  | Date
  | Temporal.Instant
  | Temporal.ZonedDateTime
  | Temporal.PlainDate
  | Temporal.PlainTime
  | Temporal.PlainDateTime
  | Temporal.PlainYearMonth
  | Temporal.PlainMonthDay;

そこで、タイトルにあるように、PlainMonthDayをformatしてみると、以下のようなエラーが発生します。

new Intl.DateTimeFormat().format(
  Temporal.Now.plainDateISO().toPlainMonthDay()
)

/*
intl.js:400 Uncaught RangeError: cannot format PlainMonthDay with calendar iso8601 in locale with calendar Gregory
    at extractOverrides (intl.js:400:1)
    at DateTimeFormatImpl.format (intl.js:143:1)
    at App (App.tsx:76:1)
*/

これがエラーになる原因については、こちらで説明されていますが、これはTemporalの機能のCalendarに関係してきます。

Temporal.Calendarについて

カレンダーという概念についてはこちらで説明されています。

日時を扱う際に、世界中で多く使われているグレゴリオ暦以外の暦を扱うことができ、例えばユダヤ暦、中国暦、また和暦もあります。
これらの暦の使い方として、ドキュメントにある例を引用すると「グレゴリオ暦のMarch 4, 2021をユダヤ暦では20 Adar 5781と表現する」などのような変換をするものがあります。

Temporalでは内部的にはISO8601カレンダーを使用していますが、表示の際に他のカレンダーを使用することができます。

あらためて、PlainMonthDayのフォーマットについて

このカレンダーについての考え方を元に、先程のissueで説明されている内容を確認してみます。

Dates can be converted between calendars.

The same is not true for Temporal.PlainMonthDay or Temporal.PlainYearMonth. The 5th month of the Chinese calendar year 2022 has no relationship to the 5th month of the Hebrew year 2022. Similarly, the 5th day or the 8th month of the Gregorian calendar is unrelated to the 5th day of the 8th month of the Chinese or Hebrew or Islamic calendar. There is no way to convert them.

このように、ある暦の2022年の5ヶ月目は別の暦の2022年の5ヶ月目と全く関係がないため、変換ができません。

For this reason, localized formatting of Temporal.PlainMonthDay or Temporal.PlainYearMonth requires that the calendar of the Temporal.PlainMonthDay or Temporal.PlainYearMonth instance must exactly match the calendar of the locale. Because the default calendar is iso8601 (a calendar that no locale uses), the caller must opt into using a specific calendar option when formatting these temporal types.

そのため、Temporal.PlainMonthDayTemporal.PlainYearMonthをローカライズフォーマットしたい時は、「インスタンスの暦」と「ロケールの暦」を一致させる必要があるようです。

// インスタンスの暦を和暦に設定
const yearMonth = Temporal.Now.plainDate('japanese').toPlainYearMonth()

// ロケールの暦を和暦に設定
console.log(yearMonth.toLocaleString('ja', { calendar: 'japanese' }))

// -> R4/03

また、Intl.DateTimeFormatを使った場合も同様です。

const yearMonth = Temporal.Now.plainDate('japanese').toPlainYearMonth()

console.log(
  new Intl.DateTimeFormat('ja', {
    calendar: 'japanese',
    year: 'numeric',
  }).format(yearMonth)
)
// 令和4年

まとめ

Temporal.PlainMonthDayTemporal.PlainYearMonthをローカライズフォーマットしたい時は、「インスタンスの暦」と「ロケールの暦」を一致させる必要がある

参考

  1. https://github.com/js-temporal/temporal-polyfill

  2. https://402.ecma-international.org/8.0/#intl-object

3
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
0