`Date::DATE_FORMATS`/`Time::DATE_FORMATS` の非推奨化を改善し、依存関係を逆転させる

rails/rails

Date::DATE_FORMATSTime::DATE_FORMATS の非推奨化メカニズムを改善し、フォーマット定義の実体をコア拡張モジュールから ActiveSupport::DateFormats / ActiveSupport::TimeFormats へと移管しました。これにより、モジュール間の依存関係が正しい方向に整理されています。

背景

このPRは、#57345 で導入された ActiveSupport::TimeFormats / ActiveSupport::DateFormats の実装に対するフォローアップです。前回の実装では、フォーマット定義の実体が Date / Time クラスのコア拡張側に置かれたまま、ActiveSupport::DateFormats / ActiveSupport::TimeFormats がそこから参照するという構造になっていました。この設計では、フォーマットモジュールがコア拡張に依存するという逆転した依存関係が生じていました。

また、非推奨警告についても、どのAPIに移行すべきかがユーザーに伝わりにくいという問題が指摘されていました。

技術的な変更

依存関係の方向を逆転させ、フォーマット定義の実体を ActiveSupport::DateFormats / ActiveSupport::TimeFormats 側に移しました。

変更前の構造:

active_support/date_formats.rbactive_support/core_ext/date/conversionsrequire し、Date::DATE_FORMATS の実体ハッシュを参照していました。

# 変更前
require "active_support/core_ext/date/conversions"

module ActiveSupport
  module DateFormats
    @list = Date::DATE_FORMATS.dup.freeze
    @deprecated_list = Date::DATE_FORMATS
    Date.deprecate_constant :DATE_FORMATS
  end
end

変更後の構造:

フォーマット定義の実体が ActiveSupport::DateFormats / ActiveSupport::TimeFormats 側に移動し、コア拡張がフォーマットモジュールを require する形に逆転しました。

# 変更後
module ActiveSupport
  module DateFormats
    @list = {
      short: "%d %b",
      long: "%B %d, %Y",
      db: "%Y-%m-%d",
      inspect: "%Y-%m-%d",
      number: "%Y%m%d",
      long_ordinal: lambda { |date|
        day_format = ActiveSupport::Inflector.ordinalize(date.day)
        date.strftime("%B #{day_format}, %Y")
      },
      rfc822: "%d %b %Y",
      rfc2822: "%d %b %Y",
      iso8601: lambda { |date| date.iso8601 }
    }.freeze

    singleton_class.attr_reader :list

    DEPRECATED_LIST = @list.dup
  end
end

コア拡張側では、Date::DATE_FORMATS の実体ハッシュ定義を削除し、代わりに ActiveSupport::Deprecation::DeprecatedConstantAccessor を用いた非推奨定数宣言に置き換えました。

require "active_support/date_formats"

class Date
  include ActiveSupport::Deprecation::DeprecatedConstantAccessor

  deprecate_constant :DATE_FORMATS, "ActiveSupport::DateFormats::DEPRECATED_LIST",
    deprecator: ActiveSupport.deprecator,
    message: "Date::DATE_FORMATS is deprecated, to register custom time formats use ActiveSupport::DateFormats.register"
end

Time 側も同様に、active_support/time_formatsrequire して Time::DATE_FORMATSActiveSupport::TimeFormats::DEPRECATED_LIST へのエイリアスとして非推奨化しています。

非推奨後の後方互換性については、DEPRECATED_LIST への書き込みが to_fs 時に参照されることをテストで確認しています。

def test_deprecated_to_fs_custom_date_format
  assert_deprecated(ActiveSupport.deprecator) do
    Date::DATE_FORMATS[:custom] = "%B %Y"
  end
  assert_equal "February 2005", Date.new(2005, 2, 21).to_fs(:custom)
ensure
  ActiveSupport::DateFormats::DEPRECATED_LIST.delete(:custom)
end

設計判断

ActiveSupport.deprecator を使用した警告の統一と、移行先APIの明示という2点が今回の改善の核心です。

前回の実装では、Date.deprecate_constant :DATE_FORMATSActiveSupport::DateFormats モジュール内から呼び出していました。これは「フォーマットモジュールがコア拡張の初期化を担う」という設計上の不自然さを生んでいました。今回は ActiveSupport.deprecatordeprecate_constant:deprecator オプションに明示的に渡すことで、Railsの標準的な非推奨メカニズムに準拠しています。

