ある程度、時間のかかる作業が積み重なっているときは仕事のスケジュールを立てることが多いはずです(少なくとも自分は必須)。この時、手計算よりもプログラムでスケジュールを計算できると楽ができます。最近あまり使っていませんが Excel では WORKDAY 関数を使うことで出勤日、営業日単位で日付の操作ができます。この記事では PHP を用いてこれを計算する方法を紹介します。
まずは基本となる平日のみを数える方法です。これは次の様に strtotime 関数に weekday あるいは weekdays という書式を渡すことで実現できます。
PHP: strtotime – Manual
PHP: 相対的な書式 – Manual
<?php
/**
* 日本らしい表記にして日付を返す
* @param $timestamp
* @return string
*/
function date_format_jp($timestamp):string{
$youbi = [ "日", "月", "火", "水", "木", "金", "土" ];
return date('Y年m月d日',$timestamp).'('.$youbi[date('w',$timestamp)].")";
}
echo '本日は'. date_format_jp(time())."\n";// 本日は2021年05月12日(水)
echo '平日を5回過ぎた後は'. date_format_jp(strtotime('+5 weekday'))."\n";// 平日を5回過ぎた後は2021年05月19日(水)
echo '平日を7回過ぎた後は'. date_format_jp(strtotime('+7 weekdays'))."\n";// 平日を7回過ぎた後は2021年05月21日(金)
[
weekday は平日の意の語であり、文字通り平日を数えることができます。
これに加えて、任意の日付をスキップできるようにしたくなります。これは祝日、創業記念日、設立記念日、有給取得日あたりを考慮した時に欲しくなります。これは次の様に平日をインクリメントする処理と、該当日付のスキップ処理を入れることで実現できます。
<?php
/**
* 日本らしい表記にして日付を返す
* @param int $timestamp
* @return string
*/
function timestamp_to_jp_format(int $timestamp): string
{
$youbi = ["日", "月", "火", "水", "木", "金", "土"];
return date('Y年m月d日', $timestamp) . '(' . $youbi[date('w', $timestamp)] . ")";
}
/** @var string[] $holidayList 休業日リスト */
$holidayList = [
'2021-05-14',// 金
'2021-05-15',// 土
'2021-05-19',// 水
];
echo '本日から2回目の営業日は' . timestamp_to_jp_format(plus_workday_timestamp(date('Y-m-d'), 2, $holidayList)) . "\n";// 本日から2回目の営業日は2021年05月17日(月)
/**
* @param string $date 起点となる日時。 strtotime で解釈できる文字列
* @param int $days 起点に追加する営業日の数
* @param array $notWorkDays 働かない日のリスト
* @return int 次営業日のタイムスタンプ
*/
function plus_workday_timestamp(string $date, int $days, array $notWorkDays): int
{
/** @var int $timestamp 渡された日時文字列をタイムスタンプ化*/
$timestamp = strtotime($date);
for($i = $days; $i > 0; $i--) {
// 要求された日分を満たすまで次営業日、次営業日からそのまた次の営業日へとタイムスタンプを回す
$timestamp = next_workday_timestamp($timestamp, $notWorkDays);
}
return $timestamp;
}
/**
* @param int $timestamp 起点となる日時のタイムスタンプ
* @param array $notWorkDays 祝日などの働かない日
* @return int 次営業日のタイムスタンプ
*/
function next_workday_timestamp(int $timestamp, array $notWorkDays): int
{
/** @var int $nextTimestamp 次の平日のタイムスタンプを取得 */
$nextTimestamp = strtotime(date('Y-m-d H:i:s', $timestamp) . '+ 1 weekday');
if(!in_array(date('Y-m-d', $nextTimestamp), $notWorkDays, true)) {
// 次の平日が働かない日に含まれていないのであれば、次の平日をそのまま返す
return $nextTimestamp;
}
// 次の平日が働かない日に含まれているであれば、再帰によって起点を次の平日にして再び次営業日を探しに行く
// 次の平日が働かない日に含まれるまでループし続ける
return next_workday_timestamp($nextTimestamp, $notWorkDays);
}
もし祝日を一律で休み扱いにしたい、といった時には azuyalabs/yasumi の様な日本の祝日を網羅したライブラリを用いて同様の処理を行うと楽です。
azuyalabs/yasumi: The easy PHP Library for calculating holidays