PHPで日付(日時)差の計算(日数、週数、月数、年数)

必要になる機会が発生したので、「エイヤー!」ではありますが作りました。
ググってみましたが、同様の記事が無さそうだったので、せっかくなので晒します。


とはいえ、まだまだ若輩者のプログラマーが作成したルーチンです。
自分自身、速度的・論理的無駄を内包しかねない事を認識しています。
なので、ご指摘・ご教授大歓迎です。お気づきあれば、ぜひ一言よろしくお願いいたします。

ルーチンの説明

  • 「開始日」x「終了日」の差を算出するルーチンです。
    • 入力値は、「開始日」 < 「終了日」 となることを想定しています。
      • 開始日=2009/6/15, 終了日=2009/6/16 はOK
      • 開始日=2009/6/17, 終了日=2009/6/16 はNG
  • 指定した単位に基づいて差を返します。
    • 月を指定した場合、差の月数
    • 日を指定した場合、差の日数
    • デフォルトは日数です。
  • 指定した単位に満たない場合は、0を返します。
    • 例えば、2008/4/1 と 2009/3/30 との差の年数を求めた場合、0となります。

考え方・実装にあたって

  • 日数、週数
    • うるう年を考慮するのが面倒だったので、タイムスタンプの差から算出
  • 月数、年数
    • 年、月、日を個別に比較し、差を算出

PHPのソース

<?php

define('TERM_UNIT_YEAR', 'year');
define('TERM_UNIT_MONTH', 'month');
define('TERM_UNIT_WEEK', 'week');
define('TERM_UNIT_DAY', 'day');
define('TIMESTAMP_UNIT_DAY', 24 * 60 * 60);

/**
 * Calculation date distance between two date.
 * 
 * @param string $dateStart  Date of start point.
 * @param string $dateEnd  Date of end point.
 * @param string $termUnit  Unit string of terms (ex. day, month...)
 * @return integer  Distance between twon date depend on assigned term unit.
 */
    function _calcDateDistance($dateStart, $dateEnd, $termUnit = null)
    {
    // Check args
        if (empty($dateStart) || empty($dateEnd)) {
            return false;
        }
    // Change to timestamp
        $timestampStart = strtotime($dateStart);
        $timestampEnd = strtotime($dateEnd);
    // Check & Compare dates
        if (empty($timestampStart) || empty($timestampEnd)) {
                return false;
        } else {
                if ($timestampStart > $timestampEnd) {
                        $tmpTimestamp = $timestampStart;
                        $timestampStart = $timestampEnd;
                        $timestampEnd = $tmpTimestamp;
                }
        }
    // Set vars    
        $timestampDistance = $timestampEnd - $timestampStart;
        $datesStart = getdate($timestampStart);
        $datesEnd = getdate($timestampEnd);
    // Calc distance
        $distance = 0;
        switch ($termUnit) {
        case TERM_UNIT_YEAR:
        case TERM_UNIT_MONTH:
        // Set vars
            $lessThanTerm = false;
            $distanceYear = $datesEnd['year'] - $datesStart['year'];
            $distanceMonth = $datesEnd['mon'] - $datesStart['mon'];
            $distanceDay = $datesEnd['mday'] - $datesStart['mday'];
        // Check day & moving down
            if ($distanceDay < 0) {
                if ($distanceMonth > 0) {
                    $distanceMonth = $distanceMonth - 1;
                } else {
                        if ($distanceYear > 0) {
                                $distanceYear = $distanceYear - 1;
                                $distanceMonth = $distanceMonth + 12 - 1;
                        } else {
                                $lessThanTerm = true;
                        }
                }
            }
        // Check month & moving down
            if ($distanceMonth < 0) {
                if ($distanceYear > 0) {
                    $distanceYear = $distanceYear - 1;
                    $distanceMonth = $distanceMonth + 12;
                } else {
                        $lessThanTerm = true;
                }
            }
        // Check year
            //if ($distanceYear < 0) {}
        // Check calc result & sum result.
            switch ($termUnit) {
                case TERM_UNIT_YEAR:
                        if ($lessThanTerm) {
                                $distance = 0;
                        } else {
                                $distance = abs($distanceYear);
                        }
                    break;
                case TERM_UNIT_MONTH:
                        if ($lessThanTerm) {
                                $distance += abs($distanceMonth);
                        } else {
                        // Sum year & month
                            if (!empty($distanceYear)) {
                                $distance += abs($distanceYear) * 12;
                            }
                            if (!empty($distanceMonth)) {
                                $distance += abs($distanceMonth);
                            }
                        }
                break;
            }
            break;
        case TERM_UNIT_WEEK:
            $distance = $timestampDistance / (TIMESTAMP_UNIT_DAY * 7);
            $distance = abs(floor($distance));
            break;
        case TERM_UNIT_DAY:
        default:
                $distance = $timestampDistance / TIMESTAMP_UNIT_DAY;
                $distance = abs(floor($distance));
            break;
        }
        return $distance;
    }
?>

実行サンプル

<?php
echo _calcDateDistance('2009-6-17', '2009-6-18', TERM_UNIT_DAY);
1

echo _calcDateDistance('2008-6-17', '2009-6-17', TERM_UNIT_DAY);
365

echo _calcDateDistance('2009-6-17', '2009-6-18', TERM_UNIT_WEEK);
0

echo _calcDateDistance('2008-6-17', '2009-6-17', TERM_UNIT_WEEK);
52

echo _calcDateDistance('2008-6-17', '2009-6-16', TERM_UNIT_MONTH);
11

echo _calcDateDistance('2008-6-17', '2009-6-17', TERM_UNIT_MONTH);
12

echo _calcDateDistance('2008-6-17', '2009-6-16', TERM_UNIT_YEAR);
0

echo _calcDateDistance('2008-6-17', '2009-6-17', TERM_UNIT_YEAR);
1
?>

日時の差は「??年?月?日」です。的な答えがほしい場合は

次の形で出来ると想像してます。(未検証)


1) 年の差分を計算
2) (1)の答え分を引数$dateStartに反映して、再帰的に呼ぶ
3) (2)の答え分を引数$dateStartに反映して、再帰的に呼ぶ
4) (1)と(2)と(3)の答えを合体


以上です。

更新履歴

  • 2009/06/22
    • 「開始日」 < 「終了日」というアホな仕様を取り除きました。最初からこうしとけば良かった。。。