date-fns vs dayjs vs moment vs luxon
JavaScript における日付操作ライブラリの比較
date-fnsdayjsmomentluxon類似パッケージ:
JavaScript における日付操作ライブラリの比較

date-fnsdayjsluxonmoment はすべて JavaScript における日付・時刻の操作を簡略化するためのライブラリです。これらのライブラリは、ネイティブの Date オブジェクトが抱える不変性の欠如、タイムゾーン対応の難しさ、直感的でない API といった課題を解決するために設計されています。moment は長らく業界標準として使われてきましたが、現在はメンテナンスモードに入っており、新規プロジェクトでの使用は推奨されていません。一方、date-fns は関数型アプローチと優れたツリーシェイキングを特徴とし、dayjsmoment に似たチェーン可能な API を軽量に再現し、luxon は現代的な Intl API を活用して強力なタイムゾーンサポートを提供します。

npmのダウンロードトレンド
3 年
GitHub Starsランキング
統計詳細
パッケージ
ダウンロード数
Stars
サイズ
Issues
公開日時
ライセンス
date-fns32,642,02636,38422.6 MB8961年前MIT
dayjs26,991,04448,478679 kB1,1832ヶ月前MIT
moment22,045,67448,0904.35 MB2912年前MIT
luxon15,380,05316,3014.59 MB1844ヶ月前MIT

日付操作ライブラリ比較:date-fns、dayjs、luxon、moment

フロントエンド開発で日付を扱う際、Date オブジェクトの限界にすぐにぶつかります。そこで登場するのが日付操作ライブラリですが、選択肢は多く、それぞれ設計思想や機能が大きく異なります。特に moment は公式に非推奨となっており、新規プロジェクトでの使用は避けるべきです。ここでは、プロフェッショナルな開発者がアーキテクチャ判断を行うために必要な、技術的・実践的な観点から4つの主要ライブラリを比較します。

📅 ライブラリの基本設計と状態

moment はかつてデファクトスタンダードでしたが、公式ドキュメントおよび npm ページ に明記されている通り、「メンテナンスモード」に入っており、新規プロジェクトでの使用は推奨されていません。代わりに date-fnsdayjsluxon のいずれかを検討すべきです。

残り3つは、以下のような設計思想の違いがあります:

  • date-fns: 関数型スタイル。不変性を保ち、個別の関数をインポートして使う。
  • dayjs: moment に似たチェーン可能な API を提供しつつ、軽量でモジュール化可能。
  • luxon: 現代的な JavaScript(特に Intl API)を活用し、タイムゾーン処理に強みを持つ。

🧩 不変性とオブジェクト指向 vs 関数型

date-fns は完全に不変で、関数に日付を渡すと新しい日付が返されます。元の値は一切変更されません。

// date-fns
import { addDays, format } from 'date-fns';

const today = new Date();
const tomorrow = addDays(today, 1);
console.log(format(tomorrow, 'yyyy-MM-dd')); // 新しい日付を返す

dayjsmoment と同様にチェーン可能なメソッドを持ちますが、内部的には不変です(各メソッドが新しいインスタンスを返す)。

// dayjs
import dayjs from 'dayjs';

const today = dayjs();
const tomorrow = today.add(1, 'day');
console.log(tomorrow.format('YYYY-MM-DD')); // 新しいインスタンス

luxon も同様に不変で、DateTime クラスのメソッドは常に新しいインスタンスを返します。

// luxon
import { DateTime } from 'luxon';

const today = DateTime.now();
const tomorrow = today.plus({ days: 1 });
console.log(tomorrow.toFormat('yyyy-MM-dd')); // 新しいインスタンス

moment(非推奨)は、古いバージョンでは可変でしたが、後期には不変メソッドも追加されました。しかし、設計が古く、bundle size も大きいのが問題です。

// moment(非推奨)
import moment from 'moment';

const today = moment();
const tomorrow = today.clone().add(1, 'days'); // clone 必須で安全
console.log(tomorrow.format('YYYY-MM-DD'));

💡 補足:dayjsluxonmoment と似た API を持ちますが、内部は不変で安全です。一方、date-fns は「関数に日付を渡す」という素朴なアプローチを取ります。

🌍 タイムゾーン対応

date-fns はコアパッケージにタイムゾーンサポートが含まれていませんdate-fns-tz という別パッケージが必要です。

