PHP による日付・時刻・時間の計算・演算のまとめ

最近 PHP 日付関連の関数をいろいろいじっていたので、いろいろな日付の計算関係をまとめてみました。

日付の単位について

基本的なことですが、一応まとめておきます。

1秒は1000ミリ秒
1分は60秒
1時間は60分

よって

1時間=60分=3600秒
1日=24時間=1440分=86400秒

現在時刻の取得

まずは、最もよく使う処理です。

//date関数を使う場合
date("Y-m-d H:i:s");
//strftime関数を使う場合
strftime("%Y-%m-%d %H:%M:%S");

日付/時刻のパース

日付/時刻を表したある書式の文字列から、年、月、日、時、分、秒を取得します。

//strptime関数を使用した場合
$date = strptime("2007-08-12 10:35:20", "%Y-%m-%d %H:%M:%S");
print("year=" . ($date['tm_year'] + 1900) . "<br />\r\n");
print("month=" . ($date['tm_mon'] + 1) . "<br />\r\n");
print("day={$date['tm_mday']}<br />\r\n");
print("hour={$date['tm_hour']}<br />\r\n");
print("minute={$date['tm_min']}<br />\r\n");
print("second={$date['tm_sec']}<br />\r\n");

上記の例の場合 $date[tm_year] は 1900 を足して、$date[tm_mon] は 1 を足して表示しています。
これは、年は 1900年からの年数を表していて、月は0〜11で1〜12月を表しているからです。

文字列の書式自体がそれほど複雑でない場合は、substr関数やexplode関数等を使っても取得できますね。

日付の妥当性チェック

指定された日付が有効な日付かどうかをチェックします。
フォームから入力された日付の値をチェックする際はよく使いますね。関数はcheckdate関数を使います。

//次の場合$flag1はFALSE、$falg2はTRUEが入ります。
$flag1 = checkdate(4, 31, 2007);//4月は30日までしかないのでFALSE
$flag2 = checkdate(1, 31, 2007);//有効な年月日なのでTRUEが入る

うるう年(閏年)の判定

通常うるう年の判定を行う場合には、次の条件をチェックするのが一般的なのですが、今回はcheckdate関数を使う方法を使います。

西暦年が4で割り切れる年はうるう年
ただし、西暦年が100で割り切れる年は平年
ただし、西暦年が400で割り切れる年はうるう年

上記が一般的ですが、checkdate関数を使うと、

//うるう年チェック関数
function checkLeapyear($year) {
    //checkdate関数を使って常に29日をチェック
    return checkdate(2, 29, $year);
}

//次の場合$flag1はTRUE、$falg2はFALSEが入ります。
$flag1 = checkLeapyear(2000);//2000年はうるう年なのでTRUE
$flag2 = checkLeapyear(2007);//平年なのでFALSE

というような感じでできます。

n日後、n日前の日付を求める

今日から1週間後は何月何日?とか、10日前は何月何日?みたいな計算を行う場合に使います。
mktime関数を使って、日付を計算がしやすいUNIXタイムスタンプ値にしています。

/**
 * 年月日と加算日からn日後、n日前を求める関数
 * $year 年
 * $month 月
 * $day 日
 * $addDays 加算日。マイナス指定でn日前も設定可能
 */
function computeDate($year, $month, $day, $addDays) {
    $baseSec = mktime(0, 0, 0, $month, $day, $year);//基準日を秒で取得
    $addSec = $addDays * 86400;//日数×1日の秒数
    $targetSec = $baseSec + $addSec;
    return date("Y-m-d H:i:s", $targetSec);
}

//2007年8月10日の30日後の日付を取得
//2007-09-09が表示されます
$dt = computeDate(2007, 8, 10, 30);
print("2007-08-10 + 30days = {$dt}<br />\r\n");

//2007年8月10日の2週間前の日付を取得
//2007-07-27が表示されます
$dt = computeDate(2007, 8, 10, -14);
print("2007-08-10 - 14days = {$dt}<br />\r\n");

任意の年月の月末日を求める

任意年月の月末日を求めます。2月のうるう年判定のため月だけではなくて年の情報も必要です。

