この記事は「DeNAその2 Advent Calendar 2018」16日目の記事です。
背景
JavaScriptの日付時刻操作は罠が多く、コードのメンテナンスも行いにくいため、
GitHub Star数が4万に迫っていて、安定感のあるmomentをこれまで採用してきました。
参考: https://qiita.com/labocho/items/5fbaa0491b67221419b4
しかし、momentはファイルサイズが大きく、jsのバンドルサイズが肥大化する原因にもなっていました。
そんな時、Vue Fes Japan 2018の公演の中だったり、有識者の記事の中だったりで、momentではない他の日付時刻操作ライブラリを採用しているケースを知り、今回調べてみることにしました。
以下は検討するきっかけとなった資料です。
前提
調べるにあたって重視したのは以下の2点です。
- momentよりもファイルサイズが軽量のもの
- momentで行なっている日付時刻操作を代替できるもの
2について補足します。
私たちのサービスにおいて、momentが行なっていた処理は以下の3つです。
サンプルコードの通り、momentがtimestampを受け取って日付時刻を操作しています。
❶. 日付時刻をフォーマットする
import moment from 'moment'
const timestamp = 1544911200 * 1000 // 2018/12/16 07:00:00
moment(timestamp).format('YYYY/MM/DD HH:mm:ss')
> 2018/12/16 07:00:00
❷. 日付の加算と減算を計算する
import moment from 'moment'
const timestamp = 1544911200 * 1000 // 2018/12/16 07:00:00
moment(timestamp).add(3, 'days').format('YYYY/MM/DD')
> 2018/12/19
moment(timestamp).add(-3, 'days').format('YYYY/MM/DD')
> 2018/12/13
❸. 2つの日付の日数を計算する
import moment from 'moment'
const timestamp1 = 1544911200 * 1000 // 2018/12/16 07:00:00
const timestamp2 = 1549663200 * 1000 // 2019/02/09 07:00:00
moment(timestamp1).diff(timestamp2, 'days')
> -55
moment(timestamp2).diff(timestamp1, 'days')
> 55
検討対象とその情報について
検討対象にしたライブラリは、「moment」と「dayjs」「date-fns」「luxon」です。GitHub Starが多く、メンテナンスが継続的に行われているライブラリをピックアップしてみました。
luxonについては、momentと同じOrganizationで開発されており、興味もあって検討対象に入れてみました。
npmのダウンロード数から見る
npmのダウンロード数を比較できるnpmtrendsによると、momentはまだまだ圧倒的に支持されており、次いでdate-fnsが徐々にシェアを伸ばしている様子。
dayjsとluxon頑張れ!というような状態です。
画像元はこちらです。
https://www.npmtrends.com/date-fns-vs-dayjs-vs-luxon-vs-moment
GitHubやDocumentから見る
上述のnpmのダウンロード数では、dayjsよりもdate-fnsが上回っていましたが、GitHub Starはdayjsの方が上回っています。
dayjsは今年の4月に生まれたばかりなので、これからさらに伸びていくことに期待です。
Documentについては、どのライブラリもよくまとめられていました。
その中でも、date-fnsは、欲しい情報がすぐに見つけられて好感を持ちました。
例えば、日付を加算するメソッドを探そうとadd
と入力したら一発で欲しい情報に辿り着けたのには感謝感激。
URL: https://date-fns.org/docs/Getting-Started
ライブラリごとの情報は以下の通りです。
(2018/12/10 00:03時点)
moment
GitHub: https://github.com/moment/moment Document: https://momentjs.com GitHub Star: 39,459 / Latest commit: Oct 31, 2018 / created: Mar 1, 2011dayjs
GitHub: https://github.com/iamkun/dayjs Document: https://github.com/iamkun/dayjs/blob/master/docs/ja/README-ja.md GitHub Star: 16,821 / Latest commit: Nov 20, 2018 / created: Apr 10, 2018date-fns
GitHub: https://github.com/date-fns/date-fns Document: https://date-fns.org GitHub Star: 15,312 / Latest commit: Nov 14, 2018 / created: Oct 6, 2014luxon
GitHub: https://github.com/moment/luxon Document: https://moment.github.io/luxon GitHub Star: 7290 / Latest commit: Nov 30, 2018 / created: Nov 30, 2015サンプルコード
各ライブラリのサンプルコードを記載していきます。
処理は前提で記載した以下3つの処理を各ライブラリごとに記載しています。
- 日付時刻をフォーマットする
- 日付の加算と減算を計算する
- 2つの日付の日数を計算する
momentのサンプル
momentは、前提に記載しているので省略します。
dayjsのサンプル
Moment.js の API との広い互換性を持ちます。
参照: https://github.com/iamkun/dayjs/blob/master/docs/ja/README-ja.md
上記の通り、dayjsはmomentと互換性があり、前提に記載したサンプルコードのmoment
をdayjs
に置換しただけで作業は終了しました。
moment経験者は学習要らずかもしれません。
❶. 日付時刻をフォーマットする
import dayjs from 'dayjs'
const timestamp = 1544911200 * 1000 // 2018/12/16 07:00:00
dayjs(timestamp).format('YYYY/MM/DD HH:mm:ss')
> 2018/12/16 07:00:00
❷. 日付の加算と減算を計算する
import dayjs from 'dayjs'
const timestamp = 1544911200 * 1000 // 2018/12/16 07:00:00
dayjs(timestamp).add(3, 'days').format('YYYY/MM/DD')
> 2018/12/19
dayjs(timestamp).add(-3, 'days').format('YYYY/MM/DD')
> 2018/12/13
❸. 2つの日付の日数を計算する
import dayjs from 'dayjs'
const timestamp1 = 1544911200 * 1000 // 2018/12/16 07:00:00
const timestamp2 = 1549663200 * 1000 // 2019/02/09 07:00:00
dayjs(timestamp1).diff(timestamp2, 'days')
> -55
dayjs(timestamp2).diff(timestamp1, 'days')
> 55
date-fnsのサンプル
他のライブラリは、メソッドチェーンで頑張っていくのに対して、date-fnsはコードがシンプルに書けて使いやすかったです。
❶. 日付時刻をフォーマットする
import format from 'date-fns/format'
const timestamp = 1544911200 * 1000 // 2018/12/16 07:00:00
format(timestamp, 'YYYY/MM/DD HH:mm:ss')
> 2018/12/16 07:00:00
❷. 日付の加算と減算を計算する
import format from 'date-fns/format'
import addDays from 'date-fns/add_days'
const timestamp = 1544911200 * 1000 // 2018/12/16 07:00:00
format(addDays(timestamp, 3), 'YYYY/MM/DD')
> 2018/12/19
format(addDays(timestamp, -3), 'YYYY/MM/DD')
> 2018/12/13
❸. 2つの日付の日数を計算する
import differenceInDays from 'date-fns/difference_in_days'
const timestamp1 = 1544911200 * 1000 // 2018/12/16 07:00:00
const timestamp2 = 1549663200 * 1000 // 2019/02/09 07:00:00
differenceInDays(timestamp1, timestamp2)
> -55
differenceInDays(timestamp2, timestamp1)
> 55
luxonのサンプル
明示的なAPIがluxonの1つの特徴です。moment, dayjs, date-fnsは、「timestamp」「ISO8601フォーマットの文字列」「Dateオブジェクト」等をAPIに渡せば日付時刻操作を行うことができます。
// momentの場合
moment(1544911200 * 1000).format('YYYY/MM/DD HH:mm:ss')
moment('2018-12-16T07:00:00+09:00').format('YYYY/MM/DD HH:mm:ss')
moment(new Date('2018-12-16 07:00:00')).format('YYYY/MM/DD HH:mm:ss')
一方luxonは、日付時刻の形式ごとにAPIが分けられているので、日付時刻のデータを厳密に管理したい人にとっては良いのかもしれません。
// luxonの場合
DateTime.fromMillis(1544911200 * 1000).toFormat('yyyy/MM/dd HH:mm:ss')
DateTime.fromISO('2018-12-16T07:00:00+09:00').toFormat('yyyy/MM/dd HH:mm:ss')
DateTime.fromJSDate(new Date('2018-12-16 07:00')).toFormat('yyyy/MM/dd HH:mm:ss')
参考: https://qiita.com/jnst/items/545d9a7a5a95e190fbf5#-なぜ-luxon-がつくられたか
それでは、サンプルコードです。
❶. 日付時刻をフォーマットする
import { DateTime } from 'luxon'
const timestamp = 1544911200 * 1000 // 2018/12/16 07:00:00
DateTime.fromMillis(timestamp).toFormat('yyyy/MM/dd HH:mm:ss')
> 2018/12/16 07:00:00
❷. 日付の加算と減算を計算する
import { DateTime } from 'luxon'
const timestamp = 1544911200 * 1000 // 2018/12/16 07:00:00
DateTime.fromMillis(timestamp).plus({ days: 3 }).toFormat('yyyy/MM/dd')
> 2018/12/19
DateTime.fromMillis(timestamp).plus({ days: -3 }).toFormat('yyyy/MM/dd')
> 2018/12/13
❸. 2つの日付の日数を計算する
import { DateTime } from 'luxon'
const timestamp1 = 1544911200 * 1000 // 2018/12/16 07:00:00
const timestamp2 = 1549663200 * 1000 // 2019/02/09 07:00:00
const dt1 = DateTime.fromMillis(timestamp1)
const dt2 = DateTime.fromMillis(timestamp2)
Math.floor(dt1.diff(dt2, 'days').days)
> -55
Math.floor(dt2.diff(dt1, 'days').days)
> 55
ファイルサイズ
上記サンプルコードをNuxt.js上に書き、それをanalyzeしてファイルサイズを確認しました。
以下にリポジトリを公開しています。
GitHub: https://github.com/yagisuke/jikan-libraries
プロジェクト全体
ライブラリごと
ファイルサイズが軽量なものから並べています。
dayjsとdate-fnsはmomentよりもだいぶ軽量だったので好印象でした。
luxonはmomentよりも重いようです。
ライブラリ名 | バンドルサイズ | 補足 |
---|---|---|
dayjs | ||
date-fns | サンプルコードで必要な format, addDays, differenceInDays のみに絞りました | |
moment | localeファイルはjaに絞りました | |
luxon |
まとめ
「moment」「dayjs」「date-fns」「luxon」をそれぞれみてきました。
比較するのって楽しいですね
そして、比較した結果、今回は dayjs
を採用することにしました
採用した理由としては、以下の通りです。
- GitHub Starやメンテナンス頻度を確認して安心感があったこと
- momentで行なっていた処理を代替できること
- momentに比べて大幅にファイルサイズを削減できること
- momentの利用経験者は学習コストがかからないこと
- momentと互換性があり、乗り換えまでに時間がかからないこと
date-fns
と悩みましたが、決め手になったのは momentと互換性があり、乗り換えまでに時間がかからないこと
でした。
momentユーザーの方は、この機会にぜひdayjs
を使ってみてはいかがでしょうか。
以上、お付き合いいただきありがとうございました。