テンプレートの全景スクショをレビューしていて見つけた表示バグの記録です。修正済み。
何が起きていたか
「柱のきず」という作品テンプレート(写真の並べ方の型)には、写真と写真の間に経過時間のラベルが出ます。背比べの柱に刻みを増やしていくイメージで、「それから 約11年12ヶ月」のように表示されます——って、11年12ヶ月は12年です。
2014年の写真と2026年の写真の間——ちょうど12年になる少し手前で、この表示が出ていた。
原因: 月の計算が12に到達できてしまう
そもそもなぜ 365 や 30 で割っているかというと、ここで出したいのが正確な年月差ではなく「約◯年◯ヶ月」という体感の粒度だからです。暦どおりの月差を求めるライブラリもあるが、柱に刻む一言に月末や閏年の厳密さは要りません。
経過ラベルを組み立てる処理は、ざっくりこうなっていた。
const years = Math.floor(days / 365); // 365で割って切り捨て = 何年ぶんか
const remMonths = Math.round((days % 365) / 30); // ← ここ。年で割った「余りの日数」を30で割って四捨五入 = 何ヶ月ぶんかdays % 365 は最大364です。余りが345日を超えたあたりから月が12になってしまいます(345 ÷ 30 = 11.5 → 四捨五入で12)。
この割り算による近似は、単位ごとの上限を自分では持っていません。「月は11までしかない」という人間側の約束は、後から手で足さないとどこにも書かれていません。
直し方
丸めた結果が12に達したら年へ繰り上げる、という条件分岐(if)を足しただけ。
// remMonths が12に達したら年を+1して月を0に戻す=「約12年」と言い切る
if (remMonths >= 12) return `それから 約${years + 1}年`;約12年0ヶ月 ではなく月ごと落として 約12年 にしたのは、もともと「約」の表示だから。繰り上がった時点で端数は誤差に沈んでいるので、0を表示するより言い切ったほうが読み手に嘘がありません。
再発防止に、境界値(remMonths が12になる日数)のテストケースも追加した。使ったのは 4,378日 = 2010.01.01 → 2021.12.27 の実測値。修正・テストとも AI に書いてもらい、自分は境界の考え方が合っているかを確認した。
学んだこと
- 人間向けの「読みやすい時間表示」で丸めを書くときは、繰り上がりの境界を先に潰しておきます。今回踏んだのは「11ヶ月→1年」だが、「59秒→1分」も同じ形で踏めそうだと後から思った
- このバグは単体テストでは見つからず、本物の写真データを流し込んで描画させた画面のスクショから見つかった。テストは自分が想定した範囲しか見てくれないので、想定そのものが抜けていた今回は、画面を目で見るまで気づけなかった。今回足した境界値テストも発見には一切寄与していなくて、二度と踏まないための後追いの杭でしかない
この記事は2026年7月時点の自分の理解で書いています。仕様や認識が変わってる可能性があります。