
AIに日付・タイムゾーン処理を書かせる|ズレる定番バグの確認手順
金曜の夕方に「先週の集計、1件合わないんですけど」と言われて、コードを何度読んでも間違いが見つからない——そんな時間を過ごしたことはありませんか。
ロジックは正しい。テストも通っている。手元で動かすと、ちゃんと合う。なのに本番だけ、日をまたぐ時間帯のデータが隣の日に入っている。
日付の処理は、バグが出ても「壊れて見えない」のがやっかいなところです。例外も出ないし、画面も普通に表示される。ただ、深夜に登録された数件だけ静かに前日へ寄っている。気づくのは、たいてい誰かが数字を突き合わせたときです。
そして、AIに書かせたコードでこれが起きやすいのには、理由があります。AIは「いつ・どこの時刻か」を聞かずに、その場でいちばん自然な書き方を出してくるからです。悪気があるわけでも、能力が低いわけでもありません。前提を渡していないだけです。
結論:日付まわりは、AIの出力を疑うのではなく、渡す前提を先に決めておくのが近道です。順番はこう。①「保存はUTC、表示は利用者の時刻、変換は入口と出口だけ」という方針を先に決める。これを1行プロンプトに書くだけで、出てくるコードの質が変わります。②引数なしのnow()と、時差の付いていない日時(naive datetime)を探す。事故のほとんどはこの2つです。③期間の絞り込みが「以上・未満」になっているか見る——23:59:59を終端に使っていたら、そこは隙間が空いています。④月末・うるう日・年またぎの3つだけテストを足す。1年に数日しか起きない不具合は、テストでしか捕まりません。そして⑤DBの列型と、動いている環境のタイムゾーン設定を実物で確認する。コードが正しくても、ここがずれていると同じ症状が出ます。
一度に全部やらなくて大丈夫です。 今日は、直近でAIに書かせた日付処理をひとつ開いて、now() の呼び方を見るだけでも十分です。
なぜAIの日付コードは「だいたい動く」のか

