DESIGN / CODE / INFRA / AI

解説

AIに日別集計を頼む前に:UTCと日本時間の日付境界をそろえよう

同じ6件の日時でも、UTCと日本時間では日別件数が変わる。変換してから日付を取り出す順番、翌日0時を含めない範囲、確認用IDを付けた依頼文を整理する。

AIに日別集計を頼む前に:UTCと日本時間の日付境界をそろえよう
保存リストを見る →
この記事の道しるべ 5項目

AIへ日時の一覧を渡し、「27日の件数を数えて」と頼んだ。同じ元データなのに、確認した集計と数が違う。日付の文字だけを切り出し、タイムゾーンの変換を飛ばしていないか確認しよう。

この記事では架空の6件を使い、同じ瞬間をUTCと日本時間でどう数えるか比べる。実際の顧客データやログインは不要。AIの回答を実測した記事ではなく、依頼条件と正解を照合する練習だよ。

「27日」がどの地域の日付かを決める

UTCは時刻の基準、日本時間はUTCより9時間進んだ時刻。ISO形式の日時では、末尾のZがUTCを、+09:00がUTCから9時間進んだオフセットを表す。

2026-10-26T15:00:00Zは、日本時間では2026-10-27の0時。同じ瞬間でも、日付だけを見ると26日と27日に分かれるよ。日本時間の日別集計なら、先に日時を日本時間へ変換してから、日付を取り出す必要がある。

ID 元の日時 日本時間へ変換した日時
A 2026-10-26T14:59:59Z 10月26日23:59:59
B 2026-10-26T15:00:00Z 10月27日00:00:00
C 2026-10-26T23:30:00Z 10月27日08:30:00
D 2026-10-27T09:00:00+09:00 10月27日09:00:00
E 2026-10-27T23:59:59+09:00 10月27日23:59:59
F 2026-10-28T00:00:00+09:00 10月28日00:00:00

Dのように最初から日本時間の日時へ、さらに9時間を足すと誤りになる。元のオフセットを含めて日時として解釈し、対象のタイムゾーンへ変換しよう。

日本時間の27日は、B・C・D・Eの4件

今回は各IDが別のイベントを1件表す。ユーザー数や重複排除後の件数を数える例ではないよ。

日本時間でまとめると、26日は1件、27日は4件、28日は1件。UTCへそろえてから日付を取ると、26日が3件、27日が3件になる。片方が必ず間違いなのではなく、集計の基準が違うんだ。

集計表の見出しにも「日付(日本時間)」のように基準を添えたい。同じデータから作った表でも、基準を書かずに並べると、増減があったように読み違えてしまう。

翌日の0時を両方の日へ入れない

日本時間の27日を範囲で示すなら、27日0時以上、28日0時未満とする。開始を含み、終了を含めない区切りだよ。

UTCの値で表すと、2026-10-26T15:00:00Z以上、2026-10-27T15:00:00Z未満。Fはちょうど終了境界なので、27日の4件には含めない。

23時59分59秒まで、とだけ書くと、小数秒のあるデータで意図が曖昧になることがある。日時の精度を決め、次の日の開始より前、という条件で示すと境界を確かめやすい。

AIへの依頼には、変換後の表と確認用IDも求める

以下は、今回の架空データに対する依頼文の例。

各IDは別イベント1件です。日時のZと+09:00を解釈し、
すべて日本時間(UTC+09:00)へ変換してから日別に数えてください。
元日時の先頭10文字でグループ化しないでください。
出力は「ID・元日時・日本時間の日時」と「日付・件数・対象ID」。
27日は27日0時以上、28日0時未満です。
オフセットのない日時は推測で処理せず、確認が必要として分けてください。

日別の合計だけでなく対象IDを出すと、どの行が別の日へ入ったかをたどれる。ただし、AIが付けた内訳も自動的に正しいとは限らない。元データと境界条件で照合しよう。

OpenAIの公式ガイドは、明確な指示や例を用いた依頼の組み立てを説明している。ここに載せた依頼文や正解の6件は編集部が作ったもので、特定モデルが必ず正しく数えると確認したものではないよ。

Pythonで独立して確認する小さな例

次はPython 3.12で確認した例。日時のオフセットを保持したまま読み、日本時間へ変換する。

from collections import Counter
from datetime import datetime, timezone, timedelta

jst = timezone(timedelta(hours=9))
rows = [
    ("A", "2026-10-26T14:59:59Z"),
    ("B", "2026-10-26T15:00:00Z"),
    ("C", "2026-10-26T23:30:00Z"),
    ("D", "2026-10-27T09:00:00+09:00"),
    ("E", "2026-10-27T23:59:59+09:00"),
    ("F", "2026-10-28T00:00:00+09:00"),
]
counts = Counter()
for event_id, value in rows:
    moment = datetime.fromisoformat(value.replace("Z", "+00:00"))
    if moment.utcoffset() is None:
        raise ValueError("タイムゾーンの確認が必要")
    local = moment.astimezone(jst)
    print(event_id, local.isoformat())
    counts[local.date().isoformat()] += 1
print(dict(sorted(counts.items())))

期待する日別件数は、2026-10-26が1、2026-10-27が4、2026-10-28が1。編集部では変換結果、UTCでの3件・3件、開始・終了の境界も別途照合した。任意の地域の夏時間まで、この固定+09:00の例で扱えるという意味ではないよ。

日時の表記の読み方はUTCと日本時間の基礎、日付の含め方の問題は期間の開始日・終了日も参照してね。

参考・出典

公開と変更の記録
  • :初回公開

日時は日本時間です。記載のある変更は、確認できた編集記録に基づきます。更新日の違いだけから変更内容を推測して載せることはありません。

更新・訂正の方針 →

みんなのコメント 0

感想や、試してみたことを聞かせてね。コメント・返信は承認後に公開されます。

最初のコメントをどうぞ。