phpで祝日を判定する方法
目次
日本の祝日
日本の祝日は「国民の祝日に関する法律」によって定められていて、現在16日あります。
それらを計算するのは大変ですね。
文化の日、勤労感謝の日など固定の日付であれば容易に判定できますが、敬老の日、スポーツの日など第何月曜日の祝日はCarbonなどを利用して判定しないといけません。
また春分の日、秋分の日は国立天文台が昼と夜がほぼ同じになる日を判定して制定されるため年によって1日ずれます。ここまでくると計算のしようがありません。
Yasumiを使おう
こういうときに便利なライブラリがあります。その名も「Yasumi」です。
azuyalabs/yasumi
https://github.com/azuyalabs/yasumi
使い方は簡単です。
国、年、ロケール情報を入力するだけ。
function getHolidays($date = null): array
{
if (!$date) {
$carbon_date = Carbon::now();
} else {
$carbon_date = Carbon::parse($date);
}
$current_year_holidays = Yasumi::create('Japan', $carbon_date->format('Y'), 'ja_JP');
$holidays = [];
foreach ($current_year_holidays as $holiday) {
$holidays[] = $holiday->format('Y-m-d');
}
return $holidays;
}
getNameで祝日名を取得することも出来ます。
foreach($holidays as $holiday) {
echo $holiday . ':' . $holiday->getName() .”\n”;
}
とてもシンプルで簡単ですね。
ライブラリの内部処理
ライブラリの中で具体的にどのような処理を行っているのでしょうか?
public static function create(string $class, int $year = self::YEAR_LOWER_BOUND, string $locale = self::DEFAULT_LOCALE): ProviderInterface
{
// Find and return holiday provider instance
$providerClass = sprintf('Yasumi\Provider\%s', str_replace('/', '\\', $class));
$classとして渡された地域名を利用して国別のプロバイダクラスを呼び出しています。
さらにJapanクラスを見てみると
public function initialize(): void
{
$this->timezone = 'Asia/Tokyo';
// Add common holidays
if ($this->year >= 1948) {
$this->addHoliday($this->newYearsDay($this->year, $this->timezone, $this->locale));
}
// Calculate other holidays
$this->calculateNationalFoundationDay();
$this->calculateShowaDay();
$this->calculateConstitutionMemorialDay();
意外と無骨に各祝日を決定しているようです。
例えばゴールデンウィークあたりの処理はどうなっているでしょう?
昭和の日
private function calculateShowaDay(): void
{
if ($this->year >= 2007) {
$this->addHoliday(new Holiday(
'showaDay',
[
'en' => 'Showa Day',
'ja' => '昭和の日',
],
new \DateTime("{$this->year}-4-29", DateTimeZoneFactory::getDateTimeZone($this->timezone)),
$this->locale
));
}
}
天皇誕生日
private function calculateEmperorsBirthday(): void
{
$emperorsBirthday = null;
if ($this->year >= 2020) {
$emperorsBirthday = "{$this->year}-2-23";
} elseif ($this->year >= 1989 && $this->year < 2019) {
$emperorsBirthday = "{$this->year}-12-23";
} elseif ($this->year >= 1949 && $this->year <= 1988) {
$emperorsBirthday = "{$this->year}-4-29";
}
if (null !== $emperorsBirthday) {
$this->addHoliday(new Holiday(
'emperorsBirthday',
['en' => 'Emperors Birthday', 'ja' => '天皇誕生日'],
new \DateTime($emperorsBirthday, DateTimeZoneFactory::getDateTimeZone($this->timezone)),
$this->locale
));
}
}
みどりの日
private function calculateGreeneryDay(): void
{
$date = null;
if ($this->year >= 2007) {
$date = new \DateTime("{$this->year}-5-4", DateTimeZoneFactory::getDateTimeZone($this->timezone));
} elseif ($this->year >= 1989) {
$date = new \DateTime("{$this->year}-4-29", DateTimeZoneFactory::getDateTimeZone($this->timezone));
}
if ($date instanceof \DateTimeInterface) {
$this->addHoliday(new Holiday(
'greeneryDay',
['en' => 'Greenery Day', 'ja' => 'みどりの日'],
$date,
$this->locale
));
}
}
ちゃんと年で判定していて 4/29の場合、
- 1988年以前:天皇誕生日
- 2006年まで:みどりの日
- 2007年以降:昭和の日
という判定になっています。
さらにみどりの日は2007年以降は5/4の祝日として移動しています。
春分、秋分の日もちゃんと年ごとに判定していました。
ちなみにそんなに使う機会は無いかもしれませんが、世界各国の祝日を取得することも出来ます。
まずは活用できるライブラリを探そう
今回の例の祝日だけでなく、まず何か有用なライブラリが無いか検索してみるのがオススメです。
コツコツ自分でコードを書くのも良いですがもっと良いライブラリが存在していると単なる車輪の再発明を行っているだけになってしまいます。
QiitaやZennなどで検索するのも良いですし、Packagistなどのライブラリ検索サービスで探してみるというやり方もあります。
例えばデータをスプレッドシートとして出力したい、またはスプレッドシートからデータを読み込みたいと思った場合キーワードにexcelと入れると近いライブラリがずらっと出てきます。
その中から直近まで更新があり、利用しているフレームワークのバージョンにあっているもの、かつダウンロード数が多いものを採用すると良いです。



















