深夜のオフィスで、日次集計の件数が1日ぶんずれていることに気づいて画面を確認している開発者

AIに日付・タイムゾーン処理を書かせる|ズレる定番バグの確認手順

金曜の夕方に「先週の集計、1件合わないんですけど」と言われて、コードを何度読んでも間違いが見つからない——そんな時間を過ごしたことはありませんか。

ロジックは正しい。テストも通っている。手元で動かすと、ちゃんと合う。なのに本番だけ、日をまたぐ時間帯のデータが隣の日に入っている。

日付の処理は、バグが出ても「壊れて見えない」のがやっかいなところです。例外も出ないし、画面も普通に表示される。ただ、深夜に登録された数件だけ静かに前日へ寄っている。気づくのは、たいてい誰かが数字を突き合わせたときです。

そして、AIに書かせたコードでこれが起きやすいのには、理由があります。AIは「いつ・どこの時刻か」を聞かずに、その場でいちばん自然な書き方を出してくるからです。悪気があるわけでも、能力が低いわけでもありません。前提を渡していないだけです。

結論:日付まわりは、AIの出力を疑うのではなく、渡す前提を先に決めておくのが近道です。順番はこう。①「保存はUTC、表示は利用者の時刻、変換は入口と出口だけ」という方針を先に決める。これを1行プロンプトに書くだけで、出てくるコードの質が変わります。②引数なしの now() と、時差の付いていない日時(naive datetime)を探す。事故のほとんどはこの2つです。③期間の絞り込みが「以上・未満」になっているか見る——23:59:59 を終端に使っていたら、そこは隙間が空いています。④月末・うるう日・年またぎの3つだけテストを足す。1年に数日しか起きない不具合は、テストでしか捕まりません。そして⑤DBの列型と、動いている環境のタイムゾーン設定を実物で確認する。コードが正しくても、ここがずれていると同じ症状が出ます。

一度に全部やらなくて大丈夫です。 今日は、直近でAIに書かせた日付処理をひとつ開いて、now() の呼び方を見るだけでも十分です。

なぜAIの日付コードは「だいたい動く」のか

日時データを保存するときと画面に表示するときで、時差の変換をどこで行うかを左から右へ3つの段階で示した図
変換するのは入口と出口だけ。真ん中を通っている間は、ひとつの基準にそろえておく

まず、いま何が起きているのかを整理させてください。

AIが日付の処理を書くとき、「この日時は、どこの時刻のつもりか」という情報は渡されていません。だから、いちばん短くて自然な書き方——つまり datetime.now()new Date() のような、その環境の設定に依存する書き方を出します。

これは、手元では正しく動きます。開発機のタイムゾーンは日本時間になっていることが多いからです。ところが本番のコンテナは、既定のままだと協定世界時(UTC。世界の時刻の基準)で動いていることがよくあります。同じコードが、置かれた場所によって9時間ちがう答えを返す。 これが「手元では合うのに本番だけずれる」の正体です。

似た形で、次のようなものが混ざりやすくなります。

大事なのは、これらはAIの失敗ではなく、前提が渡っていないだけだということです。人間の開発者に「日時を保存する処理を書いて」とだけ頼んでも、同じものが上がってきます。

① 先に方針を決めて、プロンプトに書く

いちばん効くのは、書かせる前です。コードを直すより速い。

決めるのは3つだけです。

この3つを、そのままプロンプトに書きます。

前提:
- 保存はUTC。DBの列は時差つきの型を使う。
- 表示は Asia/Tokyo。変換は画面に出す直前だけ。
- 時差の付いていない日時は作らない。
- 期間の絞り込みは「以上・未満」の半開区間で書く。
- 使うのは <言語・バージョン> と <日時ライブラリ>。他は使わない。

たった5行ですが、これがあるとないとで結果はかなり変わります。前提の渡し方そのものはAIにコードを書かせる前に渡す前提と制約|伝え方の型に詳しく書きました。

時差を「+9時間」と数字で固定しないほうが安全です。日本にいまサマータイムはありませんが、地名で指定しておけば、国や年による決まりの違いはライブラリ側が面倒を見てくれます。海外拠点や海外のお客さんが後から増えたときに、書き直す範囲がぐっと狭くなります。

