Hitung jumlah jam antara 2 tanggal dalam PHP

107

Bagaimana cara menghitung perbedaan antara dua tanggal dalam jam?

Sebagai contoh:

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

Dalam hal ini hasilnya harus 47 jam.

Pemikir
sumber
1
Tanggapan awal saya adalah, mengubah kedua nilai menjadi stempel waktu menggunakan strftime()dan membagi perbedaan dengan 3600, tetapi apakah itu akan selalu berhasil? Sialan, Waktu Musim Panas!
Pekka
@Pekka: tidak, itu tidak akan selalu berhasil, saya kira ... Lihat jawaban saya. Di sana saya telah memposting solusi mempertimbangkan, zona waktu, tahun kabisat, detik kabisat, dan dst :)
Fidi
@Pekka, jika Anda menggunakannya strtotime()AKAN selalu berfungsi, selama Anda menggunakan zona waktu default ATAU secara eksplisit menentukan offset zona waktu. Tidak ada alasan untuk mengutuk DST.
Walter Tross

Jawaban:

205

Yang lebih baru PHP-Versi menyediakan beberapa kelas baru yang disebut DateTime, DateInterval, DateTimeZonedan DatePeriod. Hal yang keren tentang kelas ini adalah, kelas ini mempertimbangkan zona waktu yang berbeda, tahun kabisat, detik kabisat, musim panas, dll. Dan di atas itu, sangat mudah digunakan. Inilah yang Anda inginkan dengan bantuan objek ini:

// 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');

Objek DateInterval, yang dikembalikan juga menyediakan metode selain format. Jika Anda menginginkan hasil dalam hitungan jam saja, Anda bisa melakukan sesuatu seperti ini:

$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;

Dan berikut ini tautan untuk dokumentasi:

Semua kelas ini juga menawarkan cara prosedural / fungsional untuk beroperasi dengan tanggal. Oleh karena itu, lihat ikhtisar: http://php.net/manual/book.datetime.php

Fidi
sumber
+1 Kerja bagus! Ini terlihat kokoh dan merupakan gambaran yang bagus. Penting untuk diperhatikan bahwa penghitungan dapat bervariasi menurut zona waktu karena aturan DST yang berbeda, jadi sebaiknya selalu tentukan zona dan tidak bergantung pada pengaturan server.
Pekka
Ya. Dengan objek ini Anda bahkan dapat menghitung antara tanggal dalam zona waktu yang berbeda. $date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');dan$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
Fidi
3
Jika seseorang mengalami masalah yang sama seperti yang baru saja saya lakukan di mana $diff->dsama dengan 0 (karena saya mencoba menghitung jam antara dua tanggal yang persis berjarak 2 bulan): Berlari var_dump($diff)menunjukkan kepada saya parameter lain:, ["days"]=>int(61)jadi saya akhirnya menggunakan $hours = $diff->days * 24;, dan itu datang mendekati "rata-rata" dari 1440 jam yang diberikan 2 30 hari bulan, jadi itu terlihat jauh lebih baik daripada hasil 0. (Menebak versi PHP saya agak tua ...)
semmelbroesel
2
Maksud saya, di banyak bagian dunia, setahun memiliki satu hari 23 jam dan satu hari 25 jam.
Walter Tross
4
@Amal Murali, jadi Anda memutuskan untuk memberikan bonus untuk jawaban ini, mana SALAH? Sudahkah Anda mencoba menghitung dengan jawaban ini jumlah jam antara siang hari pertama Januari dan siang hari pertama Juni, di zona waktu mana pun yang memiliki DST (waktu musim panas)? Anda akan mendapatkan hasil genap, sedangkan hasil sebenarnya adalah ganjil.
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 );
Jan Hančič
sumber
4
Mengapa tidak $diff / 3600?
Alex G
4
@AlexG Itu hanya gaya. Output yang sama, tetapi pemrogram biasanya menggunakan perkalian dalam hal waktu
Pengguna
saya sarankan Anda untuk menyukai: round (($ t1 - $ 22) / 3600); gunakan putaran untuk mendapatkan jam yang benar
Shiv Singh
20

Untuk menyediakan metode lain DatePeriodsaat menggunakan zona waktu UTC atau GMT .

Hitung Jam 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)';

Hasil

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

Hitung Jam dengan Daylight Savings https://3v4l.org/QBQUB

