date-fns、dayjs、js-joda、luxon、moment はいずれも JavaScript で日付や時刻を扱うためのライブラリです。これらのライブラリは、日付のパース、フォーマット、計算、タイムゾーン変換などの共通の課題を解決しますが、アーキテクチャ、パフォーマンス特性、API設計、国際化対応において大きく異なります。特に、moment は公式に非推奨とされており、新規プロジェクトでの使用は推奨されません。他の4つはそれぞれ異なる設計哲学を持ち、プロジェクトの要件に応じて適切な選択が求められます。
フロントエンド開発で日付を扱う際、適切なライブラリ選びはパフォーマンス・メンテナンス性・開発体験に大きく影響します。この記事では、代表的な5つのJavaScript日付ライブラリ — date-fns、dayjs、js-joda、luxon、moment — を、実務の観点から徹底比較します。
まず最初に明確にしておきます。moment は公式に非推奨(deprecated)とされています。npmページおよびGitHubリポジトリには「プロジェクトはメンテナンスモードに入っており、新規プロジェクトでの使用は推奨されない」と記載されています。既存コードの保守には使えますが、新規プロジェクトでは絶対に選ばないでください。以下では比較の公平性のために言及しますが、選択肢としては除外すべきです。
日付ライブラリの根幹となる設計思想は、「オブジェクトを変更するか(ミュータブル)」それとも「新しいインスタンスを返すか(イミュータブル)」です。
moment は ミュータブル です。メソッド呼び出しで元のオブジェクトが変更されます。
// moment: ミュータブル(危険!)
const now = moment();
const tomorrow = now.add(1, 'day');
console.log(now.isSame(tomorrow)); // true ← now も変更されている!
一方、date-fns、dayjs、js-joda、luxon はすべて イミュータブル を採用しています。安全で予測可能な動作が得られます。
// date-fns: イミュータブル
import { addDays } from 'date-fns';
const now = new Date();
const tomorrow = addDays(now, 1);
console.log(now === tomorrow); // false ← 別のインスタンス
// dayjs: イミュータブル
import dayjs from 'dayjs';
const now = dayjs();
const tomorrow = now.add(1, 'day');
console.log(now.isSame(tomorrow)); // false
// js-joda: イミュータブル
import { LocalDate } from 'js-joda';
const today = LocalDate.now();
const tomorrow = today.plusDays(1);
console.log(today.equals(tomorrow)); // false
// luxon: イミュータブル
import { DateTime } from 'luxon';
const now = DateTime.now();
const tomorrow = now.plus({ days: 1 });
console.log(now.hasSame(tomorrow, 'day')); // false
💡 フロントエンド開発では、状態管理との整合性やバグ防止のため、イミュータブル設計が強く推奨されます。この点で
momentは時代遅れです。
バンドルサイズを最適化するには、使った関数だけが含まれる「ツリーシェイキング」対応が必須です。
date-fns は 関数ベース の設計で、個別の関数を直接インポートできます。
// date-fns: 関数単位でインポート → 最小バンドル
import { format, parseISO, addDays } from 'date-fns';
dayjs は 軽量コア + プラグイン 方式です。基本機能は小さいですが、追加機能(例:相対時間、ロケール)はプラグインとして後から読み込みます。
// dayjs: コア + 必要なプラグインのみ
import dayjs from 'dayjs';
import relativeTime from 'dayjs/plugin/relativeTime';
dayjs.extend(relativeTime);
js-joda と luxon は クラスベース で、必要なクラスや静的メソッドをインポートします。ただし、内部的に依存が多い場合があり、完全なツリーシェイキングは難しいことがあります。
// js-joda: クラス単位でインポート
import { LocalDateTime, ZoneId } from 'js-joda';
// luxon: 名前付きインポート
import { DateTime, Duration } from 'luxon';
💡 SPAやモバイル向けアプリでは、
date-fnsまたはdayjsがバンドルサイズ面で有利です。
グローバルアプリ開発では、タイムゾーンや多言語対応が必須です。
date-fns は タイムゾーン非対応 です。UTCやローカル時刻は扱えますが、任意のタイムゾーン(例:Asia/Tokyo)での計算はできません。i18nは別途 date-fns/locale からロケールをインポートします。
// date-fns: タイムゾーン非対応
import { format } from 'date-fns';
import { ja } from 'date-fns/locale';
format(new Date(), 'yyyy年MM月dd日', { locale: ja });
dayjs は プラグインでタイムゾーン対応 します。timezone プラグインを使えば、任意のタイムゾーンで日付操作が可能です。
// dayjs: タイムゾーンプラグイン
import dayjs from 'dayjs';
import timezone from 'dayjs/plugin/timezone';
import utc from 'dayjs/plugin/utc';
dayjs.extend(utc);
dayjs.extend(timezone);
dayjs.tz('2023-01-01', 'America/New_York').tz('Asia/Tokyo').format();
js-joda は JavaのJSR-310仕様に準拠 しており、ネイティブで強力なタイムゾーンサポート を備えています。ZoneId や ZonedDateTime といったクラスで正確な計算が可能です。
// js-joda: ネイティブタイムゾーン
import { ZonedDateTime, ZoneId } from 'js-joda';
const tokyoTime = ZonedDateTime.now(ZoneId.of('Asia/Tokyo'));
luxon も ネイティブでタイムゾーン対応 しており、ブラウザの Intl API を活用します。設定不要で高精度な変換が可能です。
// luxon: 組み込みタイムゾーン
import { DateTime } from 'luxon';
const tokyo = DateTime.now().setZone('Asia/Tokyo');
💡 タイムゾーンを多用するアプリ(例:スケジューラ、国際物流)では、
js-jodaかluxonが最適です。dayjsは軽量さを犠牲にせず対応したい場合に検討できます。
ユーザー入力やAPIレスポンスからの日付パース、表示用フォーマットも重要です。
date-fns は parse と format で柔軟に対応します。フォーマット文字列は独自仕様ですが直感的です。
// date-fns
import { parse, format } from 'date-fns';
const date = parse('2023-01-01', 'yyyy-MM-dd', new Date());
const str = format(date, 'yyyy年MM月dd日');
dayjs は dayjs(string) でISO形式を自動パースし、.format() で出力します。カスタムフォーマットも可能。
// dayjs
const date = dayjs('2023-01-01');
const str = date.format('YYYY年MM月DD日');
js-joda は DateTimeFormatter を使って厳密にパース・フォーマットします。Java経験者には馴染みやすいです。
// js-joda
import { LocalDate, DateTimeFormatter } from 'js-joda';
const formatter = DateTimeFormatter.ofPattern('yyyy-MM-dd');
const date = LocalDate.parse('2023-01-01', formatter);
const str = date.format(formatter);
luxon は DateTime.fromFormat() と .toFormat() を使います。フォーマット文字列はUnicode Technical Standard #35に準拠。
// luxon
import { DateTime } from 'luxon';
const date = DateTime.fromFormat('2023-01-01', 'yyyy-MM-dd');
const str = date.toFormat('yyyy年MM月dd日');
💡 シンプルなユースケースなら
date-fnsやdayjsで十分。厳密なフォーマット制御が必要ならjs-joda、Intl連携重視ならluxonが向いています。
他のライブラリや標準APIとの連携も考慮しましょう。
date-fns: 純粋な Date オブジェクトを入出力するため、他ライブラリとの連携が容易。dayjs: .toDate() で Date に変換可能。軽量で多くのプロジェクトで採用されている。js-joda: Javaの日付APIに似ているが、JavaScript標準とは異なる。Date との変換には注意が必要。luxon: ブラウザの Intl API を内部で使い、Date との変換も簡単。現代的な設計。| ライブラリ | タイムゾーン | バンドルサイズ | 学習コスト | 推奨用途 |
|---|---|---|---|---|
date-fns | ✘ | ◎ (最小) | ◎ | 軽量SPA、シンプルな日付操作 |
dayjs | △ (プラグイン) | ◎ | ◎ | 汎用フロントエンド、軽量かつ拡張性が必要 |
js-joda | ◎ | △ | △ | 複雑な日付計算、Java経験者 |
luxon | ◎ | △ | ◎ | 国際化アプリ、モダンブラウザ向け |
moment | ◎ | ✘ (大きい) | ◎ | 非推奨:新規プロジェクトで使用禁止 |
moment を使うのは絶対にやめましょう。技術的負債になります。date-fns か dayjs を選ぶ。luxon(モダンで直感的)か js-joda(計算精度重視)。moment なら → 段階的に dayjs(APIが類似)へ移行するのが現実的です。正しいライブラリを選ぶことで、日付周りのバグを減らし、アプリの信頼性と開発速度を両立できます。
date-fns は関数ベースでツリーシェイキングに最適化されており、バンドルサイズを極限まで抑えたい軽量SPAやモバイル向けアプリに最適です。ただし、タイムゾーン操作には非対応なので、ローカル時刻やUTCのみで済むシンプルなユースケースに向いています。イミュータブル設計で安全に利用でき、Dateオブジェクトとの相互運用性も高いです。
dayjs は軽量なコアにプラグイン機構を組み合わせた設計で、必要最小限の機能だけをバンドルできます。moment.js に似たチェーンメソッドAPIを持ちながらイミュータブルで、既存のmomentユーザーの移行先としても人気です。タイムゾーンや相対時間などの高度な機能はプラグインで追加可能ですが、複雑な日付計算には向かない場合があります。
luxon はモダンブラウザのIntl APIを活用した設計で、タイムゾーンやロケール対応が組み込みで強力です。イミュータブルで直感的なAPIを持ち、国際化対応が必要なグローバルアプリやモダンなフロントエンドプロジェクトに最適です。ただし、古いブラウザをサポートする必要がある場合はpolyfillが必要になる点に注意してください。
js-joda はJavaのJSR-310仕様をJavaScriptに移植したもので、非常に正確で堅牢な日付計算が可能です。タイムゾーンや暦法に関する複雑な要件がある金融系システムや業務アプリ向けです。ただし、学習コストがやや高く、バンドルサイズも大きめなので、シンプルなWebアプリにはオーバースペックになることがあります。
moment は公式に非推奨(deprecated)とされており、新規プロジェクトでの使用は絶対に避けてください。既存コードの保守目的でのみ使用すべきです。ミュータブル設計による予期しない副作用、大きなバンドルサイズ、モダンなJavaScriptのベストプラクティスに合わない点など、多くの問題があります。新規開発では必ず代替ライブラリを検討してください。
🔥️ NEW: date-fns v4.0 with first-class time zone support is out!
date-fns provides the most comprehensive, yet simple and consistent toolset for manipulating JavaScript dates in a browser & Node.js
👉 Blog
It's like Lodash for dates
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
See date-fns.org for more details, API, and other docs.