月末の23時59分59秒まで、という条件では、その後の小数秒を取りこぼす可能性があります。データの時刻精度が変わるたびに、終了時刻を細かく書き直す運用も追いにくくなります。
月次の抽出は月初以上・翌月月初未満で表せます。開始は含め、終了は含めません。月の日数を数えずに月を区切れるので、2月や年越しも同じ条件で扱えます。
翌月の最初を終了条件にする
次は同じタイムゾーンにそろえた架空の日時を使います。Pythonのdatetimeを使い、2024年2月の開始と終了を作ります。時差の変換は行わず、月の範囲に入るかだけを比べる例です。
from datetime import datetime
def month_bounds(year, month):
start = datetime(year, month, 1)
if month == 12:
end = datetime(year + 1, 1, 1)
else:
end = datetime(year, month + 1, 1)
return start, end
start, end = month_bounds(2024, 2)
records = [
("A", datetime(2024, 2, 1)),
("B", datetime(2024, 2, 29, 23, 59, 59, 999999)),
("C", datetime(2024, 3, 1)),
]
selected = [key for key, stamp in records if start <= stamp < end]
print("抽出ID:", selected)
print("開始:", start.isoformat())
print("終了は含めない:", end.isoformat())
assert selected == ["A", "B"]
assert month_bounds(2026, 12)[1] == datetime(2027, 1, 1)
assert month_bounds(2025, 2)[1] == datetime(2025, 3, 1)
抽出されるのはAとBです。2月29日の最後の小数秒を含むBは入り、3月1日ちょうどのCは入りません。Windows・Python 3.12.10で、この抽出と平年の2月、12月の年越しを確認しました。実際の売上記録は使っていません。
翌月の開始を次の集計の開始にも使えば、境界の一件を両月へ入れたり、両方から外したりする判断もそろえられます。月別に違う条件を手書きする場合は、この一致が崩れていないかを確認してください。
時刻の基準を混ぜない
上のdatetimeはタイムゾーン情報を持たない値です。同じ基準にそろっている入力を想定しています。UTCのログと日本時間の帳票を、そのまま同じ月の条件で比べる例ではありません。
日付だけを持つ表と、時刻まで持つ表も区別します。入力が文字列なら、先に決めた書式で日時へ変換し、不正な値を確認へ回します。変換できない行を黙って外してしまうと、月次の件数が合わない理由を探しにくくなります。
集計結果には対象年月だけでなく、使った開始と終了の値も残すと、どこからどこまでを集計したかを説明できます。「終了未満」という条件も、終了値とセットで記載します。
カレンダーの月と業務の締め月
月末締めでない業務には月初から翌月月初という条件をそのまま当てはめません。20日締め、翌営業日扱い、取消を別月へ計上する、といったルールは、カレンダーの月とは別の条件です。
抽出のコードへ入れる前に、どの日付を基準にするかも決めます。受注日、出荷日、入力日を混ぜれば、日付の範囲が正しくても別の集計になります。まず境界の前後だけを含む少数の明細で、担当者が選ぶ結果とコードの結果を照合します。
数値の検算はCSV集計の基本、月次全体の手順は月次レポート自動化の設計で確認できます。
参考資料
Python datetime公式資料を2026年11月1日に確認しました。
