Qiita Teams that are logged in
You are not logged in to any team

Log in to Qiita Team
Community
OrganizationEventAdvent CalendarQiitadon (β)
Service
Qiita JobsQiita ZineQiita Blog
154
Help us understand the problem. What are the problem?

More than 1 year has passed since last update.

Organization

日付時刻操作ライブラリを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と同じOrganizationで開発されており、興味もあって検討対象に入れてみました。

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を使ってみてはいかがでしょうか。

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

Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away
154
Help us understand the problem. What are the problem?