PHPで2つの日付間の時間数を計算する


107

2つの日付の差を時間で計算するにはどうすればよいですか?

例えば:

day1=2006-04-12 12:30:00
day2=2006-04-14 11:30:00

この場合、結果は47時間になります。


1
私の最初の応答は、両方を使用して両方の値をタイムスタンプに変換しstrftime()、差を3600で分割することでしたが、それは常に機能しますか?くそー、夏時間!
ペッカ

@Pekka:いいえ、いつもうまくいくとは限りません...私の答えを見てください。そこで、タイムゾーン、うるう年、うるう秒、dstを考慮したソリューションを投稿しました:)
Fidi

@Pekka、これを使用strtotime()する場合、デフォルトのタイムゾーンを使用するか、タイムゾーンのオフセットを明示的に指定する限り、常に機能します。DSTを呪う理由はありません。
Walter Tross 2014年

回答:


205

新しいPHP-バージョンと呼ばれる、いくつかの新しいクラスを提供しDateTimeDateIntervalDateTimeZoneDatePeriod。このクラスのクールな点は、さまざまなタイムゾーン、うるう年、うるう秒、夏時間などが考慮されることです。さらに、非常に使いやすいです。このオブジェクトを使用して必要なものは次のとおりです。

// Create two new DateTime-objects...
$date1 = new DateTime('2006-04-12T12:30:00');
$date2 = new DateTime('2006-04-14T11:30:00');

// The diff-methods returns a new DateInterval-object...
$diff = $date2->diff($date1);

// Call the format method on the DateInterval-object
echo $diff->format('%a Day and %h hours');

返されるDateIntervalオブジェクトは、以外のメソッドも提供しますformat。時間だけで結果を取得したい場合は、次のようにすることができます:

$date1 = new DateTime('2006-04-12T12:30:00');
$date2 = new DateTime('2006-04-14T11:30:00');

$diff = $date2->diff($date1);

$hours = $diff->h;
$hours = $hours + ($diff->days*24);

echo $hours;

そしてここにドキュメントへのリンクがあります:

これらすべてのクラスは、日付を操作するための手続き的/機能的な方法も提供します。したがって、概要を見てください:http : //php.net/manual/book.datetime.php


+1よくできました!これは堅固に見え、概要を示しています。DSTルールが異なるため、タイムゾーンによって計算が異なる可能性があることに注意することが重要です。そのため、サーバー設定に依存せずに常にゾーンを定義することをお勧めします。
ペッカ

うん。このオブジェクトを使用すると、異なるタイムゾーンの日付を計算することもできます。$date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');および$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
Fidi