② レビューで最初に見る2か所

上がってきたコードは、まずここを見ます。この2つで、事故のかなりの部分が見つかります。

引数なしの「今」

datetime.now()new Date()time.Now() のように、どこの時刻か指定せずに現在時刻を取る書き方を探します。検索で拾えるので、目視で全部読む必要はありません。

見つけたら、次のどちらかに直します。

あわせて、現在時刻を関数の中で直接呼ばない形にできると、あとが楽になります。時刻を引数で受け取る、または時計の役割をひとつ外から渡す。こうしておくと、「深夜0時ちょうど」のテストが普通に書けるようになります。テストできない時刻処理は、永遠に本番でしか確認できません

なお、言語やバージョンによっては、以前よく使われていた「世界時刻で今を取る関数」が非推奨になっていることがあります。AIは古い書き方を自然に出してくるので、使っている言語の公式ドキュメントで、いまの推奨形を一度確認してください。ここは更新が入る部分なので、記憶や過去の記事で決めないほうが安全です。

時差の付いていない日時

値を作っている場所と、外から受け取っている場所を見ます。

言語によっては、混ぜた時点でエラーになって助かります。逆に、黙って通ってしまう言語のほうが危ないとも言えます。

文字列からの変換では、もうひとつ知っておきたい癖があります。日付だけの文字列と、時刻まで含む文字列とで、解釈の基準が変わる言語があります。「2026-08-08」は世界時刻の深夜とみなすのに、「2026-08-08T00:00:00」はその環境の時刻とみなす、という形です。同じ日を表しているつもりが、9時間ずれる。 手で書いた文字列を日付に変換している場所があれば、そこは一度実際に動かして確かめる価値があります。

③ 期間の絞り込みは「以上・未満」で

これも定番です。「8月分」を出すつもりで、こう書かれているコード。

一見よさそうですが、23:59:59 と翌日0時のあいだに隙間があります。秒より細かい値を保存している列なら、23:59:59.5 に登録されたデータは、どちらの月にも入りません。

正しくは、次の期間の始まりを終端にして、そこは含めない書き方です。

この形なら、秒の細かさが変わっても、月の日数が違っても、そのまま成り立ちます。「以下」で終わる期間の条件を見かけたら、いったん立ち止まる——これはレビューの合図として覚えておいて損がありません。

もうひとつ。「今日」の範囲は、時差を決めてからでないと決まりません。日本時間の8月8日は、世界時刻では8月7日15時に始まります。日次の集計を世界時刻の0時で区切っていると、深夜0時から朝9時までのデータが前日に入ります。「1件合わない」の多くは、ここです。

SQLで直接絞り込んでいる場合の見方はAIに書かせたSQLの確認ポイント|N+1・インデックス漏れとあわせて確認すると早いです。

④ 月末・うるう日・年またぎだけテストする

日付のバグは、1年のうち数日しか顔を出しません。だから通常のテストをいくら足しても捕まらない。逆に言えば、その数日だけ狙えばいいということです。

まず足したいのは、この3つです。

  1. 月末をまたぐ:1月31日に「1か月後」を求めたら何になるか。2月31日は存在しないので、2月28日に丸めるのか、3月3日に送るのか、仕様として決まっている必要があります。AIはたいてい、どちらかを黙って選びます。どちらが正しいかは、あなたの業務が決めることです。
  2. うるう日:2月29日。ここは判定式そのものが間違っていることがあります。4で割り切れる年、ただし100で割り切れる年は除き、400で割り切れる年は含む——この3段目まで書かれているかを確認します。標準ライブラリの日付型を使っていれば自前で判定する必要はないので、手書きの判定式が出てきたら、そもそも要るのかを疑うのが早道です。
  3. 年またぎの深夜:12月31日23時台と、1月1日0時台。年をまたぐ瞬間は、書式の指定ミスが表に出る場所でもあります。日時を文字列にするときの年を表す記号を大文字と小文字で取り違えると、年末年始だけ1年ずれる——という有名な事故があります(言語によっては、大文字の年が「週を基準にした年」を意味します)。年末に「2027年」と印字された帳票が出て初めて気づく、という形です。

