日時ライブラリのTemporalの色々なAPIをいじってみた

## はじめに

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

JavaScriptで日時ライブラリを使用する機会があり、Temporalが stage 3 だったのを思い出したので、少し触ってみようと思いました。

せっかくなので、何か作りながら触ってみようということで、簡易的なカレンダーを作ってみました。

https://codesandbox.io/embed/green-sky-olr0m4?fontsize=14&hidenavigation=1&theme=dark

## Temporalとは

JavaScript標準ライブラリへの追加を目指して開発中の日時ライブラリです。

https://github.com/tc39/proposal-temporal

Temporalについては日本語でもドキュメントを一部翻訳いただいていたり、Qiitaにすでにいい記事がいくつかあるので、詳しくはそちらをご覧ください。

https://tc39.es/proposal-temporal/docs/ja/index.html

- こちらは Stage 2 時点のものなのでAPIが変わっているものがありますが、Temporalとはなにか?というのがわかりやすいです。

https://qiita.com/uhyo/items/5f34f5d6f33aa091a104

- stage 3 時点での各APIについて説明されています

https://qiita.com/sangotaro/items/8ee4b0f40cbcf3f12784

## つくったもの

色々なAPIを使いたいと思い、カレンダーを作りました。 ([ソースコードはこちら](https://github.com/kyntk/q-temporal-sandbox))

https://codesandbox.io/embed/green-sky-olr0m4?fontsize=14&hidenavigation=1&theme=dark

| ![calendar.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/212aa5fb-9ea2-5aae-e9a7-fa06645f7fd8.png) |
| ---------------------------------------------------------------------------------------------------------------------------- |

## インストール

[js-temporal/temporal-polyfill](https://github.com/js-temporal/temporal-polyfill)のREADMEを参考にライブラリをインストールします。

## APIの詳細 (随時追加予定)

今回いじった部分をいくつかピックアップして使い方を説明していきます。

### 現在の日時

`Temporal.Now`で現在の日時を取得できます。

[Object の関係図](https://tc39.es/proposal-temporal/docs/ja/index.html#object-%E3%81%AE%E9%96%A2%E4%BF%82%E5%9B%B3)にあるのですが、Temporalには`PlainDateTime`や`PlainDate`, `PlainMonthDay`などのオブジェクトがあり、最初使い方に迷います。

どれを使えばいいかは以下の画像[^1]を参考にするとわかりやすいです。

[^1]: https://tc39.es/proposal-temporal/docs/ja/index.html より引用

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/143317/37825bfc-8586-f89c-b2b7-878ff3eefe14.png)

```ts
Temporal.Now.zonedDateTimeISO().toString() // 2022-03-03T22:59:43.755983755+09:00[Asia/Tokyo]
Temporal.Now.zonedDateTimeISO().toLocaleString() // 2022/3/3 22:59:43 JST

Temporal.Now.plainDateISO().toString() // 2022-03-03
Temporal.Now.plainDateISO().toLocaleString() // 2022/3/3
```

また以下のように各種プロパティを使うことで、`year`,`month`などの値を取得することができます。

<iframe src="https://docs.google.com/presentation/d/1xP3_UaXlS4-SilVpFu6UFOL8QQw0Dr_VsaR8mSSEATI/embed?slide=id.g541ca9457d_0_185&start=false&loop=false&delayms=3000" frameborder="0" width="500" height="569" allowfullscreen="true" mozallowfullscreen="true" webkitallowfullscreen="true"></iframe>

### 日付の計算

`PlainDateTime`や`PlainDate`などのオブジェクトには、`add`, `subtract`といったメソッドが用意されています。

```ts
const today = Temporal.Now.plainDateISO()
const tomorrow = today.add({ days: 1 })
const yesterday = today.subtract({ days: 1 })
```

また、比較は`equals`や`compare`を使います。
compareの戻り値は −1, 0, 1 なので、これで並び替えができます。

```ts
Temporal.PlainDate.compare(today, tomorrow)  // -1
Temporal.PlainDate.compare(today, today)     // 0
Temporal.PlainDate.compare(today, yesterday) // 1
```

今回は使わなかったのですが、期間を計算する`until`,`since`などもあります。

また、`with`を使うと、日付だけ固定するなどの日時を作ることができるのも便利そうです。

```ts
const today = Temporal.Now.plainDateISO()
const firstDayOfMonth = today.with({ day: 1 })
```

### フォーマットして表示

文字列として表示をしたいときに、`toJSON`, `toString`, `toLocaleString`などを使うことができます。

これらのメソッドのオプションで表示方法を指定することができます。

```ts
Temporal.Now.plainTimeISO().toString({
  smallestUnit: 'minute',
  roundingMode: 'floor',
})
// 23:25

Temporal.Now.plainDateISO().toLocaleString(undefined, {
  dateStyle: 'medium',
})
// 2022/03/03
```

また、他にTemporalのpolyfillには`Intl`に対する拡張も含まれており、これを使ってもformatができるようです。

```ts
const dateTimeFormat = new Intl.DateTimeFormat()
console.log(dateTimeFormat.format(Temporal.Now.plainDateTimeISO())) // 2022/3/4 9:38:22
console.log(dateTimeFormat.format(Temporal.Now.plainDateISO())) // 2022/3/4
console.log(dateTimeFormat.format(Temporal.Now.plainDateTimeISO().toPlainTime())) // 9:40:24
```

これを使うと、以下のようにフォーマットができます。

```ts
console.log(firstDayOfThisMonth.date))// Temporal.PlainDate <2022-03-01>

const monthDateFormater = new Intl.DateTimeFormat(undefined, {
  day: 'numeric',
  month: 'numeric',
})
console.log(monthDateFormater.format(firstDayOfThisMonth.date)) // 3/1
```

PlainYearMonthやPlainMonthDateのフォーマット方法については、こちらの記事で説明していますので、ご覧ください。

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

## その他

Cookbookに色々な事例付きで使い方が乗っているので、参考にしてみてください。

https://tc39.es/proposal-temporal/docs/cookbook.html

タイムゾーンについてはこちらで説明されています。

https://tc39.es/proposal-temporal/docs/ja/ambiguity.html

## まとめ

Temporalは機能が豊富すぎて、最初どう使えばいいのか分からなかったのですが、基本のオブジェクトについてはわかってきました。
もう少し触りながら、Temporalの概念や細かいAPIの仕様なども調べてみようと思います。

## 参考

- [tc39/proposal-temporal: Provides standard objects and functions for working with dates and times.](https://github.com/tc39/proposal-temporal)
- [js-temporal/temporal-polyfill: Polyfill for Temporal (under construction)](https://github.com/js-temporal/temporal-polyfill)
- [Temporal documentation](https://tc39.es/proposal-temporal/docs/ja/index.html)
- [Temporal walkthrough - Google スライド](https://docs.google.com/presentation/d/1xP3_UaXlS4-SilVpFu6UFOL8QQw0Dr_VsaR8mSSEATI/edit#slide=id.p)
- [JavaScriptの日時処理はこう変わる！　Temporal入門 - Qiita](https://qiita.com/uhyo/items/5f34f5d6f33aa091a104)
- [Temporal で JavaScript の次世代の日時処理に触れてみる - Qiita](https://qiita.com/sangotaro/items/8ee4b0f40cbcf3f12784)