内閣府から祝日の一覧を取得+DBへ格納し、祝日マスタをプログラムでメンテナンスする方法をします。
概要
・祝日情報は内閣府(www8.cao.go.jp)から取得する
・取得したデータをDBに格納する
・一定期日前より前の日付は取り込まない
・12/31、1/2、1/3は追加で登録する
・取得したデータをDBに格納する
・一定期日前より前の日付は取り込まない
・12/31、1/2、1/3は追加で登録する
祝日マスタの登録について
内閣府から祝日データを自動取得し、SPIRALのデータベースに一括登録するPHPスクリプトを紹介します。
実装の概要
1. 内閣府の祝日CSVデータをcURLで取得する。
2. 取得したCSVデータをパースし、指定日付以降のデータをフィルタリングする。
3. 大晦日や三が日などの追加休日を設定する。
4. 日付順にソートしてデータを整理する。
5. SPIRAL APIを使用してデータベースに一括登録・更新する。
6. 処理結果を表示する。
2. 取得したCSVデータをパースし、指定日付以降のデータをフィルタリングする。
3. 大晦日や三が日などの追加休日を設定する。
4. 日付順にソートしてデータを整理する。
5. SPIRAL APIを使用してデータベースに一括登録・更新する。
6. 処理結果を表示する。
データベース設計
祝日マスタのデータベース構造は以下の通りです。
| 項目名 | 識別名 | フィールドタイプ | DB上で必須な属性 |
|---|---|---|---|
| 日付 | holidayAt | 日付(○年○月○日) | 必須制約 ユニーク制約 主キー |
| 休日名 | body | テキストフィールド(128 bytes) | なし |
サンプルデータ
| 日付 | 休日名 |
| 2025/1/1 | 元日 |
| 2025/1/13 | 成人の日 |
| 2025/2/11 | 建国記念の日 |
PHPコード(メイン機能)
祝日データを自動取得・登録するPHPスクリプトです。
コピー<?php
/**
* 休日登録サービス
* 内閣府から休日データを取得し、Spiral APIを使用してデータベースに登録する
*/
// ==================== 設定定数 ====================
// 環境ごとに設定してください
const SPIRAL_API_TOKEN = '';
const SPIRAL_API_SECRET = '';
// マスタ登録する日付の下限
const WITHOUT_LOWER_DATE = '2025-01-01';
class HolidayRegisterService
{
// 内閣府の休日データを取得するURL
private const SHUKUJITU_CSV_URL = 'https://www8.cao.go.jp/chosei/shukujitsu/syukujitsu.csv';
// データベース設定
// データベース名は環境ごとに設定してください
private const DB_TITLE = 'holiday';
private const KEY_COLUMN = 'holidayAt';
private const COLUMNS = ['holidayAt', 'body'];
/**
* 値が空でないかチェックする関数
*
* @param mixed $value チェックする値
* @return bool 値が存在し、空でない場合はtrue
*/
private function filled($value): bool
{
if (is_null($value)) {
return false;
}
if (is_string($value)) {
return trim($value) !== '';
}
if (is_array($value)) {
return !empty($value);
}
return true;
}
/**
* cURLを使用してHTTPリクエストを実行する
*
* @param string $url リクエストURL
* @param array $options cURLオプション
* @return string レスポンス
* @throws Exception cURLエラーが発生した場合
*/
private function executeCurlRequest(string $url, array $options = []): string
{
$curl = curl_init();
// デフォルトオプションを設定
$defaultOptions = [
CURLOPT_URL => $url,
CURLOPT_RETURNTRANSFER => true,
CURLOPT_TIMEOUT => 30,
CURLOPT_FOLLOWLOCATION => true,
];
// オプションをマージ(追加オプションが優先)
$mergedOptions = $options + $defaultOptions;
curl_setopt_array($curl, $mergedOptions);
$response = curl_exec($curl);
if (curl_errno($curl)) {
$error = curl_error($curl);
curl_close($curl);
throw new Exception("cURL error occurred: {$error}");
}
curl_close($curl);
return $response;
}
/**
* 内閣府から休日データを取得する
*
* @param DateTime|null $lower 取得する日付の下限(nullの場合は全期間)
* @return array 休日データの配列 [['日付', '休日名'], ...]
* @throws Exception データ取得に失敗した場合
*/
private function fromCabinetOffice(?DateTime $lower = null): array
{
// 下限日付を設定
if ($this->filled($lower)) {
$lower->setTime(0, 0, 0);
}
// 内閣府からCSVデータを取得
$response = $this->executeCurlRequest(self::SHUKUJITU_CSV_URL);
// 文字エンコーディングを変換(SHIFT-JIS → UTF-8)
$response = mb_convert_encoding($response, 'utf-8', 'SHIFT-JIS');
// CSVデータをパース
$holidays_data = explode(PHP_EOL, rtrim($response));
array_shift($holidays_data); // ヘッダー行を削除
// 日付フィルタリングとデータ整形
$filtered_holidays = array_map(function ($item) use ($lower) {
return $this->parseHolidayData($item, $lower);
}, $holidays_data);
// null値を除去して配列を再構築
$filtered_holidays = array_filter($filtered_holidays, function ($item) {
return $this->filled($item);
});
return array_values($filtered_holidays);
}
/**
* 休日データの1行をパースする
*
* @param string $item CSVの1行
* @param DateTime|null $lower 下限日付
* @return array|null パースされたデータまたはnull
*/
private function parseHolidayData(string $item, ?DateTime $lower): ?array
{
$parts = explode(",", $item);
if (count($parts) < 2) {
return null;
}
$date = $parts[0]; // 例:1955/1/1
$holidayName = rtrim($parts[1]);
// 日付をDateTimeオブジェクトに変換
$dateTime = DateTime::createFromFormat('Y/n/j', $date);
if (!$dateTime) {
return null;
}
$dateTime->setTime(0, 0, 0);
// 下限日付でフィルタリング
if ($this->filled($lower) && $dateTime < $lower) {
return null;
}
return [$date, $holidayName];
}
/**
* 休日データを取得し、追加の休日を設定する
*
* @return array 完全な休日データの配列
*/
private function holidays(): array
{
// 内閣府から基本データを取得
$lowerDate = new DateTime(WITHOUT_LOWER_DATE);
$holidays = $this->fromCabinetOffice($lowerDate);
// 追加の休日を設定
$holidays = array_merge($holidays, $this->getAdditionalHolidays());
// 日付昇順にソート
usort($holidays, function ($a, $b) {
return strtotime($a[0]) - strtotime($b[0]);
});
return $holidays;
}
/**
* 追加の休日(大晦日、三が日など)を取得する
*
* @return array 追加休日の配列
*/
private function getAdditionalHolidays(): array
{
$currentYear = (int)date('Y');
$nextYear = $currentYear + 1;
return [
// 実行年の大晦日
[
date('Y/12/31'),
'大晦日',
],
// 来年の1/2(三が日)
[
"{$nextYear}/1/2",
'三が日',
],
// 来年の1/3(三が日)
[
"{$nextYear}/1/3",
'三が日',
],
];
}
/**
* 休日データをデータベースに一括登録・更新する
*
* @param array $holidays 休日データの配列
* @return array APIレスポンス
* @throws Exception API呼び出しに失敗した場合
*/
private function upsertHolidays(array $holidays): array
{
return SimpleSpiralApiRequest::databaseBulkUpsert([
'db_title' => self::DB_TITLE,
'key' => self::KEY_COLUMN,
'columns' => self::COLUMNS,
'data' => $holidays,
]);
}
/**
* メイン処理を実行する
*
* 1. 内閣府から休日データを取得
* 2. 追加の休日を設定
* 3. データベースに登録・更新
*/
public static function main(): void
{
try {
$service = new self();
echo "休日データの取得を開始します...\n";
$holidays = $service->holidays();
echo "取得した休日数: " . count($holidays) . "\n";
echo "データベースへの登録を開始します...\n";
$res = $service->upsertHolidays($holidays);
echo "登録完了!\n";
var_dump($res);
} catch (Exception $e) {
echo "エラーが発生しました: " . $e->getMessage() . "\n";
exit(1);
}
}
}
PHPコード(SPIRAL API呼び出し機能)
SPIRAL APIのBulk Upsertを呼び出すPHPスクリプトです。
コピー<?php
class SimpleSpiralApiRequest
{
private const LOCATOR_URL = "https://www.pi-pe.co.jp/api/locator";
private static ?string $apiUrl = null;
/**
* APIでlocatorを取得する
*
* @return string APIのURL
* @throws Exception API呼び出しに失敗した場合
*/
private static function fetchApiUrl(): string
{
$api_headers = [
"X-SPIRAL-API: locator/apiserver/request",
"Content-Type: application/json; charset=UTF-8",
];
$parameters = [
"spiral_api_token" => SPIRAL_API_TOKEN
];
$ch = curl_init(self::LOCATOR_URL);
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode($parameters));
curl_setopt($ch, CURLOPT_HTTPHEADER, $api_headers);
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
$response_array = json_decode($response, true);
if (!isset($response_array['location'])) {
throw new Exception('Invalid API response: location not found');
}
return $response_array['location'];
}
/**
* APIのURLを取得する(既に取得済みの場合はそれを返す)
*
* @return string APIのURL
*/
private static function getApiUrl(): string
{
if (is_null(self::$apiUrl)) {
self::$apiUrl = self::fetchApiUrl();
}
return self::$apiUrl;
}
/**
* データベースの一括登録・更新を実行する
*
* @param array $param パラメータ
* @return array APIレスポンス
* @throws Exception API呼び出しに失敗した場合
*/
public static function databaseBulkUpsert(array $param): array
{
$api_headers = [
"X-SPIRAL-API: database/bulk_upsert/request",
"Content-Type: application/json; charset=UTF-8"
];
$time = time();
// 認証パラメータを構築
$basicParam = [
"spiral_api_token" => SPIRAL_API_TOKEN,
"passkey" => $time,
"signature" => hash_hmac('sha1', SPIRAL_API_TOKEN . "&" . $time, SPIRAL_API_SECRET, false),
];
$ch = curl_init(self::getApiUrl());
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);
curl_setopt($ch, CURLOPT_POST, true);
curl_setopt($ch, CURLOPT_POSTFIELDS, json_encode(array_merge($basicParam, $param)));
curl_setopt($ch, CURLOPT_HTTPHEADER, $api_headers);
$response = curl_exec($ch);
if (curl_errno($ch)) {
throw new Exception(curl_error($ch));
}
curl_close($ch);
$response_array = json_decode($response, true);
// エラーチェック
if (!array_key_exists('code', $response_array) || $response_array['code'] !== "0") {
throw new Exception('SPIRAL API Error Occurred.[response:' . $response . ']');
}
return $response_array;
}
}
実行&実行結果
以下プログラムにて処理が実行されます。
HolidayRegisterService::main();
スクリプトを実行すると、以下のような出力が表示されます。
コピー休日データの取得を開始します...
取得した休日数: 25
データベースへの登録を開始します...
登録完了!
array(3) {
["code"]=>
string(1) "0"
["message"]=>
string(2) "OK"
["results"]=>
array(40) {
[0]=>
array(2) {
["id"]=>
int(1258)
["status"]=>
string(7) "updated"
}
...
}
}
実装時の注意点
・ このスクリプトは、SPIRAL APIの認証情報が必要です。
・ 内閣府のCSVデータはSHIFT-JISエンコーディングで提供されます。
・ 実行前にSPIRAL_API_TOKENとSPIRAL_API_SECRETを設定してください。
・ データベース名(DB_TITLE)は環境に合わせて変更してください。
・ 日付の下限設定(WITHOUT_LOWER_DATE)を適切に設定してください。
・ 内閣府のCSVデータはSHIFT-JISエンコーディングで提供されます。
・ 実行前にSPIRAL_API_TOKENとSPIRAL_API_SECRETを設定してください。
・ データベース名(DB_TITLE)は環境に合わせて変更してください。
・ 日付の下限設定(WITHOUT_LOWER_DATE)を適切に設定してください。
まとめ
本記事では、内閣府から祝日データを自動取得し、SPIRALのデータベースに一括登録するPHPスクリプトを紹介しました。
このスクリプトにより、毎年更新される祝日データを手動で管理する手間を大幅に削減できます。
実行前に適切な認証情報(TOKEN,SECRET)とデータベース設定を行い、定期的な実行(カスタムプログラムによる定期実行)で祝日マスタを最新に保つことをお勧めします。
このスクリプトにより、毎年更新される祝日データを手動で管理する手間を大幅に削減できます。
実行前に適切な認証情報(TOKEN,SECRET)とデータベース設定を行い、定期的な実行(カスタムプログラムによる定期実行)で祝日マスタを最新に保つことをお勧めします。