PythonでExcelの2つの日付の間の日数と平日の数を数える手順|受付から完了までを列に足す
Excelの受付日と完了日のような2つの日付の列から、間の日数と、土日や休みの日を除いた平日の数を、pandasとNumPyで列に足す手順を解説します。終わりの日が数えられない点や、空欄の日付でエラーになる点などのつまずきを、架空の明細で試して紹介します。
目次
この記事でわかること
- 2つの日付の列から、間の日数を列に足す方法
- 土日と自分で決めた休みの日を除いて、平日の数を数える方法
- 完了日が空欄の行があっても止まらずに数える書き方
- 終わりの日が数えられない点など、数え方のずれに気づく方法
受付から完了までに何日かかったかを、Excelの明細から1件ずつ数えて報告することがあります。行が増えると、カレンダーを見ながら数えるのは手間で、土日や休みの日を除く数え方では間違いも起きやすくなります。この記事では、pandasとNumPyで、受付日と完了日の2つの列から日数と平日の数を出し、新しい列として足す手順を紹介します。
よりどころは、pandas 3.1.0のSeries.dt.days・pandas.to_datetimeと、NumPy v2.5のnumpy.busday_countの公式の資料です。どれも2026年11月2日に原文を読みました。例に出てくる受付番号と日付は、試すために作った架空の明細です。
先に結論: 引き算とdt.daysで日数、busday_countで平日の数
完了日から受付日を引いてdt.daysを付けると、間の日数になります。 平日の数は、NumPyのnp.busday_countに受付日と完了日を渡すと出ます。このとき、終わりの日(完了日)そのものは数えません。2つの数え方の違いは、次のとおりです。
| 数え方 | 使うもの | 試した表(11月2日の月曜〜11月6日の金曜) |
|---|---|---|
| 間の日数 | 完了日 - 受付日 と dt.days | 4 |
| 平日の数 | np.busday_count | 4(休みの日を1日入れると3) |
| 完了日も含めた平日の数 | 完了日の翌日を終わりとして渡す | 5 |
日数と平日の数のしくみ
pandasでは、日付どうしを引き算すると、日の差を表す値になります。pandasの資料では、dt.daysはその差のうちの丸ごとの日数で、差を日数に直した長さそのものではないとしています。
NumPyの資料では、busday_countは始まりの日と終わりの日の間の有効な日を数え、終わりの日そのものは数に入れないとしています。どの曜日を数えるかはweekmaskで決め、初期の設定は月曜から金曜です。holidaysに渡した日は、平日でも数えません。
busday_countには日付をdatetime64[D]という日単位の形で渡し、日付が空(NaT)の行は渡せません。
手順: 受付から完了までの日数と平日の数を列に足す
次のコードは、架空の受付の明細 orders.xlsx(受付番号・受付日・完了日の3列)を読み、日数と平日の数の2つの列を足して、orders_days.xlsx に保存します。元の orders.xlsx には何も書き込まない作りです。
# 受付日と完了日から、日数と平日の数の2列を足す
import numpy as np # 平日の数を数えるのに使う
import pandas as pd
tickets = pd.read_excel("orders.xlsx") # 受付の明細(元のファイル)は開くだけ
start = pd.to_datetime(tickets["受付日"], errors="coerce") # 読めない値は空に
end = pd.to_datetime(tickets["完了日"], errors="coerce")
tickets["日数"] = (end - start).dt.days.astype("Int64") # 暦の上の日数
done = start.notna() & end.notna() # 両方の日付がそろった行だけ数える
holidays = ["2026-11-03", "2026-11-23"] # 会社の休みの日を自分で並べる
counts = np.busday_count(
start[done].to_numpy().astype("datetime64[D]"),
end[done].to_numpy().astype("datetime64[D]"),
holidays=holidays,
)
tickets["平日の数"] = pd.Series(counts, index=tickets.index[done]).astype("Int64")
print(tickets)
tickets.to_excel("orders_days.xlsx", index=False) # 結果は新しいファイルへ
pandasのほかに、NumPyと、xlsxを読み書きするopenpyxlを入れておきます。astype("Int64")は、空の行があっても数を整数のまま持つための指定です。
試した結果
架空の明細(8行)で動かしました。完了日には、空欄の行と「未定」と書いた行を1つずつ入れています。休みの日には、架空の会社の休みとして11月3日と11月23日を並べました。
- 11月2日の月曜から11月6日の金曜までは、日数が4、平日の数が3でした。休みの日を外すと4になります
- 11月6日の金曜から11月9日の月曜までは、日数が3、平日の数が1でした
- 受付日と完了日が同じ行は、日数も平日の数も0でした
- 完了日が空欄の行と「未定」の行は、日数も平日の数も空のまま残り、エラーにはなりませんでした
- orders_days.xlsx には、元の3列のあとに日数と平日の数の2列が入りました。元の orders.xlsx は、実行の前後でハッシュ値が変わりませんでした
試した環境は、Mac の作業PCで、Python 3.12.15・pandas 3.1.0・NumPy 2.5.4・openpyxl 3.1.5です。書き出したファイルはopenpyxlで開き直して中身を見ただけで、Excelのアプリでの表示は見ていません。
自分で決めるか、相談するかの判断
目安は、「どの日を数えて、どの日を数えないかが、社内で決まっているか」です。報告の決まりで「受付日は数えず、完了日は数える」のように書いてあれば、その決まりに合わせて終わりの日を1日ずらすだけで済みます。
休みの日の一覧や、土曜を営業日に入れるかどうかは、会社や部署で違います。決まりが見つからないときは、自分で決めずに、数字を使う人や予定表を管理している人に確かめてから数えます。
つまずきやすいところ
- 完了日が数えられていない: busday_countは終わりの日を数えません。試したときも、月曜から金曜を渡すと4でした。完了日も含めたいときは、完了日の翌日を終わりとして渡します。試した表では、金曜の翌日の土曜を終わりとして渡すと5になりました
- 時刻が入った日付で、日数が1つ少ない: dt.daysは丸ごとの日数だけを数えます。試したときも、11月2日の18時から11月4日の9時までは、日をまたいで2日でも、日数は1でした。日だけで数えたいときは、先にdt.normalize()で時刻を0時にそろえます
- 日付の型のエラーで止まる: pandasの日付をそのままbusday_countに渡すと、型を変えられないというTypeErrorで止まりました。.astype("datetime64[D]")で日単位に直してから渡します
- 空欄の日付でエラーになる: 完了日が空の行を含めたまま渡すと、NaTの日付では数えられないというValueErrorで止まりました。記事のコードのように、両方の日付がそろった行だけを選んで数えます
- 平日の数が4.0のような小数で出る: 一部の行にだけ値を入れると、空の行に合わせて小数の列になりました。astype("Int64")を付けると、空の行を残したまま整数で持てます
- 日付の書き方がそろっていない: スラッシュで区切った日付と、漢字で月と日を書いた日付が混ざった列は、先にそろえておきます。日付の列を直す方法は、PythonでExcelの日付を正しく読み込む手順で紹介しています
- かかった日数が長い行だけを取り出したい: 足した日数の列で条件を決めて行を絞る方法は、PythonでExcelの表から条件に合う行を抽出する手順にまとめています
- 曜日ごとに件数や合計を見たい: 日付から曜日を取り出して、月曜から日曜の順に集計する方法は、PythonでExcelの日付から曜日ごとの合計を出す手順が参考になります
まとめ
- 日数は、完了日から受付日を引いてdt.daysを付けると出ます
- 平日の数は、np.busday_countにdatetime64[D]の日付と休みの日を渡します
- busday_countは終わりの日を数えないので、完了日を含めたいときは翌日を渡します
- 空欄の日付がある行は、両方の日付がそろった行だけを選んでから数えます
よくある質問
日数と平日の数は、元のExcelのファイルに書き込まれますか?
書き込まれません。記事のコードは元の明細を読むだけで、2つの列を足した表は別のファイルの orders_days.xlsx に保存します。試したときも、元の orders.xlsx のハッシュ値は実行の前後で変わりませんでした。
受付日と完了日が同じ日の行は、どうなりますか?
日数も平日の数も0になります。試した表の同じ日の行でも、どちらも0でした。その日のうちに終わったことを1日と数えたいときは、数え方の決まりを先に決めて、1を足すかどうかをそろえます。
完了日のほうが受付日より前だと、どうなりますか?
NumPyの資料では、終わりの日が始まりの日より前なら、数はマイナスになるとしています。試したときも、金曜から月曜へ逆向きに数えると、日数も平日の数もマイナスになりました。入力の間違いを見つける手がかりになります。
土曜も営業日として数えたいときは、どうしますか?
busday_countのweekmaskで、月曜から日曜のどの日を数えるかを決められます。初期の設定は月曜から金曜で、土曜も数えるなら"1111110"のように7つの数字で渡します。
休みの日は、どこから持ってくればよいですか?
記事のコードでは、休みの日をリストで自分で並べています。会社の休みは会社ごとに違うため、使う人がいつも見ている会社の予定表から書き写し、年が変わるたびに見直します。