0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Pythonで申込締切を判定、遅延2件でも対象者が入れ替わる

0
Posted at

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秒も追えます。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?