// date-fns + date-fns-tz
import { zonedTimeToUtc, utcToZonedTime, format } from 'date-fns-tz';

const utcDate = zonedTimeToUtc('2023-07-01 12:00:00', 'Asia/Tokyo');
const tokyoTime = utcToZonedTime(utcDate, 'Asia/Tokyo');
console.log(format(tokyoTime, 'yyyy-MM-dd HH:mm', { timeZone: 'Asia/Tokyo' }));

dayjs もコアにはタイムゾーン機能がなく、timezone プラグインを有効にする必要があります。

// dayjs + timezone plugin
import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';

dayjs.extend(utc);
dayjs.extend(timezone);

dayjs.tz.setDefault('Asia/Tokyo');
const tokyoTime = dayjs.tz('2023-07-01 12:00:00', 'Asia/Tokyo');
console.log(tokyoTime.format('YYYY-MM-DD HH:mm'));

luxonビルトインでタイムゾーン対応しており、Intl API を利用するため、追加プラグイン不要で動作します。

// luxon(タイムゾーン内蔵)
import { DateTime } from 'luxon';

const tokyoTime = DateTime.fromISO('2023-07-01T12:00:00', { zone: 'Asia/Tokyo' });
console.log(tokyoTime.toFormat('yyyy-MM-dd HH:mm')); // タイムゾーン情報保持

momentmoment-timezone という別パッケージが必要で、bundle size がさらに膨らみます(非推奨)。

// moment-timezone(非推奨)
import moment from 'moment-timezone';

const tokyoTime = moment.tz('2023-07-01 12:00:00', 'Asia/Tokyo');
console.log(tokyoTime.format('YYYY-MM-DD HH:mm'));

💡 タイムゾーンを頻繁に扱うアプリ(国際サービス、スケジューラなど)では、luxon が最も手軽で堅牢です。

📦 ツリーシェイキングとバンドルサイズ

date-fns は関数単位でインポートできるため、ツリーシェイキングに最適です。使わない関数は一切バンドルされません。

// date-fns — 最小限のコード
import { format, isBefore } from 'date-fns';

dayjs もプラグイン方式で、必要な機能だけを追加できます。コアは非常に小さいです。

// dayjs — コア + 必要なプラグインのみ
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime);

luxon は ES モジュール対応でツリーシェイキング可能ですが、機能が密結合しているため、date-fnsdayjs ほど細かく削減できません。

// luxon — 基本的に DateTime を使う
import { DateTime } from 'luxon';

momentツリーシェイキングが効きにくい構造で、ロケールデータも含めると bundle size が肥大化します(非推奨)。

🗓️ パースとフォーマットの柔軟性

date-fnsparse 関数でカスタムフォーマットに対応しますが、フォーマット文字列の指定が必要です。

// date-fns
import { parse, format } from 'date-fns';

const date = parse('01/07/2023', 'dd/MM/yyyy', new Date());
console.log(format(date, 'yyyy-MM-dd'));

dayjsCustomParseFormat プラグインを使うことで、柔軟なパースが可能になります。

// dayjs + CustomParseFormat
import dayjs from 'dayjs';
import customParseFormat from 'dayjs/plugin/customParseFormat';
dayjs.extend(customParseFormat);

const date = dayjs('01/07/2023', 'DD/MM/YYYY');
console.log(date.format('YYYY-MM-DD'));

luxonfromFormat メソッドで直接カスタムフォーマットをパースできます。

// luxon
import { DateTime } from 'luxon';

const date = DateTime.fromFormat('01/07/2023', 'dd/MM/yyyy');
console.log(date.toFormat('yyyy-MM-dd'));

moment は柔軟なパースが可能でしたが、非推奨のため新規採用は避けてください。

🔁 相対時間(例:「2分前」)の扱い

date-fnsformatDistance 関数で相対時間を生成できます。

// date-fns
import { formatDistance, subMinutes } from 'date-fns';
import { ja } from 'date-fns/locale';

const past = subMinutes(new Date(), 2);
console.log(formatDistance(past, new Date(), { locale: ja })); // 「2分前」

dayjsRelativeTime プラグインを有効にすることで、.fromNow() が使えます。

// dayjs
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
import 'dayjs/locale/ja';

dayjs.extend(relativeTime);
dayjs.locale('ja');