3
誰かが私と同じ問題を$diff->d0 と同じ場所で発生した場合(ちょうど2か月離れている2つの日付間の時間を計算しようとしているため):実行すると、var_dump($diff)別のパラメーター["days"]=>int(61)が表示されたため$hours = $diff->days * 24;、を使用してしまい、 2 30か月間で与えられた1440時間の「平均」に近いので、0の結果よりもはるかに良く見えます。(私のPHPバージョンは少し古いと
思い

2
つまり、世界の多くの地域では、1年に1日23時間と1日25時間があります。
Walter Tross 2014年

4
@Amal Murali、それであなたはこの答えにボーナスを与えることにしました、それは間違っていますか?この回答を使用して、DST(夏時間)があるタイムゾーンで、1月1日の正午から6月1日の正午までの時間数を計算しようとしましたか?実際の結果は奇数ですが、結果は偶数になります。
Walter Tross 2014年

78
$t1 = strtotime( '2006-04-14 11:30:00' );
$t2 = strtotime( '2006-04-12 12:30:00' );
$diff = $t1 - $t2;
$hours = $diff / ( 60 * 60 );

4
なんで$diff / 3600
Alex G

4
@AlexGそれは単なるスタイルのことです。同じ出力ですが、プログラマーは通常、時間に関しては乗算を使用します
User

私はあなたが好きになることをお勧めします:round(($ t1-$ 22)/ 3600); ラウンドを使用して正しい時間を取得する
Shiv Singh

20

UTCまたはGMTDatePeriodを使用する場合の別の方法を提供するにはタイムゾーン。

時間を数えるhttps://3v4l.org/Mu3HD

$start = new \DateTime('2006-04-12T12:30:00');
$end = new \DateTime('2006-04-14T11:30:00');

//determine what interval should be used - can change to weeks, months, etc
$interval = new \DateInterval('PT1H');

//create periods every hour between the two dates
$periods = new \DatePeriod($start, $interval, $end);

//count the number of objects within the periods
$hours = iterator_count($periods);
echo $hours . ' hours'; 

//difference between Unix Epoch
$diff = $end->getTimestamp() - $start->getTimestamp();
$hours = $diff / ( 60 * 60 );
echo $hours . ' hours (60 * 60)';

//difference between days
$diff = $end->diff($start);
$hours = $diff->h + ($diff->days * 24);
echo $hours . ' hours (days * 24)';

結果

47 hours (iterator_count)
47 hours (60 * 60)
47 hours (days * 24)

夏時間で時間を数える https://3v4l.org/QBQUB

ご了承ください DatePeriodは夏時間の1時間を除外し、夏時間の終了時に1時間を追加しないことに。したがって、その使用法は、希望する結果と日付範囲に主観的です。

現在のバグレポートを見る

//set timezone to UTC to disregard daylight savings
date_default_timezone_set('America/New_York');

$interval = new \DateInterval('PT1H');

//DST starts Apr. 2nd 02:00 and moves to 03:00
$start = new \DateTime('2006-04-01T12:00:00');  
$end = new \DateTime('2006-04-02T12:00:00');

$periods = new \DatePeriod($start, $interval, $end);
$hours = iterator_count($periods);
echo $hours . ' hours';

//DST ends Oct. 29th 02:00 and moves to 01:00
$start = new \DateTime('2006-10-28T12:00:00');
$end = new \DateTime('2006-10-29T12:00:00'); 

$periods = new \DatePeriod($start, $interval, $end);
$hours = iterator_count($periods);
echo $hours . ' hours';

結果

#2006-04-01 12:00 EST to 2006-04-02 12:00 EDT
23 hours (iterator_count)
//23 hours (60 * 60)
//24 hours (days * 24)

#2006-10-28 12:00 EDT to 2006-10-29 12:00 EST
24 hours (iterator_count)
//25 hours (60 * 60)
//24 hours (days * 24)

#2006-01-01 12:00 EST to 2007-01-01 12:00 EST
8759 hours (iterator_count)
//8760 hours (60 * 60)
//8760 hours (days * 24)

//------

#2006-04-01 12:00 UTC to 2006-04-02 12:00 UTC
24 hours (iterator_count)
//24 hours (60 * 60)
//24 hours (days * 24)

#2006-10-28 12:00 UTC to 2006-10-29 12:00 UTC
24 hours (iterator_count)
//24 hours (60 * 60)
//24 hours (days * 24)

#2006-01-01 12:00 UTC to 2007-01-01 12:00 UTC
8760 hours (iterator_count)
//8760 hours (60 * 60)
//8760 hours (days * 24)

1
DateIntervalコンストラクターのパラメーターを見たときと同じように混乱している人にとっては、形式はISO 8601期間
TheKarateKid

もう1つの注意点はDateInterval、ISO 8601仕様のように小数値を受け入れないことです。したがってP1.2Y、PHPでは有効な期間ではありません。
fyrye

注:iterator_countは正の結果のみを返します。最初の日は、第2の1大きい場合、差分結果が0になります
SubjectDelta

1
@SubjectDelta問題はに関連していませんiterator_count。これはDatePeriod、開始日が終了日から将来の日付である場合に日付を生成できないことが原因です。参照:3v4l.org/Ypsp1で 負の日付を使用するには、負の間隔を指定DateInterval::createFromDateString('-1 hour');し、開始日を終了日から過去の日付にする必要があります。
fyrye

1
@SubjectDeltaこれは、のもう1つのニュアンスDatePeriodです。デフォルトでは、指定された期間の開始日が開始日以下でない限り、開始日が含まれます。実際には、2つの日付の間に1時間以内の1時間の期間を作成するようにphpに指示しています。を使用して、計算に関係ないため、日付オブジェクトから分と秒を削除する必要がありますDateTime::setTime(date->format('H'), 0)3v4l.org/K7ussこのようにして、範囲を1秒超えた場合、別の日付は作成されません。
fyrye

17

あなたの答えは:

round((strtotime($day2) - strtotime($day1))/(60*60))


5
間に2時間30分ある場合はどうなりますか?あなたの答えは3時間になります。2時間になるように、床を使用する方が良いと思います。本当に状況にもよりますが。
Kapitein Witbaard

14

2つの日付(日時)間の正しい時間数を取得する最も簡単な方法は、夏時間が変更された場合でも、Unixタイムスタンプの違いを使用することです。Unixタイムスタンプは、1970-01-01T00:00:00 UTCから経過した秒数で、うるう秒は無視されます(この精度はおそらく必要ないため、うるう秒を考慮に入れるのは非常に難しいため、これは問題ありません)。

オプションのタイムゾーン情報を含む日時文字列をUnixタイムスタンプに変換する最も柔軟な方法は、DateTimeオブジェクトを作成し(オプションでコンストラクターの2番目の引数としてDateTimeZoneを使用)、そのgetTimestampメソッドを呼び出すことです

$str1 = '2006-04-12 12:30:00'; 
$str2 = '2006-04-14 11:30:00';
$tz1 = new DateTimeZone('Pacific/Apia');
$tz2 = $tz1;
$d1 = new DateTime($str1, $tz1); // tz is optional,
$d2 = new DateTime($str2, $tz2); // and ignored if str contains tz offset
$delta_h = ($d2->getTimestamp() - $d1->getTimestamp()) / 3600;
if ($rounded_result) {
   $delta_h = round ($delta_h);
} else if ($truncated_result) {
   $delta_h = intval($delta_h);
}
echo "Δh: $delta_h\n";

1
マニュアルのコメントから、エポック以前の日付との互換性のformat("U")ためにgetTimestamp()
Arth

1
@Arth、いつそうなのかはわかりませんが、私のPHP 5.5.9ではもう当てはまりません。getTimestamp()は、とまったく同じ値を返すようになりましたformat("U")。ただし、前者は整数ですが、後者は文字列です(ここではあまり効率的ではありません)。
Walter Tross

かっこいい、おそらく以前のバージョンではそれは本当だった。はい、整数の方がきれいなので、getTimestamp()確信が持てればいいと思う。
Arth

4
//Calculate number of hours between pass and now
$dayinpass = "2013-06-23 05:09:12";
$today = time();
$dayinpass= strtotime($dayinpass);
echo round(abs($today-$dayinpass)/60/60);

3
$day1 = "2006-04-12 12:30:00"
$day1 = strtotime($day1);
$day2 = "2006-04-14 11:30:00"
$day2 = strtotime($day2);

$diffHours = round(($day2 - $day1) / 3600);

strtotime()関数はこの日付形式を受け入れると思います。


3
<?
     $day1 = "2014-01-26 11:30:00";
     $day1 = strtotime($day1);
     $day2 = "2014-01-26 12:30:00";
     $day2 = strtotime($day2);

   $diffHours = round(($day2 - $day1) / 3600);

   echo $diffHours;

?>

これはまた、回答フォーム2010のコピーです
ダニエル・W.

2

残念ながら、FaileNが提供するソリューションは、Walter Trossが述べているように機能しません。24日ではない場合があります。

私は可能な限りPHPオブジェクトを使用するのが好きで、もう少し柔軟性を高めるために次の関数を考え出しました。

/**
 * @param DateTimeInterface $a
 * @param DateTimeInterface $b
 * @param bool              $absolute Should the interval be forced to be positive?
 * @param string            $cap The greatest time unit to allow
 *
 * @return DateInterval The difference as a time only interval
 */
function time_diff(DateTimeInterface $a, DateTimeInterface $b, $absolute=false, $cap='H'){

  // Get unix timestamps, note getTimeStamp() is limited
  $b_raw = intval($b->format("U"));
  $a_raw = intval($a->format("U"));

  // Initial Interval properties
  $h = 0;
  $m = 0;
  $invert = 0;

  // Is interval negative?
  if(!$absolute && $b_raw<$a_raw){
    $invert = 1;
  }

  // Working diff, reduced as larger time units are calculated
  $working = abs($b_raw-$a_raw);

  // If capped at hours, calc and remove hours, cap at minutes
  if($cap == 'H') {
    $h = intval($working/3600);
    $working -= $h * 3600;
    $cap = 'M';
  }

  // If capped at minutes, calc and remove minutes
  if($cap == 'M') {
    $m = intval($working/60);
    $working -= $m * 60;
  }

  // Seconds remain
  $s = $working;

  // Build interval and invert if necessary
  $interval = new DateInterval('PT'.$h.'H'.$m.'M'.$s.'S');
  $interval->invert=$invert;

  return $interval;
}

これはをdate_diff()作成しますDateTimeIntervalが、最も高い単位は年数ではなく時間数です。通常どおりにフォーマットできます。

$interval = time_diff($date_a, $date_b);
echo $interval->format('%r%H'); // For hours (with sign)

注意:マニュアルのコメントformat('U')getTimestamp()ため、代わりに使用しました。また、エポック後および負のエポック前の日付には64ビットが必要です。


0

この関数は、次の2つの特定の日付の間の正確な年と月を計算するのに役立ちます、$doj1$doj。例4.3は、4年と3か月を意味します。

<?php
    function cal_exp($doj1)
    {
        $doj1=strtotime($doj1);
        $doj=date("m/d/Y",$doj1); //till date or any given date

        $now=date("m/d/Y");
        //$b=strtotime($b1);
        //echo $c=$b1-$a2;
        //echo date("Y-m-d H:i:s",$c);
        $year=date("Y");
        //$chk_leap=is_leapyear($year);

        //$year_diff=365.25;

        $x=explode("/",$doj);
        $y1=explode("/",$now);

        $yy=$x[2];
        $mm=$x[0];
        $dd=$x[1];

        $yy1=$y1[2];
        $mm1=$y1[0];
        $dd1=$y1[1];
        $mn=0;
        $mn1=0;
        $ye=0;
        if($mm1>$mm)
        {
            $mn=$mm1-$mm;
            if($dd1<$dd)
            {
                $mn=$mn-1;
            }
            $ye=$yy1-$yy;
        }
        else if($mm1<$mm)
        {
            $mn=12-$mm;
            //$mn=$mn;

            if($mm!=1)
            {
                $mn1=$mm1-1;
            }

            $mn+=$mn1;
            if($dd1>$dd)
            {
                $mn+=1;
            }

            $yy=$yy+1;
            $ye=$yy1-$yy;
        }
        else
        {
            $ye=$yy1-$yy;
            $ye=$ye-1;

            $mn=12-1;

            if($dd1>$dd)
            {
                $ye+=1;
                $mn=0;
            }
        }

        $to=$ye." year and ".$mn." months";
        return $ye.".".$mn;

        /*return daysDiff($x[2],$x[0],$x[1]);
         $days=dateDiff("/",$now,$doj)/$year_diff;
        $days_exp=explode(".",$days);
        return $years_exp=$days; //number of years exp*/
    }
?>

提案された編集は小さすぎますが、「提案された編集を承認する」に<php変更する必要があり<?phpます。これにより、バグがすべて削除されます。
anishsane 2013年

0

これは私のプロジェクトで働いています。私は、これはあなたのために役立つと思います。

日付が過去の場合、反転は1になります。
日付が未来の場合、反転は0になります。

$defaultDate = date('Y-m-d');   
$datetime1   = new DateTime('2013-03-10');  
$datetime2   = new DateTime($defaultDate);  
$interval    = $datetime1->diff($datetime2);  
$days        = $interval->format('%a');
$invert      = $interval->invert;

0

UNIXタイムスタンプを渡すには、次の表記を使用します

$now        = time();
$now        = new DateTime("@$now");

1
タイムゾーンは、DateTimeコンストラクターで+0:00使用@する場合と同様に渡され、出力されます。DateTime::modify()メソッドを使用している間、タイムスタンプを渡し+0:00て現在のタイムゾーンを出力します。または、次を$date = new DateTime(); $date->setTimestamp($unix_timestamp);参照してください:3v4l.org/BoAWI
fyrye

0

炭素も良い方法かもしれません。

彼らのウェブサイトから:

DateTime用の単純なPHP API拡張。http://carbon.nesbot.com/

例:

use Carbon\Carbon;

//...

$day1 = Carbon::createFromFormat('Y-m-d H:i:s', '2006-04-12 12:30:00');
$day2 = Carbon::createFromFormat('Y-m-d H:i:s', '2006-04-14 11:30:00');

echo $day1->diffInHours($day2); // 47

//...

CarbonはDateTimeクラスを拡張して、などのメソッドを継承しますdiff()。それは次のように素敵な糖を加えdiffInHoursdiffInMintutesdiffInSecondsなど


0

最初に、日付の範囲から時間隔オブジェクトを作成する必要があります。この文だけで使用されている表現によって、必要な基本的な抽象化を簡単に特定できます。概念として間隔があり、それを実装するためのいくつかの方法があり、日付の範囲から、すでに述べたものを含めます。したがって、間隔は次のようになります。

$interval =
    new FromRange(
        new FromISO8601('2017-02-14T14:27:39+00:00'),
        new FromISO8601('2017-03-14T14:27:39+00:00')
    );

FromISO8601同じセマンティクスを持っています:これは作成された日時オブジェクトなfrom iso8601-formatted stringので、名前です。

間隔がある場合は、好きなようにフォーマットできます。完全な時間数が必要な場合は、

(new TotalFullHours($interval))->value();

合計時間が必要な場合は、次のようにします。

(new TotalCeiledHours($interval))->value();

このアプローチといくつかの例の詳細については、このエントリを確認してください。


0

@fyryeの非常に役立つ回答に加えて、これは上記のバグ(これは)の夏時間に入ると1時間を差し引きますが、夏時間を離れると1時間を加算しません(そのため、ヨーロッパ/ベルリンの3月には正しい743時間ですが、10月は745時間ではなく744時間です。

両方向のDST遷移を考慮した、月の時間(または任意のタイムスパン)のカウント

function getMonthHours(string $year, string $month, \DateTimeZone $timezone): int
{
    // or whatever start and end \DateTimeInterface objects you like
    $start = new \DateTimeImmutable($year . '-' . $month . '-01 00:00:00', $timezone);
    $end = new \DateTimeImmutable((new \DateTimeImmutable($year . '-' . $month . '-01 23:59:59', $timezone))->format('Y-m-t H:i:s'), $timezone);
    
    // count the hours just utilizing \DatePeriod, \DateInterval and iterator_count, hell yeah!
    $hours = iterator_count(new \DatePeriod($start, new \DateInterval('PT1H'), $end));
    
    // find transitions and check, if there is one that leads to a positive offset
    // that isn't added by \DatePeriod
    // this is the workaround for https://bugs.php.net/bug.php?id=75685
    $transitions = $timezone->getTransitions((int)$start->format('U'), (int)$end->format('U'));
    if (2 === count($transitions) && $transitions[0]['offset'] - $transitions[1]['offset'] > 0) {
        $hours += (round(($transitions[0]['offset'] - $transitions[1]['offset'])/3600));
    }
    
    return $hours;
}

$myTimezoneWithDST = new \DateTimeZone('Europe/Berlin');
var_dump(getMonthHours('2020', '01', $myTimezoneWithDST)); // 744
var_dump(getMonthHours('2020', '03', $myTimezoneWithDST)); // 743
var_dump(getMonthHours('2020', '10', $myTimezoneWithDST)); // 745, finally!

$myTimezoneWithoutDST = new \DateTimeZone('UTC');
var_dump(getMonthHours('2020', '01', $myTimezoneWithoutDST)); // 744
var_dump(getMonthHours('2020', '03', $myTimezoneWithoutDST)); // 744
var_dump(getMonthHours('2020', '10', $myTimezoneWithoutDST)); // 744

PS(より長い)タイムスパンをチェックすると、これらの2つの遷移よりも長くなりますが、私の回避策はカウントされた時間に影響を与えず、面白い副作用の可能性を減らします。このような場合、より複雑なソリューションを実装する必要があります。見つかったすべての遷移を反復処理し、現在と最後の遷移を比較して、DSTがtrue-> falseであるかどうかを確認できます。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.