Duration#in_* がサブ秒精度を切り捨てていた不具合を修正

rails/rails

ActiveSupport::Duration の in_* 系メソッドが秒未満の小数部分を失うバグを、内部計算を in_seconds から正確な value に変更し、サブ秒単位の精度を保持できるようにしました。これにより、ドキュメント通り「浮動小数点で返す」契約が全ての単位変換で満たされます。

背景

ActiveSupport::Duration#in_* 系メソッドは、内部で in_secondsto_i のエイリアス)を用いて秒単位に変換していました。to_i は整数化するため小数秒が切り捨てられ、結果として in_minutes から in_years までのメソッドがサブ秒精度を失っていました。例として、90.5.seconds.in_minutes1.5 と返り、本来期待される 1.508333333... と大きくずれていました。この挙動は「float を返す」ことがドキュメントで保証されているにも関わらず、実装上の不一致となっていました。

技術的な変更

activesupport/lib/active_support/duration.rbin_* メソッド群in_seconds から value に置き換えられました。value は元の @value(小数秒を含む)そのものを保持しているため、除算前に切り捨てが行われません。具体的な差分は以下の通りです。

変更前:

def in_minutes
  in_seconds / SECONDS_PER_MINUTE.to_f
end

変更後:

def in_minutes
  value / SECONDS_PER_MINUTE.to_f
end

同様の修正が in_hours, in_days, in_weeks, in_months, in_years にも適用されています。さらに、activesupport/test/core_ext/duration_test.rbtest_in_units_preserve_sub_second_precision が追加され、90.5 秒や 0.5 秒といったサブ秒を含むケースで期待精度が保たれることを自動テストで担保しています。

設計判断

in_seconds(整数化)をそのまま使用し続ける 方針は、in_seconds が整数を返すことを意図した API である点で正当です。一方、in_* 系は「float を返す」ことが求められるため、value を直接利用する 改善が最も自然な選択でした。新たなメソッドや設定キーを導入せず、既存のインターフェースと内部変数を再利用したため、後方互換性に影響はありません。既存の整数単位変換は依然として to_i の挙動を踏襲し、ドキュメントと実装の乖離が解消されたことが主な設計上の利点です。

まとめ

今回の修正は、ActiveSupport::Duration の in_* 系メソッドがサブ秒精度を保持できるように内部計算を変更した 低リスクのバグフィックス です。ドキュメント通りの浮動小数点結果が得られ、既存の整数返却メソッドへの影響はありません。追加されたテストにより、将来的な回帰も防止されています。

記事メタデータ

Generated by:
gpt-oss-120b for DiffDaily
LLM Trace:
edba5ff0

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

品質レビュー結果

Review Status:
承認済み
Review Count:
1回
Reviewed by:
gpt-oss-120b for DiffDaily

Review Criteria:

記事構成 ✓ PASS

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

リード文、背景、技術的な変更、設計判断(任意)、まとめが揃っており、総論→各論→結論の流れが明確です。

カスタムMarkdown構文 ⚠ WARNING

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

コードブロックのファイル名付きハイライトは正しい形式です。PRリンクは "[PR #57516]" となっており、仕様の "[#123]" 形式と乖離しています。

対象読者への適合性 ✓ PASS

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

エンジニア向けの技術的内容で、不要な初心者向け解説はありません。

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

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

各セクションが総論・各論・結論の構成を持ち、段落はトピックセンテンスで始まり1段落1トピック、長さも適切です。

Diff内容との照合 ✓ PASS

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

記事内のコードブロックはDiffと一致しており、変更点を正確に示しています。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

使用している技術用語はPRで使われているものと一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

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

バグの原因、修正内容、影響範囲の説明がPRの記述と整合しています。

事実の突合 ✓ PASS

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

全ての主張がPR情報(タイトル、Description、Diff)で裏付けられており、ハルシネーションはありません。

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

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

PR番号やコード中の数値は正確です。

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

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

記事タイトルはPRタイトルと意味的に一致しています。

外部知識の正確性 ✓ PASS

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

外部知識(バージョンサポート等)は一切含まれていません。

時間表現の正確性 ✓ PASS

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

時間表現の歪曲はありません。