Moment.jsはDay.jsの夢を見るか？

[Moment.jsのサポート終了](momentjs.com/docs/#/-project-status)に伴って、Day.jsへ移行する際に注意したいことまとめです。



# 基本的には機械的な移行が可能
[公式でも迅速な移行をする場合に推奨されている](https://momentjs.com/docs/#/-project-status/recommendations/)通り、dayjsはmomentjsとの互換性が非常に高いため、単純に `moment` -> `dayjs` の一括置換でも大体問題ありません。

```js
import moment from 'moment';

moment().format('YYYY-MM-DD'); // 2020-10-08

// ↓

import dayjs from 'dayjs';

dayjs().format('YYYY-MM-DD'); // 2020-10-08
```

# localeの設定
localeを設定する場合には、先に対応するlocaleのimportが必要になります。

```js
import 'dayjs/locale/ja';

dayjs.locale('ja');
```

# timezoneの設定
こちらも先にimportが必要です。

```js
import utc from 'dayjs/plugin/utc';
import timezone from 'dayjs/plugin/timezone'; // needs utc plugin

dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.tz.setDefault('Asia/Tokyo');
```

# パースの挙動に陰りあり
こちら少しdayjsの挙動にハマってしまいました。
端的に言えば、生のdayjsでは文字列をコンストラクタの引数に渡した時に**限りなく甘く解釈**されてしまいます。
挙動を見てみましょう。

### 通常

```js
const date = '2020-01-01';
console.log(new Date(date));
console.log(dayjs(date, "YYYY-MM-DD hh:mm:ss Z", true).format());
console.log(moment(date, "YYYY-MM-DD hh:mm:ss Z", true).format());

// Wed Jan 01 2020 09:00:00 GMT+0900 (日本標準時)
// 2020-01-01T00:00:00+09:00 
// 2020-01-01T00:00:00+09:00 
```
当然ですね。

### nullを渡してみる

```js
const date = null;
console.log(new Date(date));
console.log(dayjs(date, "YYYY-MM-DD hh:mm:ss Z", true).format());
console.log(moment(date, "YYYY-MM-DD hh:mm:ss Z", true).format());

// Thu Jan 01 1970 09:00:00 GMT+0900 (日本標準時)
// Invalid Date 
// Invalid date 
```
JavaScriptのDateオブジェクトにnullを渡す際の謎挙動は、JavaScriptのDateオブジェクトの罠として一定の認知があると思います。
https://qiita.com/digitter/items/02666885a229c29707d4

ただしこれはmomentjs, dayjsともにparseする際にinvalidになるようにしているため、気にしなくてよし。

(dayjsでは[このissue](https://github.com/iamkun/dayjs/issues/70)とかで解決されている)

ちなみに **0を渡す**と、

```js
// Thu Jan 01 1970 09:00:00 GMT+0900 (日本標準時)
// 1970-01-01T09:00:00+09:00 
// 1970-01-01T09:00:00+09:00 
```

### '2020-hoge-fuga'とか渡してみる
```js
const date = "2020-hoge-fuga";
console.log(new Date(date));
console.log(dayjs(date, "YYYY-MM-DD hh:mm:ss Z", true).format());
console.log(moment(date, "YYYY-MM-DD hh:mm:ss Z", true).format());

// Invalid Date 
// Invalid date
// Invalid Date (error!) 
```
うんうん、そうですよね。

### '2020-01-hoge'とか渡してみる
```js
const date = "2020-01-fuga";
console.log(new Date(date));
console.log(dayjs(date, "YYYY-MM-DD hh:mm:ss Z", true).format());
console.log(moment(date, "YYYY-MM-DD hh:mm:ss Z", true).format());

// Invalid Date 
// 2020-01-01T00:00:00+09:00
// Invalid Date (error!)
```
> **// 2020-01-01T00:00:00+09:00**

............！！！！！
なんということでしょう、2020-01-01にパースされてしまいました。

dayjs単体の挙動だとこうなってしまうらしいので、dayjsの[CustomParseFormat](https://day.js.org/docs/en/plugin/custom-parse-format)プラグインを使うと良いらしい。

```js
import customParseFormat from "dayjs/plugin/customParseFormat";
dayjs.extend(customParseFormat);

const date = "2020-01-fuga";
console.log(new Date(date));
console.log(dayjs(date, "YYYY-MM-DD hh:mm:ss Z", true).format());
console.log(moment(date, "YYYY-MM-DD hh:mm:ss Z", true).format());

// Invalid Date 
// Invalid Date (error!)
// Invalid Date (error!)
```

# instanceof チェック
```js
if (v.constructor.name !== 'Moment') {
  v = moment(v);
}
```
moment.jsでこんな感じの型チェックをしている場合は、以下に置き換え可能です。

```js
if (!dayjs.isDayjs(v)) {
 v = dayjs(v);
}

// or

if (!(dayjs() instanceof dayjs)) {
 v = dayjs(v);
}
```

# Moment.js依存のライブラリ
[react-datetime](https://www.npmjs.com/package/react-datetime#usage)など、momentjsに依存しているパッケージを導入している際には注意が必要です。
react-datetimeの例だと`props`の`value`にmoment objectを渡していた箇所があったのですが、そこにdayjs objectを渡してしまっていました。ただこれでもエラーは発生しないものの(なんだって！)、出力結果が正しくありません(出力結果がすべて今日の日付になる)。
この例ではdayjs objectではなく `dayjs().format()`などで日付形式の文字列を渡すことで正しく挙動します。

他にもmomentに依存するライブラリに、機械的に置換したためにdayjs objを渡してしまったままの箇所がないか、よくご確認ください。

---

ありがとう。サラバmoment.js。