まず、いま何が起きているのかを整理させてください。
AIが日付の処理を書くとき、「この日時は、どこの時刻のつもりか」という情報は渡されていません。だから、いちばん短くて自然な書き方——つまり datetime.now() や new Date() のような、その環境の設定に依存する書き方を出します。
これは、手元では正しく動きます。開発機のタイムゾーンは日本時間になっていることが多いからです。ところが本番のコンテナは、既定のままだと協定世界時(UTC。世界の時刻の基準)で動いていることがよくあります。同じコードが、置かれた場所によって9時間ちがう答えを返す。 これが「手元では合うのに本番だけずれる」の正体です。
似た形で、次のようなものが混ざりやすくなります。
- 時差の付いていない日時:「2026-08-08 01:02」とだけ持っている状態(naive datetime と呼ばれます)。これがどこの時刻なのかは、その値だけでは決められません。あとから足したり比べたりすると、静かにずれます。
- 人間向けの言葉をそのまま計算にした処理:「1か月後」「今月末」「先週」。日本語としては一意でも、プログラムとしては定義が要ります。1月31日の1か月後はいつなのか、決めるのは仕様であってライブラリではありません。
- 昔よく使われていた書き方:AIの学習には過去の大量のコードが入っています。いまは非推奨になった関数や、開発が止まっているライブラリが、自然な顔で出てくることがあります。この性質そのものはAIの知識は少し前で止まる|古い情報で最新仕様を扱う注意点にまとめました。
大事なのは、これらはAIの失敗ではなく、前提が渡っていないだけだということです。人間の開発者に「日時を保存する処理を書いて」とだけ頼んでも、同じものが上がってきます。
① 先に方針を決めて、プロンプトに書く
いちばん効くのは、書かせる前です。コードを直すより速い。
決めるのは3つだけです。
- 保存:データベースやログには、何を基準にして入れるか。協定世界時(UTC)にそろえるか、時差の情報を必ず一緒に持つか。どちらかにします。
- 表示:画面や帳票に出すとき、どこの時刻に直すか。国内向けなら日本時間(IANAタイムゾーン名で
Asia/Tokyo)。海外の利用者がいるなら、その人の設定に従うのか、会社の基準時刻に固定するのかを決めます。 - 変換:時差の足し引きは、入口と出口だけで行う。真ん中の処理では基準にそろった値のまま扱う。ここを守ると、あとで「どこかで二重に足している」を探す仕事がなくなります。
この3つを、そのままプロンプトに書きます。
前提:
- 保存はUTC。DBの列は時差つきの型を使う。
- 表示は Asia/Tokyo。変換は画面に出す直前だけ。
- 時差の付いていない日時は作らない。
- 期間の絞り込みは「以上・未満」の半開区間で書く。
- 使うのは <言語・バージョン> と <日時ライブラリ>。他は使わない。
たった5行ですが、これがあるとないとで結果はかなり変わります。前提の渡し方そのものはAIにコードを書かせる前に渡す前提と制約|伝え方の型に詳しく書きました。
時差を「+9時間」と数字で固定しないほうが安全です。日本にいまサマータイムはありませんが、地名で指定しておけば、国や年による決まりの違いはライブラリ側が面倒を見てくれます。海外拠点や海外のお客さんが後から増えたときに、書き直す範囲がぐっと狭くなります。
② レビューで最初に見る2か所
上がってきたコードは、まずここを見ます。この2つで、事故のかなりの部分が見つかります。
引数なしの「今」
datetime.now()、new Date()、time.Now() のように、どこの時刻か指定せずに現在時刻を取る書き方を探します。検索で拾えるので、目視で全部読む必要はありません。
見つけたら、次のどちらかに直します。
- 基準時刻で取る書き方(協定世界時を明示する形)にする。
- あるいは、地名つきのタイムゾーンを渡して取る。
あわせて、現在時刻を関数の中で直接呼ばない形にできると、あとが楽になります。時刻を引数で受け取る、または時計の役割をひとつ外から渡す。こうしておくと、「深夜0時ちょうど」のテストが普通に書けるようになります。テストできない時刻処理は、永遠に本番でしか確認できません。
なお、言語やバージョンによっては、以前よく使われていた「世界時刻で今を取る関数」が非推奨になっていることがあります。AIは古い書き方を自然に出してくるので、使っている言語の公式ドキュメントで、いまの推奨形を一度確認してください。ここは更新が入る部分なので、記憶や過去の記事で決めないほうが安全です。
時差の付いていない日時
値を作っている場所と、外から受け取っている場所を見ます。
- 文字列から日時を作るとき、時差の情報を捨てていないか。
- 外部APIやCSVから受け取った日時を、どこの時刻とみなしているか。
- 時差つきの値と、時差なしの値を混ぜて比較・計算していないか。
言語によっては、混ぜた時点でエラーになって助かります。逆に、黙って通ってしまう言語のほうが危ないとも言えます。
文字列からの変換では、もうひとつ知っておきたい癖があります。日付だけの文字列と、時刻まで含む文字列とで、解釈の基準が変わる言語があります。「2026-08-08」は世界時刻の深夜とみなすのに、「2026-08-08T00:00:00」はその環境の時刻とみなす、という形です。同じ日を表しているつもりが、9時間ずれる。 手で書いた文字列を日付に変換している場所があれば、そこは一度実際に動かして確かめる価値があります。
③ 期間の絞り込みは「以上・未満」で
これも定番です。「8月分」を出すつもりで、こう書かれているコード。
- 8月1日 00:00:00 以上、8月31日 23:59:59 以下
一見よさそうですが、23:59:59 と翌日0時のあいだに隙間があります。秒より細かい値を保存している列なら、23:59:59.5 に登録されたデータは、どちらの月にも入りません。
正しくは、次の期間の始まりを終端にして、そこは含めない書き方です。
- 8月1日 00:00:00 以上、9月1日 00:00:00 未満
この形なら、秒の細かさが変わっても、月の日数が違っても、そのまま成り立ちます。「以下」で終わる期間の条件を見かけたら、いったん立ち止まる——これはレビューの合図として覚えておいて損がありません。
もうひとつ。「今日」の範囲は、時差を決めてからでないと決まりません。日本時間の8月8日は、世界時刻では8月7日15時に始まります。日次の集計を世界時刻の0時で区切っていると、深夜0時から朝9時までのデータが前日に入ります。「1件合わない」の多くは、ここです。
SQLで直接絞り込んでいる場合の見方はAIに書かせたSQLの確認ポイント|N+1・インデックス漏れとあわせて確認すると早いです。
④ 月末・うるう日・年またぎだけテストする
日付のバグは、1年のうち数日しか顔を出しません。だから通常のテストをいくら足しても捕まらない。逆に言えば、その数日だけ狙えばいいということです。
まず足したいのは、この3つです。
- 月末をまたぐ:1月31日に「1か月後」を求めたら何になるか。2月31日は存在しないので、2月28日に丸めるのか、3月3日に送るのか、仕様として決まっている必要があります。AIはたいてい、どちらかを黙って選びます。どちらが正しいかは、あなたの業務が決めることです。
- うるう日:2月29日。ここは判定式そのものが間違っていることがあります。4で割り切れる年、ただし100で割り切れる年は除き、400で割り切れる年は含む——この3段目まで書かれているかを確認します。標準ライブラリの日付型を使っていれば自前で判定する必要はないので、手書きの判定式が出てきたら、そもそも要るのかを疑うのが早道です。
- 年またぎの深夜:12月31日23時台と、1月1日0時台。年をまたぐ瞬間は、書式の指定ミスが表に出る場所でもあります。日時を文字列にするときの年を表す記号を大文字と小文字で取り違えると、年末年始だけ1年ずれる——という有名な事故があります(言語によっては、大文字の年が「週を基準にした年」を意味します)。年末に「2027年」と印字された帳票が出て初めて気づく、という形です。
境界の考え方そのものは境界値・異常系をAIに考えさせて漏れを減らす手順が使えます。テスト自体をAIに書かせる場合は、「日本時間の8月8日0時5分」のように、時差を明記した具体的な時刻を渡すのがコツです。曖昧に頼むと、曖昧なテストが返ってきます。上がってきたテストの見方はAIが書いた単体テストを信用しすぎない|確認ポイントにまとめました。
⑤ コードの外——DBと環境の設定
ここまで直しても症状が消えないときは、コードの外を見ます。
- DBの列型:時差の情報を持つ型と、持たない型があります。製品によって名前も挙動も違い、同じ「日時」でも、接続の設定に応じて勝手に変換するものと、文字通り保存するものがあります。混在していると、経路によって値が変わります。使っている製品の公式ドキュメントで、いまの仕様を必ず確認してください。
- 接続のタイムゾーン設定:アプリからDBへつなぐときの設定。ここが環境ごとに違うと、コードが同じでも結果が変わります。
- 実行環境のタイムゾーン:コンテナやサーバーの設定。既定のままだと世界時刻のことがあります。「本番と開発で同じか」を実際に表示させて確かめるのがいちばん確実です。
- ログの時刻:調査のときに効いてきます。時差の情報が入った形で出ているか。複数のサーバーで基準がそろっているか。
DBまわりの相談をAIにするときの前提の渡し方はAIにDB設計を相談する|前提の渡し方と鵜呑みにしない確認を参考にしてください。
「営業日」「祝日」はAIに聞かない
もうひとつだけ、はっきり書いておきます。
祝日の一覧を、AIの記憶から出させないでください。 日本の祝日は法律で決まっていて、年によって日付が動くものがあり、過去には特定の年だけ移動した例もあります。AIはもっともらしい一覧を返しますが、それが今年の正しい一覧である保証はありません。
締め日や納期の計算に祝日が関わるなら、内閣府が公表している一覧のような一次情報を取り込むか、保守されているライブラリを使う。そのうえで、年に一度、翌年分を更新する運用まで決めておきます。ここを人の記憶やAIの出力で埋めると、静かに間違い続けます。
AIに任せていいのは「営業日を数えるロジック」まで。どの日が休みかというデータは、人が用意する。この線引きだけ守れば大丈夫です。
つまずきやすいところ
先に知っておくと避けられるものを挙げます。どれも普通に踏むところで、責める意図はまったくありません。
- 手元では合うのに本番だけずれる:まず環境のタイムゾーン設定を疑います。コードより先に、こちらです。
- 時差を数字で固定している:「+9時間」と直接書いてある形。国内だけなら当面動きますが、海外が絡んだ瞬間に破れます。地名で指定します。
- 二重に変換している:入口でも出口でも足していて、18時間ずれる。変換する場所を1か所に決めます。
23:59:59が終端になっている:隙間が空いています。「未満」に直します。- 1か月後の仕様が決まっていない:コードを読めば挙動は分かりますが、それが意図した挙動かどうかは誰も確認していない、という状態がいちばん危ない。
- 秒とミリ秒の取り違え:言語によって、通し番号の時刻が秒だったりミリ秒だったりします。1000倍ずれると、1970年や遠い未来の日付として現れます。
- 開発が止まったライブラリを勧められる:日時ライブラリは移り変わりが早い分野です。新規に入れる前に、そのライブラリの現在の状況を公式サイトで確認してください。
- 表示だけ直して保存を直さない:画面は合ったのに、データは間違ったまま溜まり続ける。保存されている値を先に見ます。
明日からやること(まずこの3つ)
大がかりな改修は要りません。この順番で大丈夫です。
- 直近でAIに書かせた日付処理を1ファイル開いて、
now()の呼び方を見る(15分):引数なしで呼んでいる場所を数えるだけでいい。ゼロなら、その時点でかなり安心です。 - 本番と開発のタイムゾーン設定を、実際に表示させて見比べる(15分):思い込みではなく実物で。ここが違っていたら、それだけで説明がつく不具合があるかもしれません。
- プロンプトに前提の5行を足して、次の実装から使う:保存・表示・変換・半開区間・使うライブラリ。一度書けば、あとはコピーするだけです。
一度で全部そろえなくて大丈夫です。 今日「本番はUTCで動いていた」と分かったなら、それはもう十分な収穫です。 原因が見えている不具合は、見えていない不具合よりずっと扱いやすくなります。
AI生成の日時コード チェックリスト
レビューのときにさっと確認します。全部に○が要るわけではなく、気になる所だけで十分です。
方針
- 保存する基準(世界時刻にそろえる/時差を必ず持つ)が決まっているか
- 表示するときの時刻(地名つきの指定)が決まっているか
- 変換する場所が入口と出口の1か所ずつに絞られているか
- その方針をプロンプトに書いてから書かせたか
コード
- 引数なしで現在時刻を取っている場所がないか
- 時差の付いていない日時を作っていないか
- 時差ありと時差なしを混ぜて比較・計算していないか
- 時差を数字で直接足し引きしていないか
- 文字列から日時に変換している場所の解釈を確かめたか
- 現在時刻を外から差し替えられる形になっているか
期間・計算
- 期間の絞り込みが「以上・未満」になっているか
- 「今日」の範囲を、どこの時刻で区切るか決めているか
- 「1か月後」「月末」の定義が仕様として決まっているか
- 手書きのうるう年判定が紛れ込んでいないか
- 秒とミリ秒を取り違えていないか
テスト
- 月末をまたぐケースを試したか
- うるう日(2月29日)を試したか
- 年またぎの深夜を試したか
- 日をまたぐ時間帯(日本時間の0時〜9時)を試したか
コードの外
- DBの列型が、時差の扱いについて意図どおりか
- DB接続のタイムゾーン設定を確認したか
- 本番と開発の環境設定が同じか、実物で確かめたか
- 祝日データを一次情報から取っているか(AIの記憶に頼っていないか)
- ログの時刻に時差の情報が入っているか
この記事のまとめ
日付のバグを見逃したのは、注意力が足りなかったからではありません。日時は、書いた本人にも見えにくい前提を大量に抱えた領域で、それをAIに渡さないまま書かせれば、渡していない分だけ結果に幅が出る——それだけのことです。
やることは、地味です。保存と表示と変換の方針を先に決める。引数なしの「今」を探す。「以下」を「未満」に直す。月末とうるう日と年またぎだけテストする。環境の設定を実物で見る。 難しい日時ライブラリの知識は、この後で身につけても間に合います。
そして覚えておきたいのは、日付処理は一度型を決めれば、その先ずっと効くということです。今回決めた5行のプロンプトは、次の実装でもその次でも使えます。時間をかける価値がある、数少ない場所のひとつです。

「AIを活用しろ」と言われる立場にいると、新しく作ることばかりに目が向きがちです。でも、AIに書かせたコードの中で、あとから静かに効いてくる部分を先に押さえておく仕事は、たぶんいちばん割に合います。日付は、その代表格です。
今日、now() の呼び方をひとつ確かめたなら、それだけで前に進んでいます。何も問題が見つからなかったとしても、「ここは大丈夫」と言い切れる場所がひとつ増えた——それは、次に数字が合わなかったときに、あなたを助けてくれます。