17時締切の受付ログ6件で、遅延がどちらも2件なのに対象者が入れ替わるのはなぜでしょうか。
手元で試したとき、集計が2件で揃って一瞬安心しました。日時の先頭19文字だけを比べると、103番を見逃し、間に合った105番を遅れにします。合計とIDの両方を確かめたいです。
架空の申込6件をPythonで試しました。受付ログに Z(UTC)、+09:00、+10:00 が混在する想定です。
6件のうち、問題は103番と105番
締切は2026年9月25日17:00:00、日本時間。17:00:00は間に合い、17:00:01は遅れとします。実際の募集では、先に受付ルールを確かめてください。
| ID | ログの時刻 | 日本時間に直すと | 判定 |
|---|---|---|---|
| 101 | 07:59:59Z |
16:59:59 | 間に合う |
| 102 | 08:00:00Z |
17:00:00 | 間に合う |
| 103 | 08:00:01Z |
17:00:01 | 遅れ |
| 104 | 17:00:00+09:00 |
17:00:00 | 間に合う |
| 105 | 18:00:00+10:00 |
17:00:00 | 間に合う |
| 106 | 18:00:01+10:00 |
17:00:01 | 遅れ |
103番の 08:00:01Z は日本時間で17時を1秒過ぎています。105番の 18:00:00+10:00 は日本時間で17時ちょうど。文字だけを切り出すと扱いが逆になります。
ここ地味に効きます。集計表の「遅延2件」だけでは、誤りを発見できません。
Pythonで時刻を揃えてから比べる
下のコードはそのまま実行できます。例のCSVはコード内に入れました。先頭19文字で比べた結果も並べます。
import csv
import io
from datetime import datetime, timedelta, timezone
SAMPLE = """id,received_at
101,2026-09-25T07:59:59Z
102,2026-09-25T08:00:00Z
103,2026-09-25T08:00:01Z
104,2026-09-25T17:00:00+09:00
105,2026-09-25T18:00:00+10:00
106,2026-09-25T18:00:01+10:00
"""
JST = timezone(timedelta(hours=9))
DEADLINE = datetime(2026, 9, 25, 17, 0, 0, tzinfo=JST)
DEADLINE_UTC = DEADLINE.astimezone(timezone.utc)
def parse_received(raw):
dt = datetime.fromisoformat(raw.strip().replace("Z", "+00:00"))
if dt.utcoffset() is None:
raise ValueError(f"時差のない日時です: {raw}")
return dt.astimezone(timezone.utc)
rows = list(csv.DictReader(io.StringIO(SAMPLE)))
if not rows or set(rows[0]) != {"id", "received_at"}:
raise ValueError("CSVの列は id,received_at の2列にしてください")
print("ID JST時刻 文字だけ 正しい判定")
wrong_ids = []
correct_ids = []
for row in rows:
raw = row["received_at"]
received_utc = parse_received(raw)
is_late = received_utc > DEADLINE_UTC
looks_late = raw[:19] > DEADLINE.isoformat()[:19]
if looks_late:
wrong_ids.append(row["id"])
if is_late:
correct_ids.append(row["id"])
print(f"{row['id']} {received_utc.astimezone(JST):%Y-%m-%d %H:%M:%S} "
f"{'遅れ' if looks_late else '間に合う':<4} "
f"{'遅れ' if is_late else '間に合う'}")
print("文字だけの遅延ID:", ",".join(wrong_ids))
print("正しい遅延ID:", ",".join(correct_ids))
try:
parse_received("2026-09-25T08:00:01")
except ValueError as exc:
print("時差欠落:", exc)
実行結果はこうなりました。
ID JST時刻 文字だけ 正しい判定
101 2026-09-25 16:59:59 間に合う 間に合う
102 2026-09-25 17:00:00 間に合う 間に合う
103 2026-09-25 17:00:01 間に合う 遅れ
104 2026-09-25 17:00:00 間に合う 間に合う
105 2026-09-25 17:00:00 遅れ 間に合う
106 2026-09-25 17:00:01 遅れ 遅れ
文字だけの遅延ID: 105,106
正しい遅延ID: 103,106
時差欠落: 時差のない日時です: 2026-09-25T08:00:01
同じ件数になった理由
raw[:19] は Z や +10:00 を捨てます。103番は「08:00:01」で早着、105番は「18:00:00」で遅延に見えます。見逃し1件と誤判定1件が打ち消し合い、合計は2件のままです。
datetime.fromisoformat() で時差を読み、受付時刻も締切もUTCへ直して比較します。Z は読み込み前に +00:00 へ置換。表示だけ日本時間へ戻しました。
時差のない値は止めます。2026-09-25T08:00:01 がUTCか日本時間か分からないためです。実データでエラーが出たら、出力元の設定を確認します。
自分のCSVへ替えるとき
上の rows = ... を次の3行に替えます。CSVの列名は id,received_at、文字コードはUTF-8の例です。
from pathlib import Path
with Path("applications.csv").open(encoding="utf-8-sig", newline="") as f:
rows = list(csv.DictReader(f))
締切は DEADLINE で変えます。受付ログには、2026-09-25T08:00:01Z のような時差つきの値を入れます。ファイルの更新日時は使いません。再ダウンロードで変わるからです。
比較は秒以下も含みます。17:00:00.001 は遅れです。画面が分単位の表示なら、元ログの秒を確認します。
締切を集計するときは、合計とIDを一緒に照合します。今回はどちらも2件でも、103番と105番を取り違えました。受付元の時差つき時刻を保管し、UTCで判定する。結果を渡すときに日本時間へ直せば、締切ぎりぎりの1秒も追えます。