この記事でできること
- VBAでボタンを押すだけで出勤・退勤の時刻を自動記録できる
- 勤務時間と残業時間をDateDiffで自動計算できる
- 月の日付リストを自動生成し、月末に合計勤務時間を一括集計できる
対象: Excel 2016以降 / Microsoft 365、Windows 10/11
—
どんな場面で使う?
自分がこの勤怠管理マクロを作ったのは、小規模チームの勤怠管理を任されたのがきっかけだった。以下のような場面で特に役に立つ。
- 少人数チーム(5〜20人程度)の勤怠をExcelで管理している:専用の勤怠管理システムを導入するほどではないが、手入力は面倒というケースにぴったり
- 月末の勤務時間集計に毎回30分以上かかっている:1行ずつ「退勤 – 出勤 – 休憩」を計算して、電卓で合計を出す作業から解放される
- 出勤・退勤の入力忘れや計算ミスが頻発している:ボタン1つで Now 関数が現在時刻を記録するので、入力ミスがなくなる
- 毎月の日付リスト作成を手作業でやっている:年月を入力するだけで1日〜末日の日付・曜日を自動生成し、土日もグレーアウトしてくれる
—
完成イメージ(Before / After)
Before(手作業):
- 毎朝、出勤時刻をセルに手入力(「9:05」「9:12」…)
- 退勤時にも手入力
- 月末に1行ずつ「退勤 – 出勤 – 休憩」を計算
- 合計を電卓で出す
- 入力忘れや計算ミスが発生
After(自動化):
- 「出勤」ボタンを押す → 現在時刻が出勤セルに自動入力
- 「退勤」ボタンを押す → 現在時刻が退勤セルに自動入力、勤務時間が自動計算
- 月末に「集計」ボタンを押す → 合計勤務時間・残業時間が自動集計
—
5人ほどのチームの勤怠をExcelで管理していた。毎朝出勤時刻を手入力し、月末に1行ずつ勤務時間を計算。たった5人分でも30分かかり、入力忘れや計算ミスが起きていた。ボタン1つで打刻できるようにしてからは、入力ミスがなくなり、月末の集計も一瞬で終わるようになった。
勤怠管理の手入力は、VBAに任せればボタン1つで終わる。
—
実行前の準備
バックアップを取る
マクロ実行前に、Excelファイルのコピーを別フォルダに保存しておく。
Excelをマクロ有効ブック(.xlsm)で保存する
拡張子が .xlsx のままだとマクロが保存できない。
- 「ファイル」→「名前を付けて保存」
- ファイルの種類を「Excelマクロ有効ブック (*.xlsm)」に変更
- 保存
シートの準備
勤怠管理表のシート(シート名:「勤怠」)に以下のヘッダーを1行目に入力する。
| A1 | B1 | C1 | D1 | E1 | F1 | G1 | H1 |
|---|---|---|---|---|---|---|---|
| 日付 | 曜日 | 出勤 | 退勤 | 休憩(分) | 勤務時間 | 残業時間 | 備考 |
実務版コードの GenerateDateList を使えば、ヘッダーも含めて自動生成されるため、手動入力は不要。
—
手順(コピペ → 実行まで約5分)
VBE(コードを書く画面)を開く
- Excelで
Alt + F11を押す - VBE(Visual Basic Editor)が開く
標準モジュールを挿入する
- VBEのメニュー →「挿入」→「標準モジュール」
- 白い画面(コードウィンドウ)が表示される
コードを貼り付けて実行する
- コードウィンドウに、下のコードをそのままコピペする
Alt + F8→ マクロ名を選んで「実行」
ボタンに割り当てれば毎回Alt+F8を押さなくて済む。方法は マクロをボタン1つで実行する方法 を参照。出勤・退勤・集計の3つのボタンを並べて配置すると使いやすい。
—
コード(最小版)– 出勤・退勤を記録して勤務時間を計算
まずは最小構成で動きを確認する。A2に日付、C2に出勤時刻、D2に退勤時刻を記録し、F2に勤務時間を表示する。
'============================================================
' ■ 出勤・退勤の記録と勤務時間の計算(最小版)
' → 今日の行に出勤/退勤時刻を記録し、勤務時間を自動計算
'============================================================
'--- 出勤を記録する ---
Sub ClockIn()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("勤怠")
'--- 今日の日付をA2に、現在時刻をC2に記録 ---
ws.Range("A2").Value = Date
ws.Range("A2").NumberFormat = "yyyy/mm/dd"
ws.Range("B2").Value = WeekdayName(Weekday(Date))
'--- 既に出勤が記録されている場合は上書き確認 ---
If ws.Range("C2").Value <> "" Then
Dim ans As VbMsgBoxResult
ans = MsgBox("出勤時刻が既に記録されています。上書きしますか?", _
vbYesNo + vbQuestion)
If ans = vbNo Then Exit Sub
End If
ws.Range("C2").Value = Now
ws.Range("C2").NumberFormat = "hh:mm"
MsgBox "出勤を記録しました: " & Format(Now, "hh:mm"), vbInformation
End Sub
'--- 退勤を記録して勤務時間を計算する ---
Sub ClockOut()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets("勤怠")
'--- 出勤が未記録なら警告 ---
If ws.Range("C2").Value = "" Then
MsgBox "出勤時刻が記録されていません。先に出勤を記録してください。", vbExclamation
Exit Sub
End If
'--- 退勤時刻を記録 ---
ws.Range("D2").Value = Now
ws.Range("D2").NumberFormat = "hh:mm"
'--- 勤務時間を計算(分単位で取得し、時:分に変換)---
Dim startTime As Date
Dim endTime As Date
startTime = ws.Range("C2").Value
endTime = ws.Range("D2").Value
Dim workMinutes As Long
workMinutes = DateDiff("n", startTime, endTime)
'--- 休憩60分を引く ---
Dim breakMinutes As Long
breakMinutes = 60
ws.Range("E2").Value = breakMinutes
workMinutes = workMinutes - breakMinutes
If workMinutes < 0 Then workMinutes = 0
'--- 勤務時間をセルに記録 ---
ws.Range("F2").Value = Int(workMinutes / 60) & ":" & _
Format(workMinutes Mod 60, "00")
MsgBox "退勤を記録しました: " & Format(Now, "hh:mm") & vbCrLf & _
"勤務時間: " & ws.Range("F2").Value, vbInformation
End Sub
書き換えポイント
| 変数・箇所 | 説明 | 初期値 |
|---|---|---|
Sheets("勤怠") |
シート名。自分のシート名に合わせる | "勤怠" |
breakMinutes = 60 |
休憩時間(分)。自社ルールに合わせる | 60 |
Range("C2") / Range("D2") |
出勤/退勤を記録するセル | C2 / D2 |
コードの流れ
- ClockIn:
Nowで現在時刻を取得し、出勤セル(C2)に記録。既に記録がある場合は上書き確認 - ClockOut: 退勤セル(D2)に現在時刻を記録し、
DateDiff("n", 出勤, 退勤)で勤務時間を分単位で計算 - 休憩時間を引いて、時:分の形式でF2に出力
最小版コードの詳細解説
ClockIn の仕組み
Now はVBAの組み込み関数で、PCの現在日時(日付+時刻)を返す。Date は日付のみを返すので、ここでは時刻も必要なため Now を使っている。NumberFormat = "hh:mm" を設定しないとExcelのシリアル値(例:0.375)が表示されてしまうので、必ずセットする。
上書き確認の MsgBox(..., vbYesNo + vbQuestion) は、誤ってボタンを二度押ししたときのガード。勤怠データは後から修正しづらいので、この確認ダイアログがあるだけで「うっかり上書き」を防げる。MsgBoxで確認ダイアログを表示する方法(/035)も参考にしてほしい。
ClockOut の勤務時間計算
DateDiff("n", startTime, endTime) がこのマクロの核心部分。"n" は「分(miNute)」を意味する引数で、2つの時刻の差を分単位の整数で返してくれる。時間単位("h")だと端数が切り捨てられてしまうため、分単位で計算してから後で時:分に変換するのがコツ。
Int(workMinutes / 60) で時間の部分を、workMinutes Mod 60 で分の部分を取り出し、"8:30" のような文字列としてセルに書き込んでいる。この方式だとExcelのSUM関数では合計できないが、実務版の MonthlySummary マクロが文字列を分に再変換して集計するので問題ない。
重要: セルの書式が「標準」のままだと時刻がシリアル値(小数)で表示される。コード内で NumberFormat = "hh:mm" を設定しているが、手動で書式変更する場合は セルの書式をVBAで変更する方法 を参照。
—
コード(実務版)– ボタン1つで打刻&月末に勤務時間を自動集計
実務では1ヶ月分の日付リストが必要。日付の自動生成、土日のグレーアウト、打刻、勤務時間・残業時間の計算、月末集計をまとめて実装する。
日付リストの自動生成では DateSerial を使う。日付の差分計算の詳しい仕組みは 2つの日付の差分・加算・比較を計算する方法 で解説している。
'============================================================
' ■ 勤怠管理表の自動化(実務版)
' → 日付リスト生成 / 出勤・退勤打刻 / 勤務時間・残業時間計算 / 月末集計
' → 土日はグレーアウト(祝日は対象外)
'============================================================
'--- 定数 ---
Const SHEET_NAME As String = "勤怠"
Const HEADER_ROW As Long = 1
Const DATA_START_ROW As Long = 2
Const BREAK_MINUTES As Long = 60 ' 休憩時間(分)
Const STANDARD_MINUTES As Long = 480 ' 所定労働時間(分)= 8時間
'============================================================
' 日付リストを自動生成する(月初〜月末)
'============================================================
Sub GenerateDateList()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(SHEET_NAME)
'--- 対象年月を取得(InputBoxで指定) ---
Dim targetYM As String
targetYM = InputBox("勤怠表を作成する年月を入力してください(例: 2026/03)", _
"年月の指定", Format(Date, "yyyy/mm"))
If targetYM = "" Then Exit Sub
'--- 年と月を分割 ---
Dim targetYear As Integer
Dim targetMonth As Integer
targetYear = CInt(Left(targetYM, 4))
targetMonth = CInt(Right(targetYM, 2))
'--- 月末日を取得(翌月の0日 = 今月の末日) ---
' ※ 12月でも Month+1=13 → DateSerial が自動で繰り上げるため正常動作
Dim lastDay As Date
lastDay = DateSerial(targetYear, targetMonth + 1, 0)
Dim totalDays As Integer
totalDays = Day(lastDay)
'--- ヘッダーを設定 ---
ws.Range("A1:H1").Value = Array("日付", "曜日", "出勤", "退勤", _
"休憩(分)", "勤務時間", "残業時間", "備考")
ws.Range("A1:H1").Font.Bold = True
'--- 既存データをクリア ---
If ws.Cells(DATA_START_ROW, 1).Value <> "" Then
Dim ans As VbMsgBoxResult
ans = MsgBox("既存のデータをクリアして新しい勤怠表を作成しますか?", _
vbYesNo + vbQuestion)
If ans = vbNo Then Exit Sub
End If
ws.Range("A" & DATA_START_ROW & ":H" & DATA_START_ROW + 40).ClearContents
ws.Range("A" & DATA_START_ROW & ":H" & DATA_START_ROW + 40).Interior.ColorIndex = xlNone
'--- 日付リストを生成 ---
Dim i As Long
Dim targetDate As Date
Dim wDay As Integer
For i = 1 To totalDays
targetDate = DateSerial(targetYear, targetMonth, i)
Dim r As Long
r = DATA_START_ROW + i - 1
ws.Cells(r, 1).Value = targetDate
ws.Cells(r, 1).NumberFormat = "yyyy/mm/dd"
ws.Cells(r, 2).Value = WeekdayName(Weekday(targetDate))
'--- 休憩時間のデフォルト値 ---
ws.Cells(r, 5).Value = BREAK_MINUTES
'--- 土日判定でグレーアウト ---
wDay = Weekday(targetDate, vbMonday)
If wDay >= 6 Then
ws.Range("A" & r & ":H" & r).Interior.Color = RGB(220, 220, 220)
ws.Cells(r, 8).Value = IIf(wDay = 6, "土曜", "日曜")
End If
Next i
'--- 集計行を追加 ---
Dim summaryRow As Long
summaryRow = DATA_START_ROW + totalDays + 1
ws.Cells(summaryRow, 1).Value = "合計"
ws.Cells(summaryRow, 1).Font.Bold = True
'--- 列幅を自動調整 ---
ws.Columns("A:H").AutoFit
MsgBox targetYear & "年" & targetMonth & "月の勤怠表を作成しました。" & vbCrLf & _
"日数: " & totalDays & "日", vbInformation
End Sub
'============================================================
' 出勤を打刻する
'============================================================
Sub StampClockIn()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(SHEET_NAME)
'--- 今日の行を探す ---
Dim targetRow As Long
targetRow = FindTodayRow(ws)
If targetRow = 0 Then
MsgBox "今日の日付が勤怠表に見つかりません。" & vbCrLf & _
"先に「日付リスト生成」を実行してください。", vbExclamation
Exit Sub
End If
'--- 土日チェック ---
Dim wDay As Integer
wDay = Weekday(Date, vbMonday)
If wDay >= 6 Then
Dim confirmHoliday As VbMsgBoxResult
confirmHoliday = MsgBox("今日は" & WeekdayName(Weekday(Date)) & "です。" & vbCrLf & _
"休日出勤として記録しますか?", vbYesNo + vbQuestion)
If confirmHoliday = vbNo Then Exit Sub
End If
'--- 上書き確認 ---
If ws.Cells(targetRow, 3).Value <> "" Then
Dim confirmOverwrite As VbMsgBoxResult
confirmOverwrite = MsgBox("出勤時刻が既に記録されています(" & _
Format(ws.Cells(targetRow, 3).Value, "hh:mm") & ")。" & vbCrLf & _
"上書きしますか?", vbYesNo + vbQuestion)
If confirmOverwrite = vbNo Then Exit Sub
End If
'--- 出勤時刻を記録 ---
ws.Cells(targetRow, 3).Value = Now
ws.Cells(targetRow, 3).NumberFormat = "hh:mm"
MsgBox "出勤を記録しました: " & Format(Now, "hh:mm"), vbInformation
End Sub
'============================================================
' 退勤を打刻して勤務時間を計算する
'============================================================
Sub StampClockOut()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(SHEET_NAME)
'--- 今日の行を探す ---
Dim targetRow As Long
targetRow = FindTodayRow(ws)
If targetRow = 0 Then
MsgBox "今日の日付が勤怠表に見つかりません。" & vbCrLf & _
"先に「日付リスト生成」を実行してください。", vbExclamation
Exit Sub
End If
'--- 出勤未記録チェック ---
If ws.Cells(targetRow, 3).Value = "" Then
MsgBox "出勤時刻が記録されていません。先に出勤を記録してください。", vbExclamation
Exit Sub
End If
'--- 退勤時刻を記録 ---
ws.Cells(targetRow, 4).Value = Now
ws.Cells(targetRow, 4).NumberFormat = "hh:mm"
'--- 勤務時間を計算 ---
Dim startTime As Date
Dim endTime As Date
startTime = ws.Cells(targetRow, 3).Value
endTime = ws.Cells(targetRow, 4).Value
Dim workMinutes As Long
workMinutes = DateDiff("n", startTime, endTime)
'--- 休憩時間を引く ---
Dim breakMin As Long
breakMin = ws.Cells(targetRow, 5).Value
If breakMin = 0 Then breakMin = BREAK_MINUTES
workMinutes = workMinutes - breakMin
If workMinutes < 0 Then workMinutes = 0
'--- 勤務時間をセルに記録 ---
ws.Cells(targetRow, 6).Value = Int(workMinutes / 60) & ":" & _
Format(workMinutes Mod 60, "00")
'--- 残業時間を計算 ---
Dim overtimeMinutes As Long
overtimeMinutes = workMinutes - STANDARD_MINUTES
If overtimeMinutes < 0 Then overtimeMinutes = 0
If overtimeMinutes > 0 Then
ws.Cells(targetRow, 7).Value = Int(overtimeMinutes / 60) & ":" & _
Format(overtimeMinutes Mod 60, "00")
Else
ws.Cells(targetRow, 7).Value = "0:00"
End If
MsgBox "退勤を記録しました: " & Format(Now, "hh:mm") & vbCrLf & _
"勤務時間: " & ws.Cells(targetRow, 6).Value & vbCrLf & _
"残業時間: " & ws.Cells(targetRow, 7).Value, vbInformation
End Sub
'============================================================
' 月末集計(合計勤務時間・合計残業時間を計算)
'============================================================
Sub MonthlySummary()
Dim ws As Worksheet
Set ws = ThisWorkbook.Sheets(SHEET_NAME)
'--- データの最終行を取得 ---
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
'--- 合計行を探す ---
Dim summaryRow As Long
Dim i As Long
For i = DATA_START_ROW To lastRow
If ws.Cells(i, 1).Value = "合計" Then
summaryRow = i
Exit For
End If
Next i
If summaryRow = 0 Then
summaryRow = lastRow + 2
ws.Cells(summaryRow, 1).Value = "合計"
ws.Cells(summaryRow, 1).Font.Bold = True
End If
'--- 勤務時間・残業時間を分単位で集計 ---
Dim totalWorkMinutes As Long
Dim totalOvertimeMinutes As Long
Dim workDays As Long
totalWorkMinutes = 0
totalOvertimeMinutes = 0
workDays = 0
For i = DATA_START_ROW To summaryRow - 1
'--- 勤務時間の文字列("8:00" 形式)を分に変換 ---
If ws.Cells(i, 6).Value <> "" And ws.Cells(i, 6).Value <> "0:00" Then
Dim parts() As String
parts = Split(CStr(ws.Cells(i, 6).Value), ":")
If UBound(parts) >= 1 Then
totalWorkMinutes = totalWorkMinutes + CInt(parts(0)) * 60 + CInt(parts(1))
workDays = workDays + 1
End If
End If
'--- 残業時間を集計 ---
If ws.Cells(i, 7).Value <> "" And ws.Cells(i, 7).Value <> "0:00" Then
Dim oParts() As String
oParts = Split(CStr(ws.Cells(i, 7).Value), ":")
If UBound(oParts) >= 1 Then
totalOvertimeMinutes = totalOvertimeMinutes + CInt(oParts(0)) * 60 + CInt(oParts(1))
End If
End If
Next i
'--- 合計をセルに記録 ---
ws.Cells(summaryRow, 6).Value = Int(totalWorkMinutes / 60) & ":" & _
Format(totalWorkMinutes Mod 60, "00")
ws.Cells(summaryRow, 7).Value = Int(totalOvertimeMinutes / 60) & ":" & _
Format(totalOvertimeMinutes Mod 60, "00")
ws.Cells(summaryRow, 8).Value = "出勤日数: " & workDays & "日"
ws.Range("A" & summaryRow & ":H" & summaryRow).Font.Bold = True
MsgBox "月次集計が完了しました。" & vbCrLf & _
"合計勤務時間: " & ws.Cells(summaryRow, 6).Value & vbCrLf & _
"合計残業時間: " & ws.Cells(summaryRow, 7).Value & vbCrLf & _
"出勤日数: " & workDays & "日", vbInformation
End Sub
'============================================================
' 今日の日付に該当する行を探す(ヘルパー関数)
'============================================================
Function FindTodayRow(ws As Worksheet) As Long
Dim i As Long
Dim lastRow As Long
lastRow = ws.Cells(ws.Rows.Count, 1).End(xlUp).Row
For i = DATA_START_ROW To lastRow
If IsDate(ws.Cells(i, 1).Value) Then
If CDate(ws.Cells(i, 1).Value) = Date Then
FindTodayRow = i
Exit Function
End If
End If
Next i
FindTodayRow = 0
End Function
書き換えポイント
| 変数・箇所 | 説明 | 初期値 |
|---|---|---|
SHEET_NAME |
シート名 | "勤怠" |
BREAK_MINUTES |
休憩時間(分) | 60 |
STANDARD_MINUTES |
所定労働時間(分)。8時間=480分 | 480 |
DATA_START_ROW |
データ開始行 | 2(1行目はヘッダー) |
コードの流れ
- GenerateDateList: 指定した年月の日付リストを自動生成。
DateSerial(年, 月+1, 0)で月末日を取得し、1日から末日までをループで出力。土日の行をグレーアウトし、休憩時間のデフォルト値を設定 - StampClockIn:
Nowで現在時刻を取得し、今日の行の出勤セルに記録。土日なら休日出勤の確認、既存記録があれば上書き確認 - StampClockOut: 退勤時刻を記録し、
DateDiff("n", 出勤, 退勤)で勤務時間を分単位で計算。休憩を引いて勤務時間と残業時間を算出 - MonthlySummary: 全行の勤務時間・残業時間を分に変換して合計し、集計行に出力。出勤日数もカウント
- FindTodayRow: 今日の日付に一致する行を検索するヘルパー関数
実務版コードの詳細解説
GenerateDateList では DateSerial(targetYear, targetMonth + 1, 0) で月末日を取得しているが、この「翌月の0日 = 今月の末日」というテクニックは VBA の日付処理では定番中の定番。2月のうるう年判定もこれだけで自動対応できるので、自分で日数を計算する必要がない。
StampClockIn と StampClockOut では、処理の最初に FindTodayRow を呼んで今日の行を探している。この関数を別プロシージャとして切り出しているのがポイント。出勤と退勤の両方で同じ「今日の行を探す」処理が必要なので、重複コードを避けるためにヘルパー関数にしている。こうしておくと、将来「日付の比較ロジックを変えたい」となったときに1箇所だけ直せば済む。
MonthlySummary では勤務時間を「8:00」のような文字列から分に変換して合計している。なぜ文字列で保存しているかというと、Excel のシリアル値で時間を扱うと24時間を超えたときに表示がおかしくなることがあるため。「時:分」の文字列形式なら「176:30」のように24時間超でも正しく表示できる。ただしSUM関数では合計できないので、集計はマクロで行う設計にしている。
土日の判定には Weekday(日付, vbMonday) を使っている。第2引数に vbMonday を指定すると月曜=1, 火曜=2, …, 土曜=6, 日曜=7 になるため、wDay >= 6 で土日を判定できる。日付・曜日の判定の詳しい仕組みは 日付・曜日の判定で月末処理を自動化する方法 を参照。
営業日数のカウントが必要な場合は 営業日を計算する方法 と組み合わせると便利。
—
よくある落とし穴5選
| # | 落とし穴 | 原因 | 対策 |
|---|---|---|---|
| 1 | 出勤時刻がシリアル値(0.375)で表示される | セルの書式が「標準」のまま | NumberFormat = "hh:mm" を設定する。コード内で対応済みだが、手動で書式変更する場合は セルの書式をVBAで変更する方法 を参照 |
| 2 | 勤務時間がマイナスになる | 退勤時刻が出勤時刻より前(日付をまたぐ深夜勤務など) | コード内で If workMinutes < 0 Then workMinutes = 0 としている。深夜勤務に対応するなら日付込みで計算する必要がある |
| 3 | Now関数の時刻がPCの時計とずれている | PCの時計が不正確 | Windowsの「日付と時刻の設定」→「時刻を自動的に設定する」をオンにする |
| 4 | 勤怠表に今日の行が見つからない | 日付リストが未生成、または別の月の表を開いている | 先に GenerateDateList を実行して当月の日付リストを生成する |
| 5 | 月末集計の合計値がおかしい | 勤務時間が文字列("8:00")で入っているため、SUM関数では合計できない | セルのSUM関数ではなくマクロの MonthlySummary で集計する |
自分もシリアル値の罠にハマった。Now関数で打刻したつもりが、セルに 0.375 と表示されて何時なのか分からず焦った。書式を「hh:mm」に変えたら 9:00 と正しく表示された。Excelの時刻は内部的にシリアル値(0〜1の小数)で管理されている。書式設定さえすれば問題ない。
もう1つ注意しておきたいのが、シート名「勤怠」が存在しない場合のエラー。コード内で ThisWorkbook.Sheets("勤怠") とシート名を指定しているので、シート名が「勤怠管理」や「Sheet1」のままだと「インデックスが有効範囲にありません」というエラーが出る。定数 SHEET_NAME を自分のシート名に合わせるか、シート名を「勤怠」に変更してほしい。シート名の変更方法はシート名の取得・一括変更・存在チェックする方法(/069)を参照。
また、複数人で同じファイルを同時に編集している場合に注意。共有ブック機能を使っていると、VBAの動作が不安定になることがある。勤怠ファイルは1人1ファイルにするか、各自が自分のPCからのみ打刻する運用にするのが安全。
VBAで勤怠管理の時刻計算がおかしいときの対処法
「出勤9:00・退勤18:00で計算したのに勤務時間が変な値になる」という場合、原因はセルの表示形式が「標準」のまま、時刻がシリアル値(小数)で表示されていること。Excelは時刻を内部的に0〜1の小数で管理しており、9:00は 0.375、18:00は 0.75 になる。対処法は、セルの書式を "hh:mm" に設定すること。コード内で NumberFormat = "hh:mm" を入れておけば自動的に正しく表示される。自分もこれを知らなくて最初は混乱した。
VBAで勤務時間がマイナスやエラーになるときの対処法
「日付をまたぐ深夜勤務の場合に勤務時間がマイナスになる」という場合、原因は退勤時刻が出勤時刻より小さい値になっていること。たとえば出勤22:00(0.917)・退勤翌6:00(0.25)だと、差がマイナスになる。対処法は、退勤時刻が出勤時刻より小さい場合に1日分(1440分)を加算するロジックを入れること。簡易的にはマイナスを0にリセットする方法もあるが、夜勤対応が必要な場合は日付込みで計算する仕組みに変更するのがベスト。
VBAの勤怠マクロでシートが見つからないエラーの対処法
「マクロを実行したら『インデックスが有効範囲にありません』と出る」という場合、原因はコード内で指定しているシート名と実際のシート名が一致していないこと。コード冒頭の定数 SHEET_NAME が "勤怠" になっているのに、シート名が「勤怠管理」や「Sheet1」のままだとエラーになる。対処法は、定数を自分のシート名に合わせるか、シートのタブをダブルクリックして名前を「勤怠」に変更すること。
---
FAQ
Q1: 打刻を間違えた場合はどうする?
セルの値を直接修正すれば良い。出勤セル(C列)や退勤セル(D列)に正しい時刻を手入力し、退勤マクロをもう一度実行すれば勤務時間が再計算される。
Q2: 休憩時間を変更したい
コード冒頭の定数 BREAK_MINUTES を変更する。例えば45分休憩なら Const BREAK_MINUTES As Long = 45 にする。日によって休憩時間が異なる場合は、E列の値を直接書き換えれば、退勤打刻時にその値が使われる。
Q3: 所定労働時間を7.5時間にしたい
コード冒頭の定数 STANDARD_MINUTES を変更する。7.5時間 = 450分なので Const STANDARD_MINUTES As Long = 450 にする。
Q4: 翌月の勤怠表を事前に作りたい
GenerateDateList を実行すると年月を入力するダイアログが表示される。翌月の年月(例:2026/04)を入力すれば翌月分の日付リストが生成される。
Q5: この勤怠表を法的な勤怠記録として使える?
この勤怠表はExcelでの簡易管理用。法的な労働時間管理には、客観的な記録方法(ICカード、勤怠管理システムなど)が推奨されている。あくまでチーム内の簡易的な勤務時間の把握に使用すること。
---
まとめ
Nowで現在時刻を取得し、ボタン1つで出勤・退勤を記録できるDateDiff("n", 出勤, 退勤)で勤務時間を分単位で計算できるDateSerialとWeekdayで月の日付リストを自動生成し、土日を判定できる- 月末に集計マクロを実行すれば、合計勤務時間と残業時間が一瞬で出る
- 勤務時間は「時:分」の文字列形式で保存しているので、24時間超でも正しく表示できる
自分のチームではこの勤怠表を導入してから、月末の集計作業が30分から1分に短縮された。入力ミスもほぼゼロになり、「計算が合わない」と月末に残業するストレスから解放された。まずは最小版で動きを確認して、慣れたら実務版に切り替えるのがおすすめ。
集計結果を別ファイルに転記したい場合はセルのデータを別シート・別ブックに転記する方法(/016)、勤怠データをCSVで出力して他システムに連携したい場合はCSVファイルを書き出す方法(/019)と組み合わせると便利。エラーで止まらないようにしたい場合はエラー処理の応用パターン(リトライ・ログ・通知)を実装する方法(/099)でガードを入れておくと安心。
関連記事
- マクロをボタン1つで実行する方法 — 出勤・退勤・集計をそれぞれボタンに割り当て
- セルの書式をVBAで変更する方法 — 時刻表示の書式設定
- セルのデータを別シート・別ブックに転記する方法 — 集計結果の転記
- CSVファイルを書き出す方法 — 勤怠データの外部連携
- エラー処理の応用パターンを実装する方法 — マクロのエラー対策
---
次にやりたくなること
- 祝日もグレーアウトしたい → 日付・曜日の判定で月末処理を自動化する方法(/014):祝日リストを別シートに用意し、日付を突き合わせて判定するパターンが追加できる
- 営業日数を自動カウントしたい → 営業日を計算する方法(/039):月の営業日数を自動カウントし、稼働率や有給消化率の計算に使える
- 勤怠データをCSV出力したい → CSVファイルを書き出す方法(/019):他の勤怠管理システムやクラウドサービスにデータ連携する際に便利
- 集計結果をグラフ化したい → グラフを自動更新する方法(/134):月ごとの勤務時間推移や残業時間の推移をグラフで可視化できる
---
もっとカスタマイズしたい場合
「フレックスタイム制に対応したい」「シフト制で複数パターンの勤務時間を管理したい」「複数人の勤怠をまとめて管理したい」「勤怠データをCSV出力して他システムに連携したい」など、業務に合わせたカスタマイズが必要な場合は、ココナラで相談できる。
相談時に伝えると話が早い情報:
- Excel のバージョン / OS
- 勤務体系(固定時間 / フレックス / シフト制)
- 管理対象の人数
- 出力したい帳票の形式


コメント