Harap diperhatikan bahwa DatePeriodtidak termasuk satu jam untuk DST tetapi tidak menambahkan satu jam lagi saat DST berakhir. Jadi penggunaannya bergantung pada hasil dan rentang tanggal yang Anda inginkan.

Lihat laporan bug saat ini

//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';

Hasil

#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)
fyrye
sumber
1
Bagi siapa pun yang bingung seperti saya saat melihat parameter konstruktor DateInterval, formatnya adalah Durasi ISO 8601
TheKarateKid
Catatan lain adalah bahwa DateIntervaltidak menerima nilai pecahan seperti pada spesifikasi ISO 8601. Jadi P1.2Ybukan durasi yang valid di PHP.
fyrye
CATATAN: iterator_count hanya akan mengembalikan hasil positif. Jika tanggal pertama lebih besar dari yang kedua, hasil perbedaannya adalah 0.
SubjectDelta
1
@SubjectDelta masalah tidak terkait iterator_count, ini karena DatePeriodtidak dapat menghasilkan tanggal ketika tanggal mulai di masa depan dari tanggal akhir. Lihat: 3v4l.org/Ypsp1 untuk menggunakan tanggal negatif, Anda perlu menentukan interval negatif, DateInterval::createFromDateString('-1 hour');dengan tanggal mulai di masa lalu dari tanggal akhir.
fyrye
1
@SubjectDelta ini adalah nuansa lain dari DatePeriod, karena secara default akan menyertakan tanggal mulai antara periode yang ditentukan kecuali jika kurang dari atau sama dengan tanggal mulai. Akibatnya Anda memberi tahu php untuk membuat periode 1 jam antara dua tanggal, dalam waktu 1 detik. Anda perlu menghapus menit dan detik dari objek tanggal Anda, karena tidak relevan dalam penghitungan, menggunakan DateTime::setTime(date->format('H'), 0). 3v4l.org/K7uss Dengan cara ini jika Anda melebihi rentang 1 detik, tanggal lain tidak dibuat.
fyrye
17

jawabanmu adalah:

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

Sergey Eremin
sumber
5
Bagaimana jika ada 2 jam 30 menit antara? Jawaban Anda akan menghasilkan 3 jam. Saya pikir akan lebih baik menggunakan lantai sehingga akan menghasilkan 2 jam. Sangat tergantung pada situasinya.
Kapitein Witbaard
14

Cara termudah untuk mendapatkan jumlah jam yang benar antara dua tanggal (waktu), bahkan di seluruh perubahan waktu musim panas, adalah dengan menggunakan perbedaan di stempel waktu Unix. Stempel waktu Unix adalah detik berlalu sejak 1970-01-01T00: 00: 00 UTC, mengabaikan detik kabisat (ini tidak masalah karena Anda mungkin tidak memerlukan ketepatan ini, dan karena cukup sulit untuk memperhitungkan detik kabisat).

Cara paling fleksibel untuk mengonversi string datetime dengan informasi zona waktu opsional menjadi stempel waktu Unix adalah dengan membuat objek DateTime (opsional dengan DateTimeZone sebagai argumen kedua dalam konstruktor), lalu memanggil metode getTimestamp -nya .

$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";
Walter Tross
sumber
1
Dari komentar di manual , tampaknya, untuk kesesuaian dengan tanggal pra-zaman, format("U")lebih disukai daripadagetTimestamp()
Arth
1
@Arth, saya tidak tahu kapan ini masalahnya, tetapi di PHP 5.5.9 saya, itu tidak benar lagi. getTimestamp()sekarang mengembalikan nilai yang sama persis dengan format("U"). Yang pertama adalah integer, sedangkan yang terakhir adalah string (kurang efisien di sini).
Walter Tross
Keren, mungkin itu benar di versi sebelumnya .. Ya, integer akan lebih bersih, jadi saya lebih suka getTimestamp()jika saya bisa yakin.
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);
Hoàng Vũ Tgtt
sumber
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);

Saya kira fungsi strtotime () menerima format tanggal ini.

Boris Delormas
sumber
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;

?>
Pejalan Malam
sumber
Ini juga merupakan salinan dari formulir jawaban 2010.
Daniel W.
2

Sayangnya solusi yang diberikan oleh FaileN tidak berfungsi seperti yang dinyatakan oleh Walter Tross .. hari mungkin bukan 24 jam!

Saya suka menggunakan Objek PHP jika memungkinkan dan untuk sedikit lebih fleksibel, saya telah membuat fungsi berikut:

