はじめに
JavaScriptの日付操作ライブラリは、Moment.jsのメンテナンスモード移行以降、date-fns と Day.js の二強時代が続いてきました。
そんな中、2024年に登場し注目を集めていた Tempo が、2025年12月に待望の v1.0.0(Stable) に到達しました。
「ブラウザ標準API (Intl) を活用し、超軽量・タイムゾーンデータ不要」というコンセプトは非常に魅力的です。
しかし、実戦投入するには 「date-fnsとは似て非なる部分」 を理解しておく必要があります。
本記事では、v1.0時点での実力と、採用におけるトレードオフをフラットに解説します。
Tempo とは
- コンセプト: Intl.DateTimeFormat 等のラッパー。
- 特徴: ロケールやタイムゾーンの辞書データを持たないため、バンドルサイズが極小。
- ステータス: v1.0.0 (2025年12月リリース)
import { format, addDay } from "@formkit/tempo"
const now = new Date()
// 3引数形式: format(date, format, locale?)
console.log(format(now, "full", "ja"))
// オブジェクト形式: タイムゾーン指定が可能
console.log(format({ date: now, format: "full", locale: "ja", tz: "Asia/Tokyo" }))
✅ メリット:ここが素晴らしい
1. タイムゾーン対応が「完全標準準拠」
これが最大の強みです。従来のライブラリ(特にDay.js)では、タイムゾーンを扱うためにプラグインの追加や、巨大なデータファイルのロードが必要でした。
Tempoはブラウザが持つ Intl API を利用するため、追加設定一切なしで世界中のタイムゾーンを扱えます。
// アメリカ東部時間でフォーマット。追加のimportは不要。
format({ date: new Date(), format: "YYYY-MM-DD HH:mm", locale: "en", tz: "America/New_York" })
// タイムゾーン不要なら3引数形式でもOK
format(new Date(), "YYYY-MM-DD HH:mm", "en")
2. バンドルサイズの削減効果
ロケールデータを持たないため、多言語対応アプリであってもライブラリのサイズは一定(極小)です。
「日付ライブラリを入れたらバンドルサイズが数百KB増えた」という悪夢から解放されます。
3. APIスタイルは date-fns に近い
関数をimportして Date オブジェクトを渡すスタイル(Functional API)を採用しており、Tree-shakingも効きやすい設計です。
⚠️ デメリット・注意点
「date-fnsの軽量版としてそのまま置き換えられるか?」というと、答えはNoです。
以下の"落とし穴"を許容できるかが採用の分かれ目になります。
1. パース(解析)が極めて厳格(Strict Parsing)
Day.js などは、フォーマット文字列と入力値が多少ズレていてもよしなに解釈してくれますが、Tempoの parse は一文字でもズレると失敗します。
import { parse } from "@formkit/tempo"
// date-fnsやDay.jsなら通るケースでも…
// フォーマットと入力が完全一致しないとエラーになる
parse("2025/01/01", "YYYY-MM-DD") // -> Error! (区切り文字違い)
parse("2025-1-1", "YYYY-MM-DD") // -> Error! (0埋め不足)
ユーザー入力など「揺らぎ」のあるデータを扱う場合、事前の正規化処理が必須になります。
2. フォーマットトークンの微妙な差異
「date-fnsと同じ感覚で書ける」と言われますが、フォーマット指定子(トークン)に一部互換性がありません。
Tempoは Intl の仕様に引きずられる部分があるためです。
| ライブラリ | 年 | 日 |
|---|---|---|
| date-fns | yyyy |
dd |
| Tempo | YYYY |
DD |
| Day.js | YYYY |
DD |
特に date-fns から移行する場合、フォーマット文字列の全置換が必要になるため注意が必要です(Day.jsユーザーには馴染みやすい形式です)。
3. レガシー環境への依存(Intlの実装差異)
Tempoはブラウザの Intl 実装に依存します。
モダンブラウザ(Chrome/Edge/Firefox/Safariの最新版)では問題ありませんが、古いiOS (iOS 14未満など) やAndroid では、Intl の実装が不完全で意図した動作にならないリスクがあります。
これを回避するために Polyfill を入れると、Tempoの「軽量」というメリットが相殺されてしまいます。
4. 複雑な暦操作・ビジネスロジックは弱い
「第3営業日を計算する」「祝日を考慮する」といった高度な機能は本体に含まれていません。date-fnsのエコシステム(プラグイン等)に慣れていると、機能不足を感じる場面があるでしょう。
比較表(v1.0時点)
| 項目 | Tempo | date-fns | Day.js |
|---|---|---|---|
| バージョン | v1.0.0 | v4.x | v1.x |
| API | 関数型 | 関数型 | オブジェクト指向 |
| サイズ | 極小 | 小 | 小 |
| タイムゾーン | 標準 (Intl) | 別途必要 (date-fns-tz) | プラグイン必要 |
| パース | 厳格 | 厳格/柔軟(使い分け可) | 柔軟 |
| フォーマット | YYYY-MM-DD | yyyy-MM-dd | YYYY-MM-DD |
結論:v1.0の今、採用すべきか?
✅ 採用推奨
- Next.js / Nuxt / SvelteKit などのモダン環境(ターゲットブラウザが新しい)
- 多言語対応 (i18n) が必須のアプリケーション
- タイムゾーン変換が頻繁に発生する(カレンダーアプリ、海外向け予約システム)
- バンドルサイズ削減が最優先事項
❌ 様子見・不向き
- レガシーブラウザのサポートが必須(iOS 12/13など古い端末も対象)
- ユーザー入力データのフォーマットがバラバラ
- 既存の date-fns プロジェクトからの単純なリプレース(書き換えコスト意外と高め)
おわりに
Tempoは、 「ブラウザ標準機能への回帰」 という合理的かつモダンなアプローチをとっています。
特に「タイムゾーンの扱い」に関しては、既存ライブラリよりも頭一つ抜けて便利です。
新規プロジェクトであれば、第一候補として検討する価値は十分にあります。