Edited at

日付時刻操作ライブラリをmomentからdayjsへ乗り換えた

この記事は「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点です。



  1. momentよりもファイルサイズが軽量のもの

  2. 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と同じリポジトリで開発されており、興味もあって検討対象に入れてみました。


npmのダウンロード数から見る

npmのダウンロード数を比較できるnpmtrendsによると、momentはまだまだ圧倒的に支持されており、次いでdate-fnsが徐々にシェアを伸ばしている様子。

dayjsとluxon頑張れ!というような状態です。

npmtrends.png

画像元はこちらです。

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と入力したら一発で欲しい情報に辿り着けたのには感謝感激。

doc-date-fns.png

URL: https://date-fns.org/docs/Getting-Started

ライブラリごとの情報は以下の通りです。

(2018/12/10 00:03時点)


moment

logo-moment.png

GitHub: https://github.com/moment/moment

Document: https://momentjs.com

GitHub Star: 39,459 / Latest commit: Oct 31, 2018 / created: Mar 1, 2011


dayjs

logo-dayjs.png

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, 2018


date-fns

logo-date-fns.png

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, 2014


luxon

logo-luxon.png

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つの処理を各ライブラリごとに記載しています。



  1. 日付時刻をフォーマットする

  2. 日付の加算と減算を計算する

  3. 2つの日付の日数を計算する



momentのサンプル

momentは、前提に記載しているので省略します。


dayjsのサンプル


Moment.js の API との広い互換性を持ちます。

参照: https://github.com/iamkun/dayjs/blob/master/docs/ja/README-ja.md


上記の通り、dayjsはmomentと互換性があり、前提に記載したサンプルコードのmomentdayjsに置換しただけで作業は終了しました。

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


プロジェクト全体

size.png


ライブラリごと

ファイルサイズが軽量なものから並べています。

dayjsとdate-fnsはmomentよりもだいぶ軽量だったので好印象でした。

luxonはmomentよりも重いようです。

ライブラリ名
バンドルサイズ
補足

dayjs
dayjs.png

date-fns
date-fns.png
サンプルコードで必要な format, addDays, differenceInDays のみに絞りました

moment
moment.png
localeファイルはjaに絞りました

luxon
luxon.png


まとめ

「moment」「dayjs」「date-fns」「luxon」をそれぞれみてきました。

比較するのって楽しいですね:relaxed:

そして、比較した結果、今回は dayjs を採用することにしました:tada::tada:

採用した理由としては、以下の通りです。


  • GitHub Starやメンテナンス頻度を確認して安心感があったこと

  • momentで行なっていた処理を代替できること

  • momentに比べて大幅にファイルサイズを削減できること

  • momentの利用経験者は学習コストがかからないこと

  • momentと互換性があり、乗り換えまでに時間がかからないこと

date-fnsと悩みましたが、決め手になったのは momentと互換性があり、乗り換えまでに時間がかからないこと でした。

momentユーザーの方は、この機会にぜひdayjsを使ってみてはいかがでしょうか。

以上、お付き合いいただきありがとうございました。