const past = dayjs().subtract(2, 'minute');
console.log(past.fromNow()); // 「2分前」

luxontoRelative() メソッドを備えており、ロケール対応も簡単です。

// luxon
import { DateTime } from 'luxon';

const past = DateTime.now().minus({ minutes: 2 });
console.log(past.toRelative({ locale: 'ja' })); // 「2分前」

🧪 テストと不変性の安全性

すべての現代的ライブラリ(date-fns, dayjs, luxon)は不変性を保証しており、ユニットテストで副作用の心配がありません。一方、moment は過去に可変インスタンスによるバグが多発した経緯があり、それが非推奨の理由の一つでもあります。

✅ まとめ:どのライブラリを選ぶべきか?

特徴date-fnsdayjsluxonmoment
新規プロジェクト推奨❌(非推奨)
不変性✅(関数型)✅(チェーン)✅(クラス)⚠️(注意が必要)
タイムゾーン別パッケージプラグインビルトイン別パッケージ
ツリーシェイキング最適良好可能だが限定的劣悪
学習コスト中(関数型)低(moment に似てる)中(Intl 依存)低(だが非推奨)

実践的な選択基準

  • 最小限の bundle size と関数型志向が好きなら → date-fns
  • moment に似た API で軽量さが欲しいなら → dayjs
  • タイムゾーンや国際化を多用するなら → luxon
  • 新規プロジェクトで moment を使うのは避けてください

最終的には、チームの好みやプロジェクトの要件(特にタイムゾーンの有無)によって決めるのがベストです。ただし、moment は公式に非推奨なので、移行計画を立てるか、最初から他の選択肢を選ぶことを強く推奨します。

選び方: date-fns vs dayjs vs moment vs luxon
  • date-fns:

    date-fns は、関数型プログラミングを好み、bundle size を最小限に抑えたいプロジェクトに最適です。各関数を個別にインポートできるため、ツリーシェイキングが非常に効果的です。ただし、タイムゾーン処理には別パッケージ(date-fns-tz)が必要で、API が moment のようなチェーン形式ではない点に注意が必要です。

  • dayjs:

    dayjs は、moment に似た使いやすさを維持しつつ、軽量でモダンな代替手段を求めているチームに向いています。プラグイン方式で必要な機能だけを追加でき、bundle size も小さく抑えられます。既存の moment コードを移行する際にも学習コストが低く済みます。

  • moment:

    moment は公式に「メンテナンスモード」に入っており、新規プロジェクトでの使用は推奨されていません。bundle size が大きく、ツリーシェイキングも効きにくく、現代的な要件(特にタイムゾーンや不変性)に対して劣っています。既存プロジェクトの保守には使えますが、新規開発では date-fnsdayjsluxon のいずれかを検討すべきです。

  • luxon:

    luxon は、タイムゾーンや国際化(ロケール)を多用するアプリケーション、例えばグローバル展開するサービスやスケジューリングツールに最適です。Intl API を活用しているため、追加のプラグインなしで強力なタイムゾーンサポートが利用できます。ただし、ES2018 以降の環境を前提としている点に留意してください。

date-fns のREADME

🔥️ NEW: date-fns v4.0 with first-class time zone support is out!

date-fns

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

👉 Documentation

👉 Blog


It's like Lodash for dates

  • It has 200+ functions for all occasions.
  • Modular: Pick what you need. Works with webpack, Browserify, or Rollup and also supports tree-shaking.
  • Native dates: Uses existing native type. It doesn't extend core objects for safety's sake.
  • Immutable & Pure: Built using pure functions and always returns a new date instance.
  • TypeScript: The library is 100% TypeScript with brand-new handcrafted types.
  • I18n: Dozens of locales. Include only what you need.
  • and many more benefits
import { compareAsc, format } from "date-fns";

format(new Date(2014, 1, 11), "yyyy-MM-dd");
//=> '2014-02-11'

const dates = [
  new Date(1995, 6, 2),
  new Date(1987, 1, 11),
  new Date(1989, 6, 10),
];
dates.sort(compareAsc);
//=> [
//   Wed Feb 11 1987 00:00:00,
//   Mon Jul 10 1989 00:00:00,
//   Sun Jul 02 1995 00:00:00
// ]

The library is available as an npm package. To install the package run:

npm install date-fns --save

Docs

See date-fns.org for more details, API, and other docs.


License

MIT © Sasha Koss