/**
 * 年月を指定して月末日を求める関数
 * $year 年
 * $month 月
 */
function getMonthEndDay($year, $month) {
    //mktime関数で日付を0にすると前月の末日を指定したことになります
    //$month + 1 をしていますが、結果13月のような値になっても自動で補正されます
    $dt = mktime(0, 0, 0, $month + 1, 0, $year);
    return date("d", $dt);
}

//2007年2月の月末日を求めます
//28が表示されます
$day = getMonthEndDay(2007, 2);
print("2007-02 = {$day}<br />\r\n");

//2007年12月の月末日を求めます
//31が表示されます
$day = getMonthEndDay(2007, 12);
print("2007-12 = {$day}<br />\r\n");

nヶ月後、nヶ月前の日付を求める

nヶ月後とは単純にn * 30日ではないことに注意してください。
例えば、1月10日の1ヶ月後は2月10日ですが、1月31日の1ヶ月後は2月28日(うるう年の場合2月29日)になるという考え方です。

/**
 * 年月日と加算月からnヶ月後、nヶ月前の日付を求める
 * $year 年
 * $month 月
 * $day 日
 * $addMonths 加算月。マイナス指定でnヶ月前も設定可能
 */
function computeMonth($year, $month, $day, $addMonths) {
    $month += $addMonths;
    $endDay = getMonthEndDay($year, $month);//ここで、前述した月末日を求める関数を使用します
    if($day > $endDay) $day = $endDay;
    $dt = mktime(0, 0, 0, $month, $day, $year);//正規化
    return date("Y-m-d", $dt);
}

//2000年1月31日の1ヶ月後の日付
//2000-02-29が表示されます
$dt = computeMonth(2000, 1, 31, 1);
print("2000-01-31 + 1 month = {$dt}<br />\r\n");

//2000年7月31日の2ヶ月後の日付
//2007-09-30が表示されます
$dt = computeMonth(2007, 7, 31, 2);
print("2007-07-31 + 2 months = {$dt}<br />\r\n");

日付の比較を行う

日付の比較を行います。

//2007-08-10と2007-07-31を比較します
//処理Aが実行されます
if(mktime(0, 0, 0, 8, 10, 2007) > mktime(0, 0, 0, 7, 31, 2007)) {
    //処理A
} else {
    //処理B
}

日付の桁数が常に同じ場合は、UNIXタイムスタンプ値に変換しないで、文字列同士の比較をしても日付の比較は行えます。

2つの日付の差(何日間あるか)を求める

2つの日付の差を求めます。n年n月n日まで、あとn日。みたいな感じでカウントダウンなんかにも使えます。

/**
 * 2つの日付の差を求める関数
 * $year1 1つのめ日付の年
 * $month1 1つめの日付の月
 * $day1 1つめの日付の日
 * $year2 2つのめ日付の年
 * $month2 2つめの日付の月
 * $day2 2つめの日付の日
 */
function compareDate($year1, $month1, $day1, $year2, $month2, $day2) {
    $dt1 = mktime(0, 0, 0, $month1, $day1, $year1);
    $dt2 = mktime(0, 0, 0, $month2, $day2, $year2);
    $diff = $dt1 - $dt2;
    $diffDay = $diff / 86400;//1日は86400秒
    return $diffDay;
}

//2007年8月9日と2007年7月9日の差を求める
//31が表示されます
$days = compareDate(2007, 8, 9, 2007, 7, 9);
print("2007-08-09 - 2007-07-09 = {$days} days<br />\r\n");

//2007年1月10日と2006年10月10日の差を求める
//92が表示されます
$days = compareDate(2007, 1, 10, 2006, 10, 10);
print("2007-01-10 - 2006-10-10 = {$days} days<br />\r\n");

任意の日付の曜日を取得

任意の日付の曜日を取得します。日本語の曜日名を取得するのに、配列を使っています。

//2007年8月10日の曜日を表示
//金曜日が表示されます
$week = array("日", "月", "火", "水", "木", "金", "土");
$dayOfWeek = date("w", mktime(0, 0, 0, 8, 10, 2007));
print("曜日={$week[$dayOfWeek]}<br />\r\n");

