海外の売上CSVをAIで日別集計するときは、「どの時刻を、どの地域の日付として数えるか」を先に決めます。UTCの文字列から日付だけ切り出す方法と、現地時刻へ変換してから日付を取る方法では、同じデータでも日別の合計が変わります。
この記事では、UTCで記録された架空の売上5件を、ニューヨークの暦日へ変換します。夏時間が終わる日に同じ1時30分が二度現れる例を使い、ID、時差、対象期間を残した確認表を作ります。実売上やAIサービスを使った分析結果ではありません。
目次
売上の日付を決める列と地域を固定する
注文、決済、出荷、返金の時刻は別のイベントです。今回は「決済が確定した時刻」を集計する前提とし、すべて同一通貨USD、金額はこの説明用データの値をそのまま合計します。税、為替、返金、キャンセルの会計処理を決める記事ではありません。
元の時刻はUTCと定義し、集計先はAmerica/New_Yorkとします。端末の現在地や、CSVを開いた人のタイムゾーンで日付を決めません。店舗ごとに地域が違う場合は、一つの地域へまとめるのか、店舗別の営業日を使うのかを先に決めます。
表は横にスクロールできます
| 先に決める項目 | 今回の例 |
|---|---|
| レコードの単位 | 一つの確定決済、一つのevent_id |
| 元の時刻 | paid_at_utc。末尾ZはUTCとして扱う |
| 集計する日付 | America/New_Yorkの暦日 |
| 日付の境界 | 現地0時以上、翌日0時未満 |
| 金額 | 全件USD。今回は除外・返金・換算なし |
現地の午前5時を締めにするなど、業務で別の境界を使う場合は今回と異なります。「現地日付にしたから正しい」とせず、レポートの定義へ合わせます。
元の時刻を残したまま、変換列を追加する
次が架空の入力CSVです。event_idは時刻から作らず、元データの識別子を使います。
event_id,paid_at_utc,amount_usd
E01,2025-11-02T03:30:00Z,100
E02,2025-11-02T05:30:00Z,200
E03,2025-11-02T06:30:00Z,300
E04,2025-11-03T04:30:00Z,400
E05,2025-11-03T05:00:00Z,500
PythonのzoneinfoはIANAの地域別タイムゾーンデータを利用します。UTCなど別のタイムゾーンから変換すると、切替前後の時差に応じた現地時刻を得られます。今回の変換はローカルのPythonと時刻データで計算しました。Python公式:zoneinfo(新しいタブで開く)
表は横にスクロールできます
| ID | UTCの元時刻 | 現地日時とUTCオフセット | 集計日付 | USD |
|---|---|---|---|---|
| E01 | 2025-11-02 03:30Z | 2025-11-01 23:30 −04:00 | 2025-11-01 | 100 |
| E02 | 2025-11-02 05:30Z | 2025-11-02 01:30 −04:00 | 2025-11-02 | 200 |
| E03 | 2025-11-02 06:30Z | 2025-11-02 01:30 −05:00 | 2025-11-02 | 300 |
| E04 | 2025-11-03 04:30Z | 2025-11-02 23:30 −05:00 | 2025-11-02 | 400 |
| E05 | 2025-11-03 05:00Z | 2025-11-03 00:00 −05:00 | 2025-11-03 | 500 |
E01はUTCでは11月2日ですが、今回の集計地域では11月1日です。E04は反対にUTCでは11月3日でも、現地では11月2日に入ります。元列を上書きせず、現地日時、地域名、集計日付を追加すれば、どの行が日付をまたいだか追えます。
同じ現地時刻を、重複レコードと決めない
E02とE03はどちらも現地の11月2日1時30分です。ただしUTCでは1時間違い、UTCオフセットも−04:00と−05:00で異なります。夏時間終了時の時計の戻りによるもので、時刻の見た目だけを理由に片方を削除しません。
この例はUTCの瞬間が元データにあるため、両者を区別できます。一方、「2025-11-02 01:30」という地域もオフセットもない値だけでは、どちらの瞬間かを一意に決められません。地域名が分かっても切替時の曖昧さが残る場合があります。記録元の仕様や追加情報を確認し、勝手に先の時刻・後の時刻へ寄せないでください。
Pythonにはこうした重なる現地時刻を区別するfoldの仕組みがありますが、値を選ぶ根拠が元データにない問題まで解決するわけではありません。時刻の解析失敗や地域不明の行は確認表へ残し、ゼロ件や対象外へ黙って置き換えません。
暦日をUTCの区間へ戻して境界を確認する
今回の地域の2025年11月2日は、次のUTC区間に対応します。開始を含み、終了を含まない区間です。
開始:2025-11-02T04:00:00Z 以上
終了:2025-11-03T05:00:00Z 未満
対象:E02、E03、E04
合計:200 + 300 + 400 = 900 USD
開始から終了までの経過時間は25時間です。現地の一日を常にUTCの24時間として切ると、この日に必要な範囲と一致しません。対象日の現地0時と翌日0時をそれぞれUTCへ変換してから、期間を確認します。
E05は終了境界と同じ05:00Zなので、11月2日の合計には入りません。23時59分59秒までの条件で切るより、翌日0時未満とする方が、小数秒のあるデータにも同じ境界を説明できます。
UTC集計と現地集計の違いを、全体合計でも照合する
同じ5件を二通りで集計すると、次のようになります。空欄の代わりの0は、この架空データ内で該当件数がないことを表すもので、実データの欠損補完ではありません。
表は横にスクロールできます
| 日付 | UTC日付で合計 | 現地日付で合計 |
|---|---|---|
| 2025-11-01 | 0 | 100 |
| 2025-11-02 | 600 | 900 |
| 2025-11-03 | 900 | 500 |
| 全5件合計 | 1,500 | 1,500 |
日別の差は、E01の100とE04の400が別の日へ移ったことで説明できます。全体合計が1,500で一致しても、日別の分類が正しい証拠にはなりません。反対に、日別合計だけを見ると、取引が消えたのか別の日へ移ったのか区別できません。ID別の変換表、日別集計、全体件数と金額を一緒に照合します。
実データで変換できない行があれば、集計対象と確認待ちを分けます。入力件数=集計した件数+確認待ち件数など、今回採用した処理区分で照合できるようにします。確認待ちの金額も、読み取れる範囲を別に記録します。
AIへは、日付の定義と確認表を指定する
このCSVの日別売上を確認してください。
元時刻paid_at_utcはUTC、集計地域はAmerica/New_Yorkです。
paid_at_utcを現地日時へ変換してから暦日を取得してください。
境界は現地0時以上、翌日0時未満です。
元IDと元時刻を保持し、地域名・UTCオフセット付き現地日時・集計日付を追加。
同じ現地時刻という理由だけでレコードを重複削除しないでください。
地域やオフセットが不明な時刻、解析できない行は推測せず確認待ちにしてください。
出力:ID別変換表、日別件数と合計、対象日のUTC境界、確認待ち一覧。
全体件数・金額も照合してください。今回の金額は全件USDです。
使用した計算方法とタイムゾーンデータの情報を示してください。
実行していない計算を、検証済みの結果として説明しないでください。
入力:[利用が認められたCSV]
日時を変換せず、タイムゾーン名だけを付け直す操作にも注意します。Pythonのastimezoneは同じ瞬間を保って別の地域へ変換するための方法です。単にtzinfoを置き換える操作とは役割が異なります。Python公式:datetime(新しいタブで開く)
地域の時差や夏時間の規則は変更されることがあります。IANAのデータベースもその変更を反映して更新されるため、計算環境のデータを記録します。公式サイトの最新版を見たことと、自分の環境がその版を使っていることは別です。IANA:Time Zones(新しいタブで開く)
CSV分析の道具選びは、CSVを分析するAIの選び方で扱います。日別の数値を受け渡すときは、集計地域、対象のイベント時刻、日付境界、確認待ち、元IDへの対応まで残すと、翌月の再集計でも同じ定義を使えます。
参考資料
公式資料の確認日:2026年10月20日。売上5件は架空例で、日時変換と合計をローカルで計算しました。実売上の定義、実システムの記録仕様、AIサービスの出力、表計算画面の表示は確認していません。
