Temporalで和暦にローカライズさせて表示する

## はじめに

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

TC39 proposalのTemporalをいじってみて、以下2つのような記事を書いたのですが、和暦の扱い方についてピックアップしてまとめます。

https://qiita.com/kyntk/items/6a5a30357c285dfd3387

https://qiita.com/kyntk/items/8daee17a82c6535332d9

## 暦について

Temporalでは和暦のようなグレゴリオ暦以外の暦も扱うことができます。こちらについては別記事で説明しています。

https://qiita.com/kyntk/items/8daee17a82c6535332d9

## 和暦への変換

例えば、2022年3月6日の和暦の年号を表示したいということもあるかと思います。

このときに、グレゴリオ暦と和暦の対応表を作って、何月何日から何月何日は和暦だといくつみたいなかなりめんどくさい実装をしたり、年が変わったり年号が変わって、令和対応をしたという方もいるのではないかと思います。

Temporalでは複数の暦を扱うことができ、これの変換も機能として備わっているので、楽に変換ができそうです。
(正確には[Intl.Locale.prototype.calendar](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar)にあるようにTemporal外でも暦を扱うことができます)

## やりかた

`Intl.DateTimeFormat`を使うと楽に和暦の表示ができます。
これはTemporalの機能ではないので、Dateでも使うことができますが、TemporalでPlainMonthDate等を使う場合はインスタンスにも暦の設定が必要になります。 (詳しくは[こちら](https://qiita.com/kyntk/items/8daee17a82c6535332d9))

```ts:Temporal
const date = Temporal.Now.plainDate()
console.log(
  new Intl.DateTimeFormat([], {
    calendar: 'japanese',
    year: 'numeric',
  }).format(date)
)

// -> 令和4年
```

```ts:Date
const date = new Date()
console.log(
  new Intl.DateTimeFormat([], {
    calendar: 'japanese',
    year: 'numeric',
  }).format(date)
)

// -> 令和4年
```

`DateTimeFormat`の第1引数は`locales`で、空配列を指定するとブラウザーのロケールを使用します。

特定の言語に固定したい場合は、明示してあげます。

```ts
const date = Temporal.Now.plainDate('japanese')
console.log(
  new Intl.DateTimeFormat('en', {
    calendar: 'japanese',
    year: 'numeric',
  }).format(date)
)

// -> 4 Reiwa
```

注意点としては、第1引数の`locale`と、第2引数のオプションの`calendar: 'japanese'`の意味は異なり、calendarは和暦を表しています。

詳しくはこちらで説明してあります。

https://qiita.com/kyntk/items/8daee17a82c6535332d9

## 和暦の計算

和暦の計算といっても、Temporalは内部的にはISO8601カレンダーを使用しているので、日付の計算をして、表示の部分で和暦に変換を行います。

```ts
const date = Temporal.Now.plainDate('japanese').subtract({ years: 3 }) // 3年前 (2019/3/6)
console.log(
  new Intl.DateTimeFormat([], {
    calendar: 'japanese',
    dateStyle: 'long',
  }).format(date)
)

// -> 平成31年3月6日
```

## おまけ （`Intl.DateTimeFormat`のオプションについて）

TypeScriptの`lib.es2020.intl.d.ts`の[型定義ファイル](https://github.com/microsoft/TypeScript/blob/v4.6.2/lib/lib.es2020.intl.d.ts#L240)に以下のように定義されています。
Temporalのpolyfillでは一部拡張されている部分はありますが、基本的にこのあたりを使用することができます。

```ts
interface DateTimeFormatOptions {
    calendar?: string | undefined;
    dayPeriod?: "narrow" | "short" | "long" | undefined;
    numberingSystem?: string | undefined;

    dateStyle?: "full" | "long" | "medium" | "short" | undefined;
    timeStyle?: "full" | "long" | "medium" | "short" | undefined;
    hourCycle?: "h11" | "h12" | "h23" | "h24" | undefined;
}
```

ただ、full, long, medium, shortなど、どういう結果になるのか分かりづらいので、それぞれ試してみます。

### full

```ts:full
const dateTime = Temporal.Now.plainDateTimeISO()
const locale = 'ja'

const option = {
  dateStyle: 'full',
  timeStyle: 'full',
} as const
new TemporalIntl.DateTimeFormat(locale, option).format(dateTime)

// 2022年3月6日日曜日 23時27分04秒 日本標準時
```

### long

```ts:long
const option = {
  dateStyle: 'long',
  timeStyle: 'long',
}
// 2022年3月6日 23:28:11 JST
```

### medium

```ts:medium
const option = {
  dateStyle: 'medium',
  timeStyle: 'medium',
}
// 2022/03/06 23:31:25
```

### short

```ts:short
const option = {
  dateStyle: 'short',
  timeStyle: 'short',
}
// 2022/03/06 23:32
```

### hourCycle

```ts:short
const option = {
  timeStyle: 'short',
  hourCycle: 'h11'
}
// 午後11:35
```

### dayPeriod

```ts:short
const option = {
  dayPeriod: 'long',
  hourCycle: 'h12'
}
// 夜中
```

### formatMatcher

その他、細かく指定することもできます。
今回はlocale: 'en'で実行してみます。

```ts
const dateTime = Temporal.Now.instant()
const locale = 'en'
const option = {
  era: 'short',
  year: 'numeric',
  month: 'short',
  day: '2-digit',
  timeZoneName: 'long',
  hour: 'numeric',
  minute: '2-digit',
  second: 'numeric',
  hourCycle: 'h12',
} as const

const formatter = new TemporalIntl.DateTimeFormat(locale, option)

console.log(formatter.format(dateTime))
// Mar 06, 2022 AD, 11:46:40 at night Japan Standard Time
```

このようにformatterを定義してあげることで柔軟に表示させることができます。

## 参考

- [Intl.Locale.prototype.calendar - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/Locale/calendar)
- [Intl.DateTimeFormat() コンストラクター - JavaScript | MDN](https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/Intl/DateTimeFormat/DateTimeFormat)
- [TypeScript/lib.es2020.intl.d.ts at v4.6.2 · microsoft/TypeScript](https://github.com/microsoft/TypeScript/blob/v4.6.2/lib/lib.es2020.intl.d.ts#L240)