任意の年月の第n曜日の日付を求める

指定した年月の第n曜日の日付を求めます。
よくある、定休日は毎月第1月曜日と第3月曜日です。みたいな時の実際の日付を求めるのに使えます。

/**
 * 任意の年月の第n曜日の日付を求める関数
 * $year 年
 * $month 月
 * $number 何番目の曜日か、第1曜日なら1。第3曜日なら3
 * $dayOfWeek 求めたい曜日。0〜6までの数字で曜日の日〜土を指定する
 */
function getWhatDayOfWeek($year, $month, $number, $dayOfWeek) {
    $firstDayOfWeek = date("w", mktime(0, 0, 0, $month, 1, $year));//指定した年月の1日の曜日を取得
    $day = $dayOfWeek - $firstDayOfWeek + 1;
    if($day <= 0) $day += 7;//1週間を足す
    $dt = mktime(0, 0, 0, $month, $day, $year);
    $dt += (86400 * 7 * ($number - 1));//n曜日まで1週間を足し込み
    return date("Y-m-d", $dt);
}

//2007-08 第4月曜日を求める
//2007-08-27が表示されます
$dt = getWhatDayOfWeek(2007, 8, 4, 1);
print("2007-08 第4月曜日 = {$dt}<br />\r\n");

//2007-08 第3水曜日を求める
//2007-08-15が表示されます
$dt = getWhatDayOfWeek(2007, 8, 3, 3);
print("2007-08 第3水曜日 = {$dt}<br />\r\n");

以上、とりあえず、日付・時刻関連の計算のまとめでした。
上記のサンプルコードは自由に使っていただいてOKです。
ただ、エラー処理や例外処理等は考慮されていませんので、使うときは適宜調整するといいかもしれないです。

-- 2008-11-24追記 --------
「任意の年月の第n曜日の日付を求める」に特定の日付の時に前月の日付を表示してしまうというバグがありました。
具体的には2008年12月第1日曜日を取得すると2008年11月30日が表示される等です。
現在バグは修正してあります。また、併せて一部ソースコードの簡略化を行いました。

-- 2013-08-01追記 --------
「日付/時刻のパース」の「strptime関数を使用した場合」の処理で、連想配列の添字が引用符(シングルクォート)で括られていないとのご指摘を頂きましたので修正しました。

Twitterでも情報配信中!



コメント(10)


yamazon

はじめまして。
日時関係のプログラムで頭を悩ませていましたが、検索でこちらのブログを発見しました。
参考にさせていただきます。
有益な記事をありがとうございました。


kousuke

こんばんわ!はじめまして

たまたま検索していて辿り着きました

私も何か参考になるものを探していましたので

参考にさせて頂きたいと思います。ありがとうございます


日付関係のメソッド

PHPの日付関係のメソッド。

http://www.hoge256.net/2007/08/63.html

こういう関数は、すごく助かる!…


jyn

説明もわかりやすく、観てそのまま参考に出来る内容なので、よかったです


たかやま

任意の年月の月末日を求める
ですけど、date関数で使える 「t 該当月の日数」ではダメですか?


hoge256

>ですけど、date関数で使える 「t 該当月の日数」ではダメですか?

それでも大丈夫ですね。
書いてから随分経つんですけど、書いたときは気づかなかったみたいです。
すいませんです。。。

date関数のtフォーマットを使う場合には、「任意の年月の月末日を求める」関数内を
以下のような感じに書き換えるといいかもです。

$dt = mktime(0, 0, 0, $month, 1, $year);
return date(“t”, $dt);


PHP初

とてもわかりやすい関数で使わせてもらいました


たちゅや

はじめまして。PHP初心者です。サンプルコード使わせて頂きます。


とろ

以下のコードは

print(“day={$date[tm_mday]}\r\n”);

正しくは以下ではないでしょうか?

print(“day={$date[‘tm_mday’]}\r\n”);


hoge256

ご指摘ありがとうございます。
連想配列の添字を引用符で括るように修正しました。


コメントを残す

メールアドレスが公開されることはありません。



※コメントは承認制です。承認されるまで表示されません。