境界の考え方そのものは境界値・異常系をAIに考えさせて漏れを減らす手順が使えます。テスト自体をAIに書かせる場合は、「日本時間の8月8日0時5分」のように、時差を明記した具体的な時刻を渡すのがコツです。曖昧に頼むと、曖昧なテストが返ってきます。上がってきたテストの見方はAIが書いた単体テストを信用しすぎない|確認ポイントにまとめました。

⑤ コードの外——DBと環境の設定

ここまで直しても症状が消えないときは、コードの外を見ます。

DBまわりの相談をAIにするときの前提の渡し方はAIにDB設計を相談する|前提の渡し方と鵜呑みにしない確認を参考にしてください。

「営業日」「祝日」はAIに聞かない

もうひとつだけ、はっきり書いておきます。

祝日の一覧を、AIの記憶から出させないでください。 日本の祝日は法律で決まっていて、年によって日付が動くものがあり、過去には特定の年だけ移動した例もあります。AIはもっともらしい一覧を返しますが、それが今年の正しい一覧である保証はありません

締め日や納期の計算に祝日が関わるなら、内閣府が公表している一覧のような一次情報を取り込むか、保守されているライブラリを使う。そのうえで、年に一度、翌年分を更新する運用まで決めておきます。ここを人の記憶やAIの出力で埋めると、静かに間違い続けます。

AIに任せていいのは「営業日を数えるロジック」まで。どの日が休みかというデータは、人が用意する。この線引きだけ守れば大丈夫です。

つまずきやすいところ

先に知っておくと避けられるものを挙げます。どれも普通に踏むところで、責める意図はまったくありません。

明日からやること(まずこの3つ)

大がかりな改修は要りません。この順番で大丈夫です。

  1. 直近でAIに書かせた日付処理を1ファイル開いて、now() の呼び方を見る(15分):引数なしで呼んでいる場所を数えるだけでいい。ゼロなら、その時点でかなり安心です。
  2. 本番と開発のタイムゾーン設定を、実際に表示させて見比べる(15分):思い込みではなく実物で。ここが違っていたら、それだけで説明がつく不具合があるかもしれません。
  3. プロンプトに前提の5行を足して、次の実装から使う:保存・表示・変換・半開区間・使うライブラリ。一度書けば、あとはコピーするだけです。

一度で全部そろえなくて大丈夫です。 今日「本番はUTCで動いていた」と分かったなら、それはもう十分な収穫です。 原因が見えている不具合は、見えていない不具合よりずっと扱いやすくなります。

AI生成の日時コード チェックリスト

レビューのときにさっと確認します。全部に○が要るわけではなく、気になる所だけで十分です。

方針

コード

期間・計算

テスト

コードの外

この記事のまとめ

日付のバグを見逃したのは、注意力が足りなかったからではありません。日時は、書いた本人にも見えにくい前提を大量に抱えた領域で、それをAIに渡さないまま書かせれば、渡していない分だけ結果に幅が出る——それだけのことです。

やることは、地味です。保存と表示と変換の方針を先に決める。引数なしの「今」を探す。「以下」を「未満」に直す。月末とうるう日と年またぎだけテストする。環境の設定を実物で見る。 難しい日時ライブラリの知識は、この後で身につけても間に合います。

そして覚えておきたいのは、日付処理は一度型を決めれば、その先ずっと効くということです。今回決めた5行のプロンプトは、次の実装でもその次でも使えます。時間をかける価値がある、数少ない場所のひとつです。

月末と年またぎのテストがすべて通ったのを見て、ほっと表情がゆるんだ開発者
境界のテストが1回通れば、その先ずっと守ってくれる

「AIを活用しろ」と言われる立場にいると、新しく作ることばかりに目が向きがちです。でも、AIに書かせたコードの中で、あとから静かに効いてくる部分を先に押さえておく仕事は、たぶんいちばん割に合います。日付は、その代表格です。

今日、now() の呼び方をひとつ確かめたなら、それだけで前に進んでいます。何も問題が見つからなかったとしても、「ここは大丈夫」と言い切れる場所がひとつ増えた——それは、次に数字が合わなかったときに、あなたを助けてくれます。

よければ、こちらも

関連用語