PHP에서 두 날짜 사이의 시간 계산


107

두 날짜의 시간 차이를 어떻게 계산합니까?

예를 들면 :

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

이 경우 결과는 47 시간이어야합니다.


1
내 초기 응답은 두 값을 타임 스탬프로 바꾸고 strftime()차이를 3600으로 나누는 것이었지만 항상 작동합니까? 젠장, 일광 절약 시간!
Pekka 2010-06-24

@Pekka : 아니 항상 작동하지 않을 것 같아요 ... 내 대답을 살펴보십시오. 거기에 시간대, 윤년, 윤초 및 dst를 고려하는 솔루션을 게시했습니다 :)
Fidi

@Pekka, strtotime()사용하는 경우 기본 시간대를 사용하거나 시간대 오프셋을 명시 적으로 지정하는 한 항상 작동합니다. DST를 저주 할 이유가 없습니다.
Walter Tross 2014 년

답변:


205

새로운 PHP-버전이라는 새로운 클래스를 제공 DateTime, DateInterval, DateTimeZoneDatePeriod. 이 수업의 멋진 점은 다양한 시간대, 윤년, 윤초, 여름철 등을 고려한다는 것입니다. 게다가 사용하기가 매우 쉽습니다. 이 개체의 도움으로 원하는 것은 다음과 같습니다.

// 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 규칙이 다르기 때문에 표준 시간대에 따라 계산이 달라질 수 있으므로 항상 영역을 정의하고 서버 설정에 의존하지 않는 것이 좋습니다.
Pekka

네. 이 개체를 사용하면 서로 다른 시간대의 날짜를 계산할 수도 있습니다. $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 개월 간격 인 두 날짜 사이의 시간을 계산하려고하기 때문입니다) : Running var_dump($diff)은 나에게 또 다른 매개 변수를 보여주었습니다 : ["days"]=>int(61), 그래서 나는을 사용하게 $hours = $diff->days * 24;되었고, 그래서이 0의 결과보다 훨씬 더 찾고 있음을, 2 월 30 일, 주어진 1천4백40시간의 "평균"가까이에서 (내 PHP 버전은 ... 조금 오래 추측)
semmelbroesel

2
내 말은, 세계의 많은 지역에서 1 년에 하루 23 시간과 하루 25 시간이 있습니다.
Walter Tross 2014 년

4
@Amal Murali,이 답변에 보너스를 주기로 결정했습니다. DST (일광 절약 시간)가있는 시간대에서 1 월 1 일 정오와 6 월 1 일 정오 사이의 시간을이 답변으로 계산해 보셨습니까? 진정한 결과는 이상하지만 짝수 결과를 얻을 수 있습니다.
Walter Tross

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 19

20

UTC 또는 GMT 시간대 를 DatePeriod사용할 때 다른 방법을 제공 합니다.

시간 계산 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제외에게 DST에 대한 시간 만 때 DST가 종료 한 시간을 추가하지 않습니다. 따라서 사용은 원하는 결과와 날짜 범위에 따라 달라집니다.

현재 버그 보고서보기

//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 Duration
TheKarateKid

또 다른 참고 사항은 DateIntervalISO 8601 사양에서와 같이 분수 값을 허용하지 않는다는 것입니다. 따라서 P1.2YPHP에서 유효한 기간이 아닙니다.
fyrye

참고 : iterator_count는 긍정적 인 결과 만 반환합니다. 첫 번째 날짜가 두 번째 날짜보다 크면 diff 결과는 0이됩니다.
SubjectDelta

1
@SubjectDelta 문제가와 관련이 없습니다 . 시작 날짜가 종료 날짜로부터 미래인 경우 날짜를 생성 할 수 없기 iterator_count때문 DatePeriod입니다. 참조 : 3v4l.org/Ypsp1 에서 음수 날짜를 사용하려면 DateInterval::createFromDateString('-1 hour');시작 날짜가 종료 날짜 이후의 과거 인 음수 간격을 지정해야 합니다.
fyrye

1
@SubjectDelta 이것은 DatePeriod기본적으로 시작 날짜보다 작거나 같지 않은 경우 지정된 기간 사이의 시작 날짜를 포함하므로 의 또 다른 뉘앙스입니다 . 실제로 당신은 PHP에게 두 날짜 사이에 1 초 이내에 1 시간의 기간을 생성하도록 지시하고 있습니다. 을 사용하여 계산과 관련이 없으므로 날짜 개체에서 분과 초를 제거해야합니다 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

일광 절약 시간이 변경 되더라도 두 날짜 (날짜 시간) 사이의 정확한 시간을 얻는 가장 쉬운 방법은 Unix 타임 스탬프의 차이를 사용하는 것입니다. Unix 타임 스탬프는 1970-01-01T00 : 00 : 00 UTC 이후 경과 된 초로 윤초를 무시합니다 (이 정밀도가 필요하지 않고 윤초를 고려하기가 매우 어렵 기 때문에 괜찮습니다).

선택적 시간대 정보가있는 datetime 문자열을 Unix 타임 스탬프로 변환하는 가장 유연한 방법은 DateTime 객체 (선택적으로 생성자의 두 번째 인수로 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)

NB 내가 사용하고 format('U')대신 getTimestamp()때문에에서 주석의 매뉴얼 . 또한 포스트 에포크 및 이전 음수 에포크 날짜에는 64 비트가 필요합니다!


0

이 기능을 사용하면 지정된 두 날짜 사이의 정확한 년 개월을 계산하는 데 도움이, $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-06-28

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

유닉스 타임 스탬프를 전달하려면이 표기법을 사용하십시오.

$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(). 이 같은 좋은 설탕 추가 diffInHours, diffInMintutes, diffInSeconds


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갖습니다. 생성 된 datetime 객체 이므로 이름이됩니다.

간격이 있으면 원하는대로 형식을 지정할 수 있습니다. 전체 시간이 필요한 경우 다음을 수행 할 수 있습니다.

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

총 시간 제한을 원하면 여기로 이동하십시오.

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

이 접근 방식과 몇 가지 예에 대한 자세한 내용은 이 항목을 확인하십시오 .


0

@fyrye의 매우 유용한 답변 외에도 언급 된 버그 ( this one )에 대한 괜찮은 해결 방법입니다. DatePeriod는 여름철에 들어갈 때 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

추신 : 두 가지 이상의 전환으로 이어지는 (더 긴) 시간 범위를 확인하면 내 해결 방법은 재미있는 부작용의 가능성을 줄이기 위해 계산 된 시간을 건드리지 않습니다. 이러한 경우 더 복잡한 솔루션을 구현해야합니다. 발견 된 모든 전환을 반복하고 현재와 마지막 전환을 비교하고 DST가 true-> false 인 전환인지 확인할 수 있습니다.


0
$diff_min = ( strtotime( $day2 ) - strtotime( $day1 ) ) / 60 / 60;
$total_time  = $diff_min;

이것을 시도 할 수 있습니다.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.