DEPRECATED_LIST の設計も注目に値します。@list.dup で作成した可変なハッシュを定数として公開することで、既存コードが Date::DATE_FORMATS[:custom] = ... のように書き込んだ場合も to_fs から参照されるという後方互換性が維持されます。lookup メソッドが @list[format] || DEPRECATED_LIST[format] の順で探索することで、新APIと旧APIの共存を実現しています。

まとめ

この変更は、依存関係の方向を「フォーマットモジュール→コア拡張」から「コア拡張→フォーマットモジュール」へと正しく逆転させつつ、ActiveSupport.deprecator による標準的な警告と移行先APIの明示を組み合わせた実装です。既存コードへの後方互換性を保ちながら、Date::DATE_FORMATS / Time::DATE_FORMATS から ActiveSupport::DateFormats.register / ActiveSupport::TimeFormats.register への移行路を明確に提示しています。

記事メタデータ

Generated by:
Claude Sonnet 4.6 for DiffDaily
LLM Trace:
9c12233f

この記事はAIによって自動生成されています。内容の正確性については、必ずソースコードやPRを確認してください。

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
Gemini 2.5 Pro for DiffDaily

Review Criteria:

記事構成 ✓ PASS

Title, Context, Technical Detailの存在と明確さ

記事はリード文(総論)、背景・技術的な変更・設計判断(各論)、まとめ(結論)の3部構成を明確に満たしており、非常に分かりやすい構造です。

カスタムMarkdown構文 ✓ PASS

シンタックスハイライト・GitHubリンク記法の正確性

ファイル名付きシンタックスハイライト(`言語:ファイルパス`)およびGitHubのPR番号リンク記法(`[#123](URL)`)が正しく使用されています。

対象読者への適合性 ✓ PASS

エンジニア向けの適切な技術レベルと表現

依存関係の逆転、`ActiveSupport.deprecator`、コア拡張といった専門的な内容を扱っており、対象読者である専門知識を持つエンジニアに適した技術レベルです。

パラグラフ・ライティング ✓ PASS

トピックセンテンス・1段落1トピック・段落長

各セクションが総論→各論の構成になっており、各段落はトピックセンテンスで始まっています。1段落1トピックの原則が守られ、段落長も適切で可読性が高いです。

Diff内容との照合 ✓ PASS

コードブロックとDiff内容の一致

記事で引用されているすべてのコードブロックは、提供されたDiff情報と正確に一致しています。ファイル名、変更前後の内容ともに正しく反映されています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

「依存関係の逆転」「コア拡張」「非推奨化」などの技術用語が、文脈に沿って正確かつ適切に使用されています。

説明の技術的正確性 ✓ PASS

技術的主張の正確性と論理性

依存関係が逆転した点、`DEPRECATED_LIST`を用いて後方互換性を維持している点など、技術的な変更に関する説明はすべてDiffの内容と整合しており、論理的かつ正確です。

事実の突合 ✓ PASS

PR情報による主張の裏付け(ハルシネーション検出)

記事内のすべての主張(#57345のフォローアップであること、依存関係を逆転させたことなど)は、PRのDescriptionやDiffによって完全に裏付けられており、ハルシネーションは検出されませんでした。

数値・固有名詞の確認 ✓ PASS

PR番号・コミットID・バージョン等の正確性

PR番号(#57405, #57345)は正確に記載されています。

タイトル・説明との一致 ✓ PASS

記事タイトル・説明とPR内容の一致

記事のタイトルはPRのタイトル「Improve Date::DATE_FORMATS and Time::DATE_FORMAT deprecation」の内容を正確に反映し、さらにPRの主要な目的である「依存関係の逆転」という重要なコンテキストを加えており、適切です。

外部知識の正確性 ✓ PASS

PRに記載のない外部知識(LTS、サポート状況など)の不使用

記事はPRで言及されていない外部知識(バージョンのサポート状況やリリース日程など)を含んでおらず、提供された情報源に忠実です。

時間表現の正確性 ✓ PASS

時間表現がPR情報と一致しているか

「変更前」「変更後」「前回の実装」といった時間表現は、PRがフォローアップであるという文脈と一致しており、正確に使用されています。