Moment.js の代わりとしての date-fns とは

https://qiita.com/geeknees/items/42728ea62b4a099af462 こちらの記事で Moment.js と 代替としての Day.js または標準ライブラリの Date オブジェクトの比較を行ったのですが、date-fns というのを教えてもらったので、こちらについて今回比較したいと思います。

### date-fns とは

READMEによると…

> date-fns provides the most comprehensive, yet simple and consistent toolset for manipulating JavaScript dates in a browser & Node.js.

> date-fns is like lodash for dates. It has 180+ functions for all occasions.

date-fnsは、ブラウザやNode.jsで最も包括的かつ、シンプルで一貫性のある、JavaScriptの日付を操作するためのツールセットを提供します。

date-fnsは日付を操作するためのlodashのようなものです。あらゆる場面で使える180以上の機能を持っています。

要点としては

- Moment.jsと同じAPIを持っているわけではない
- 大きな違いとしては独自のクラスを持つのではなく、日付関連の独自の処理をする関数をまとめて提供しています
    - どこでもいいのですが、例えば addMonths など見てみるとよくわかります
        - https://github.com/date-fns/date-fns/blob/master/src/addMonths/index.js#L27
    - こういった点が lodash と説明しているところです
- それによる効果として、immutable で破壊的な変更を行わないようになっています
    - ここらへんが標準仕様としての追加が検討されている Tempral も意識しているポイントです
        - https://qiita.com/geeknees/items/42728ea62b4a099af462#temporal-%E3%81%A8%E3%81%AF


#### 補足 lodash

説明不要かもしれませんが、 lodash は `_` から便利なメソッドを呼び出せるライブラリです。
https://lodash.com/

```javascript
_.add(6, 4);
// => 10
```

で、こちらの実装方針も独自の処理をする関数をまとめて提供しています。
https://github.com/lodash/lodash/blob/master/add.js#L16

`-` で呼び出せるのは `-` で require するからですね。

```javascript
// Load the full build.
var _ = require('lodash');
```

### date-fnsの使い方

まずサンプルコード

```javascript

import { format, formatDistance, formatRelative, subDays } from 'date-fns'

format(new Date(), "'Today is a' iiii")
//=> "Today is a Sunday"

formatDistance(subDays(new Date(), 3), new Date())
//=> "3 days ago"

formatRelative(subDays(new Date(), 3), new Date())
//=> "last Friday at 7:26 p.m."
```

 format, formatDistance, formatRelative, subDays などの関数を import してそのまま使うだけですね。

- 加算
    - https://date-fns.org/v2.15.0/docs/add
- ソート
    - https://date-fns.org/v2.15.0/docs/compareAsc
- フォーマット
    - https://date-fns.org/v2.15.0/docs/format
- 比較、判定
    - https://date-fns.org/v2.15.0/docs/isBefore
- パーサー
    - https://date-fns.org/v2.15.0/docs/parse

基本的な処理（かなり便利に）はできそうですね。

getterの便利なヘルパーも多く実装されています。
- https://date-fns.org/v2.15.0/docs/getSeconds
- https://date-fns.org/v2.15.0/docs/isFriday

### date-fns with Time Zones

最後に Time Zones を見てみましょう。ドキュメントになぜ Time Zone の扱いにおいて難しい部分をわかりやすく説明してくれています。

> The difficulty comes when working with another time zone's local time, other than the current system's, like showing the local time of an event in LA at 8pm PST on a Node server in Europe or a user's machine set to EST.

難しいのは、現在のシステム以外のタイムゾーンのローカルタイムを扱う場合です。例えば、ヨーロッパのノードサーバやユーザのマシンがESTに設定されている場合に、ロスのイベントのローカルタイムを20時PSTで表示するような場合です。

サンプルコードは下記のとおりです。わかりやすいですね。

```javascript

const { zonedTimeToUtc, utcToZonedTime, format } = require('date-fns-timezone')

// Set the date to "2018-09-01T16:01:36.386Z"
const utcDate = zonedTimeToUtc('2018-09-01 18:01:36.386', 'Europe/Berlin')

// Obtain a Date instance that will render the equivalent Berlin time for the UTC date 
const date = new Date('2018-09-01Z16:01:36.386Z')
const timeZone = 'Europe/Berlin'
const zonedDate = utcToZonedTime(date, timeZone)
// zonedDate could be used to initialize a date picker or display the formatted local date/time

// Set the output to "1.9.2018 18:01:36.386 GMT+02:00 (CEST)"
const pattern = 'D.M.YYYY HH:mm:ss.SSS [GMT]Z (z)'
const output = format(zonedDate, pattern, { timeZone })
```

date-fns-timezone は別パッケージとして管理されています。
https://github.com/prantlf/date-fns-timezone

date-fns-tz も互換があります。ドキュメントのサンプルコードは date-fns-timezone を使っていますが、 date-fns-tz のほうが直近のコミットもあり、date-fns v2.0.0 の対応を明記されているので良いかもしれません。 
https://github.com/marnusw/date-fns-tz

その上で実際のタイムゾーンの情報は timezone-support が活用されています。
https://github.com/prantlf/timezone-support

実際のデータは下記らへんですね。
https://github.com/prantlf/timezone-support/blob/master/util/data/meta.json
https://github.com/prantlf/timezone-support/blob/master/util/data/unpacked.json

#### date-fns を使うかどうかの個人的な見解

- date-fns は薄いし、タイムゾーンの対応も拡張できるとても便利なライブラリだと思います
- モダンなフロントエンド開発においては、あえて Moment.js を使う理由は見当たりません
- 一方で、標準ライブラリでも対応できる範囲であれば、後からでも入れられるので無理して最初から入れなくても良いかもしれません
    - タイムゾーンを扱う場合や曜日の判定を多く実装するのであれば導入を検討していいでしょう
- ただし、 node や deno などサーバーサイドで用いる場合は、タイムスタンプのバリデーションなどを簡易に書けるので入れておくほうが良い気がします

まとめると……依存関係が複数のメンテナーに分散しているのは気になりますが、今っぽい感じ(npmっぽい)とも言えます。クライアントサイドで使う場合は、用法用量によるとおもうので判断が分かれる感じもしますが、node や deno などサーバーサイドで日付関連の処理を多く行うのであれば、かなり導入を検討しても良さそうです。