/**
 * @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;
}

Ini seperti date_diff()membuat DateTimeInterval, tetapi dengan unit tertinggi sebagai jam bukan tahun .. dapat diformat seperti biasa.

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

NB saya gunakan format('U')sebagai ganti getTimestamp()karena komentar di manual . Perhatikan juga bahwa 64-bit diperlukan untuk tanggal pasca-zaman dan pra-zaman-negatif!

Arth
sumber
0

Fungsi ini membantu Anda menghitung tahun dan bulan yang tepat antara dua tanggal tertentu, $doj1dan $doj. Ini mengembalikan contoh 4.3 berarti 4 tahun dan 3 bulan.

<?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*/
    }
?>
geomagas
sumber
Pengeditan yang disarankan terlalu kecil, tetapi <phpperlu diubah menjadi <?phpAtau setujui pengeditan yang disarankan, yang menghapus bug secara bersamaan.
anishsane
0

Ini bekerja dalam proyek saya. Saya pikir, ini akan membantu Anda.

Jika Tanggal sudah lewat maka pembalikan akan 1.
Jika Tanggal di masa depan maka pembalikan akan 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;
Gurudutt Sharma
sumber
0

Untuk meneruskan stempel waktu unix gunakan notasi ini

$now        = time();
$now        = new DateTime("@$now");
Steve Whitby
sumber
1
Catatan Zona waktu akan diteruskan dan keluaran seperti +0:00saat menggunakan @pada konstruktor DateTime. Saat menggunakan DateTime::modify()metode ini akan meneruskan stempel waktu sebagai +0:00dan menampilkan zona waktu saat ini. Atau gunakan $date = new DateTime(); $date->setTimestamp($unix_timestamp);lihat: 3v4l.org/BoAWI
fyrye
0

Karbon juga bisa menjadi cara yang bagus untuk digunakan.

Dari situs web mereka:

Ekstensi API PHP sederhana untuk DateTime. http://carbon.nesbot.com/

Contoh:

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 memperluas kelas DateTime untuk mewarisi metode termasuk diff(). Ia menambahkan gula yang bagus seperti diffInHours, diffInMintutes, diffInSecondsdll

joshuamabina
sumber
0

Pertama, Anda harus membuat objek interval dari rentang tanggal. Dengan kata-kata yang digunakan dalam kalimat ini saja, seseorang dapat dengan mudah mengidentifikasi abstraksi dasar yang dibutuhkan. Ada interval sebagai sebuah konsep, dan beberapa cara lagi untuk menerapkannya, termasuk yang sudah disebutkan - dari rentang tanggal. Jadi, interval terlihat seperti itu:

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

FromISO8601memiliki semantik yang sama: itu adalah objek datetime yang dibuat from iso8601-formatted string, oleh karena itu namanya.

Saat Anda memiliki jeda, Anda dapat memformatnya sesuka Anda. Jika Anda membutuhkan sejumlah jam penuh, Anda bisa memilikinya

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

Jika Anda ingin total jam yang ditentukan, ini dia:

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

Untuk informasi lebih lanjut tentang pendekatan ini dan beberapa contoh, lihat entri ini .

Vadim Samokhin
sumber
0

Selain jawaban @ fyrye yang sangat membantu, ini adalah solusi yang oke untuk bug yang disebutkan (yang ini ), DatePeriod mengurangi satu jam ketika memasuki musim panas, tetapi tidak menambahkan satu jam ketika meninggalkan musim panas (dan dengan demikian Maret Eropa / Berlin memiliki mengoreksi 743 jam tetapi Oktober memiliki 744, bukan 745 jam):

Menghitung jam dalam sebulan (atau rentang waktu apa pun), dengan mempertimbangkan transisi DST di kedua arah

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

NB Jika Anda memeriksa rentang waktu (lebih lama), yang mengarah ke lebih dari dua transisi tersebut, solusi saya tidak akan menyentuh jam yang dihitung untuk mengurangi potensi efek samping yang lucu. Dalam kasus seperti itu, solusi yang lebih rumit harus diterapkan. Seseorang dapat mengulang semua transisi yang ditemukan dan membandingkan arus dengan yang terakhir dan memeriksa apakah itu satu dengan DST true-> false.

spackmat.dll
sumber
0
$diff_min = ( strtotime( $day2 ) - strtotime( $day1 ) ) / 60 / 60;
$total_time  = $diff_min;

Anda bisa mencoba yang ini.

Shahabuddin
sumber