ISO週番号年境界で無効な week 値が生成されるバグを修正

rails/rails

<input type=\"week\"> が受け付ける文字列は ISO 8601 の 週番号年 (yyyy-Www) である必要があります。この PR は、Rails の WeekField が年末・年始の境界で誤った年を付与し、ブラウザに拒否される文字列を出力していた問題を修正します。

背景

WeekField の内部で format_datetimestrftime("%Y-W%V") を使用していたため、カレンダー年 (%Y) と ISO 週番号 (%V) が組み合わされました。ISO 週番号は前年または翌年に属することがあるため、例えば Date.new(2016, 1, 1)"2016-W53" とフォーマットされ、実際には存在しない週文字列となります。この不整合はブラウザ側でバリデーションエラーを引き起こします。

既存の week_field テストは中間期の日付(%Y == %G)しかカバーしておらず、境界ケースが検出されていませんでした。問題の根本は年情報の指示子が不適切だったことにあります。

技術的な変更

WeekField#format_datetime の実装を strftime("%Y-W%V") から strftime("%G-W%V") に変更しました。%G は ISO 週番号年を返す指示子であり、%V と組み合わせることで常に正しい年・週の組み合わせが得られます。変更前後のコードは以下の通りです。

- value&.strftime("%Y-W%V")
+ value&.strftime("%G-W%V")

さらに、境界ケースを検証するテストが追加されました。DateTime.new(2016, 1, 1, 1, 2, 3)"2015-W53" と正しくフォーマットされることを確認します。

+ def test_week_field_with_iso_week_year_boundary
+   expected = %{<input id="post_written_on" name="post[written_on]" type="week" value="2015-W53" />}
+   @post.written_on = DateTime.new(2016, 1, 1, 1, 2, 3)
+   assert_dom_equal(expected, week_field("post", "written_on"))
+ end

これにより、WeekField が全ての有効な日付に対して正しい ISO 週文字列を生成することが保証されます。

設計判断

WeekFieldDatetimeField 系列のサブクラスであり、他のフィールド(DateFieldMonthFieldDatetimeLocalField)は既に指示子を一貫させています。今回の修正は、同様の設計方針に合わせて %Y%G に置き換えるという最小限の変更です。既存の API(week_field の呼び出し方)は保持され、valuenil の場合も同様に安全です。境界外の日付に対する挙動は変わらず、正しい年・週の組み合わせが得られるようになりました。

まとめ

WeekField#format_datetime が ISO 週番号年境界で誤った文字列を生成していたバグを、%Y%G の置換で解消し、境界テストを追加しました。これにより <input type=\"week\"> が常に有効な yyyy-Www 形式を出力し、ブラウザ側のバリデーションエラーを防ぎます。既存の挙動は保持され、コードベース全体の一貫性も高まりました。

記事メタデータ

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

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

品質レビュー結果

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

Review Criteria:

記事構成 ✓ PASS

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

リード文、背景、技術的変更、設計判断、まとめの5セクションが揃っており、まとめはリード文の単なる繰り返しではなく結論として機能しています。

カスタムMarkdown構文 ⚠ WARNING

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

コードブロックは正しい `言語:ファイルパス` 形式です。PRリンクは `[PR #57890](URL)` と記述されており、#付きリンクはあるものの「PR 」という前置詞が含まれているため完全一致ではなく警告としました。

対象読者への適合性 ✓ PASS

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

Rails の内部実装に関する専門的な内容で、初心者向けの余計な解説はなく、エンジニア向けとして適切です。

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

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

各セクションは総論→各論→結論の流れを保ち、段落はトピックセンテンスで始まり1段落1トピック、6文以内に収まっています。空行で区切りも適切です。

Diff内容との照合 ✓ PASS

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

記事中のコードブロックは提供された Diff と完全に一致し、ファイル名・変更内容・追加テストとも相違がありません。

技術用語の正確性 ✓ PASS

技術用語の正確な使用

`%Y`, `%G`, `%V`, `WeekField`, `format_datetime` などの用語は PR と一致し、誤用はありません。

説明の技術的正確性 ✓ PASS

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

技術的な説明は PR の記述と合致し、因果関係も論理的です。

事実の突合 ✓ PASS

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

全ての主張は PR タイトル、説明、Diff に裏付けられており、推測や外部知識の付加はありません。

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

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

PR 番号 #57890、テストで期待される文字列 `2015-W53` など、数値・固有名詞は正確です。

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

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

記事タイトルは PR の内容(ISO 年境界での無効な week 文字列修正)を正確に表現しています。

外部知識の正確性 ✓ PASS

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

記事内に PR には記載されていないバージョンサポート情報やリリーススケジュール等の外部知識は含まれていません。

時間表現の正確性 ✓ PASS

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

時間表現の歪曲はなく、PR の記述と一致しています。