Ruby: 2026年にもなってDateTimeを使うな(ただしユネスコの仕事は除く)(翻訳)
DateTimeクラスはRuby 3.0のときから非推奨化されたとみなされています。今年は2026年だというのに、未だにDateTimeを使っている人たちがいるのはどういうことでしょう?
最近のコードレビューでもこんなコードを目にしました。
whatever.starts_at = DateTime.now
ここでTimeクラスではなくDateTimeクラスを使っている理由を尋ねたところ、「DateTimeの方が日付の範囲が広いんでしょ?」という返答でした。
これは部分的には正しいのですが、あくまで2008年の32ビットシステム上での話です。
🔗 DateTimeの日付範囲のメリットはRuby 1.9.2の時点で消滅している
2010年にリリースされたRuby 1.9.2より前は、Timeクラスが扱える範囲はシステムのtime_t型(典型的には1901年〜2038年をカバーする32ビット符号付き整数)によって制限されていたため、当時はDateTimeクラスの方が断然範囲が広かったのです。
これはRuby 1.9.2の時点で変更されました。
TimeクラスはUNIX時間のエポックを起点とするナノ秒数を表す63ビット符号付き整数値を使うようになり、扱える日付範囲が1823年〜2116年に広がりました。Timeクラスはこの範囲を超えるとBignumまたはRationalクラスを使うようになるので、遅くはなるものの動きます。
つまり、DateTimeの方が日付の範囲が広いというメリットはとっくに失われているのです。
🔗 Rails 4.2を覚えてますか?
古いRailsの話で恐縮です1。少し前にRailsアプリを4.2から5.0にアップグレードしたとき、テストスイートが失敗したことで運よく問題を発見できました。驚くべきことに、原因はDateTime#utcの戻り値型が変更されたことだったのです。
Rails 4.2の場合:
DateTime.now.utc.class
# => DateTime
Rails 5.0の場合:
DateTime.now.utc.class # => Time
これによって、DateTimeを期待する厳密な型定義を持つDry::Structがいくつも壊れました。しかしこのときの私たちは、拙速に型を「修正する」代わりに、よりよい問いを発したのです。「一体どうしてDateTimeが使われていたんだろうか?」
Rails 5でDateTime#utcが破壊的に変更されたのはバグではなく、ちょっとした警告でした。つまり「このクラスを使うんじゃない」というメッセージを発していたのです。
Railsのアップグレードで苦しんでいる皆さんにお知らせ: Arkencyのソリューションをご検討ください
🔗 ユネスコ問題とは
DateTimeクラスの利用が正当化されるユースケースは、実は1つあります。すなわち歴史的な暦の改定です。
Ruby 2.4.1のドキュメントから引用します。
「ウィリアム・シェイクスピアとミゲル・デ・セルバンテスという同時代の文豪が亡くなった歴史上の日付は同じである」というのは、よくある誤解です。ユネスコはこれにちなんで4月23日を世界図書デー(World Book Day)と定めているほどです。
当時のイングランドではグレゴリオ暦への改定がまだ行われておらず(実際1752年まで行われませんでした)、2人が死んだ日は実際には10日もずれていたのです2。
RubyのTimeクラスは先発グレゴリオ暦(proleptic Gregorian calendar)と呼ばれる暦を採用しています。これはグレゴリオ暦を遡るときに歴史的事実を無視します。たとえば1582年10月10日という日付はイタリアには存在しません(教皇グレゴリウス13世がその10月から10日分を取り除いたため3)が、RubyのTimeクラスはそれに構わず、歴史的には存在しないはずの日付のタイムスタンプをしれっと生成します。
一方DateTimeクラスは、さまざまな暦改正日を扱えます。
shakespeare = DateTime.iso8601('1616-04-23', Date::ENGLAND)
cervantes = DateTime.iso8601('1616-04-23', Date::ITALY)
(shakespeare - cervantes).to_i # => 10日ずれているのがわかる!
歴史的資産をカタログ化したり、1752年より前の日付を複数の国に渡って処理したい場合なら、DateTimeクラスが有用です。
これ以外の用途にDateTimeクラスを応用するのは、どんな用途であろうと文字通り99.99%間違っています。
Norbert Wójtowiczはwroclove.rbでまさにこの問題について素晴らしい講演を行いました。
🔗 DateTimeの本当の問題とは何か
🔗 1: タイムゾーンをサポートしていない
DateTimeはタイムゾーンを処理できません。
DateTime.now
# => #<DateTime: 2026-01-14T13:00:00+00:00>
# 自分のシステムクロックはCET (+01:00)なのにどうして+00:00に?
🔗 2: Railsと互換性がない
ActiveSupportが拡張しているTimeはタイムゾーンをサポートしていますが、DateTimeはこのありさまです。
Time.current # ✅ Rails.application.config.time_zoneを反映している
DateTime.now # ❌ Railsコンフィグを無視してシステムタイムゾーンを使う
計算も混乱します。
Time.now + 1 # => 1秒後
DateTime.now + 1 # => 1日後
これは大量のバグを生み出す温床となります。
🔗 3: 夏時間が無視される
DateTimeは夏時間(DST: daylight saving time)について関知しません。タイムゾーンに関連する場所でDateTimeを使ったら最後、きっとバグになります。
🔗 4: パフォーマンスが落ちる
Timeクラスの方がDateTimeよりよほど高速です。
🔗 まだ使っている人との想定問答集
Q:「ずっと前から使い続けてるんだけど」
A: このコードが書かれたのは2009年ですが、今は2026年です。お願いだから更新してください。
Q:「時刻抜きで日付だけ保存したいんだけど」
A: それならDateクラスを使ってください。まさにそのためのクラスです。
Date.current # ✅ Rails.application.config.time_zone が反映される
Q:「使ってるライブラリがDateTimeクラスで値を返してくるんだけど」
A: 速攻で変換してください。
legacy_gem.fetch_date.to_time.in_time_zone
🔗 代わりに何を使うべきか
- タイムスタンプ
Time.currentかTime.zone.nowを使うこと- 日付
Date.currentを使うこと- 日時の解析
Time.zone.parse('2026-01-14 13:00:00')を使うこと
🔗 唯一の例外
歴史的文書や遺産をカタログ化する場合や、複数の国にまたがって暦改正日以前の日時を扱う場合にのみ、DateTimeクラスが有用です。ただし、どの暦改正日が適用されるかを追跡する必要があります。
それ以外のあらゆる用途(最新のアプリケーション、API、データベース)ではTimeクラスを使うこと。
DateTimeクラスが非推奨化されているのには、れっきとした理由があるのです。
🔗 参考記事
- Ruby Time documentation - Ruby 1.9.2 changes
- Ruby DateTime documentation - UNESCO calendar problem
- Norbert Wójtowicz - It's About Time (wroclove.rb)
- Ruby Style Guide: No DateTime
関連記事
- 原文の「Pepperidge Farm Remembers」は懐古趣味を揶揄するミームです。 ↩
- 訳注: シェイクスピアが亡くなったのはグレゴリオ暦では5月3日にあたります。 ↩
- 訳注: グレゴリオ暦への改定にあたって10日のずれを削るアイデアは、グレゴリオ暦の原案者の1人である天文学者アロイシウス・リリウスによる提案に含まれていました。なお、アロイシウスは提案がグレゴリウス13世に達する前に亡くなっていました。参考: 【ゆっくり解説】人類の叡智が求めた「正しい1年」【歴史解説】 - YouTube ↩
概要
元サイトの許諾を得て翻訳・公開いたします。
日本語タイトルは内容に即したものにしました。