Moja początkowa odpowiedź brzmiałaby: zamień obie wartości na znaczniki czasu za pomocą strftime()i podziel różnicę przez 3600, ale czy to zawsze zadziała? Cholera, czas letni!
Pekka
@Pekka: nie, to chyba nie zawsze zadziała ... Spójrz na moją odpowiedź. Tam zamieściłem rozwiązanie uwzględniające strefy czasowe, lata przestępne, sekundy przestępne i czas
letni
@Pekka, jeśli strtotime()jej użyjesz , BĘDZIE zawsze działać, o ile używasz domyślnej strefy czasowej LUB wyraźnie określisz przesunięcie strefy czasowej. Nie ma powodu, by przeklinać czas letni.
Walter Tross,
Odpowiedzi:
205
Nowsze wersje PHP podać kilka nowych klas zwane DateTime, DateInterval, DateTimeZonei DatePeriod. Fajną rzeczą w tych zajęciach jest to, że uwzględniają różne strefy czasowe, lata przestępne, sekundy przestępne, czas letni itp. A ponadto są bardzo łatwe w użyciu. Oto, czego chcesz przy pomocy tych obiektów:
// Create two new DateTime-objects...
$date1 =newDateTime('2006-04-12T12:30:00');
$date2 =newDateTime('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');
Zwracany obiekt DateInterval udostępnia również inne metody niż format. Jeśli chcesz uzyskać wynik tylko w godzinach, możesz coś takiego:
Wszystkie te zajęcia oferują również proceduralny / funkcjonalny sposób operowania datami. Dlatego spójrz na przegląd: http://php.net/manual/book.datetime.php
+1 Dobra robota! Wygląda to solidnie i jest dobrym przeglądem. Należy pamiętać, że obliczenia mogą się różnić w zależności od strefy czasowej ze względu na różne reguły czasu letniego, więc prawdopodobnie dobrym pomysłem jest zawsze definiowanie strefy i nie poleganie na ustawieniach serwera.
Pekka
Tak. Dzięki temu obiektowi możesz nawet obliczyć daty w różnych strefach czasowych. $date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');i$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
Fidi
3
Jeśli ktoś napotka ten sam problem, co właśnie zrobiłem, gdzie $diff->djest równe 0 (ponieważ próbuję obliczyć godziny między dwiema datami, które są dokładnie 2-miesięczne): Uruchomienie var_dump($diff)pokazało mi inny parametr:, ["days"]=>int(61)więc skończyło się na używaniu $hours = $diff->days * 24;i nadeszło zbliżone do "średniej" 1440 godzin z 2 30
dniowymi
2
Chodzi mi o to, że w wielu częściach świata rok ma jeden 23-godzinny dzień i jeden 25-godzinny dzień.
Walter Tross
4
@Amal Murali, więc zdecydowałeś się przyznać premię za tę odpowiedź, która jest ZŁA? Czy próbowałeś obliczyć za pomocą tej odpowiedzi liczbę godzin między południem pierwszego stycznia a południem pierwszego czerwca w dowolnej strefie czasowej, w której obowiązuje czas letni (DST)? Otrzymasz parzysty wynik, podczas gdy prawdziwy wynik jest dziwny.
$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)';
Należy pamiętać, że DatePeriodwyklucza to godzinę czasu letniego, ale nie dodaje kolejnej godziny po zakończeniu czasu letniego. Dlatego jego użycie jest subiektywne w stosunku do pożądanego wyniku i zakresu dat.
Dla każdego tak zdezorientowanego, jak widząc parametr konstruktora DateInterval, format to ISO 8601 Duration
TheKarateKid
Inną uwagą jest to, że DateIntervalnie akceptuje wartości ułamkowych, jak w specyfikacji ISO 8601. Więc P1.2Ynie jest to poprawny czas trwania w PHP.
fyrye
UWAGA: iterator_count zwróci tylko pozytywne wyniki. Jeśli pierwsza data jest większa niż druga, wynik różnicy będzie
wynosił
1
@SubjectDelta problem nie jest związany iterator_count, jest to spowodowane brakiem DatePeriodmożliwości wygenerowania dat, gdy data początkowa przypada w przyszłości od daty zakończenia. Zobacz: 3v4l.org/Ypsp1 aby użyć daty ujemnej, musisz określić przedział ujemny, DateInterval::createFromDateString('-1 hour');z datą początkową przypadającą w przeszłości lub od daty zakończenia.
fyrye
1
@SubjectDelta to kolejny niuans DatePeriod, ponieważ domyślnie będzie zawierał datę rozpoczęcia między określonymi okresami, chyba że są one mniejsze lub równe dacie rozpoczęcia. W efekcie mówisz php, aby utworzył okres 1 godziny między dwiema datami, w ciągu 1 sekundy. Musisz usunąć minuty i sekundy z obiektów daty, ponieważ nie są one istotne w obliczeniach za pomocą DateTime::setTime(date->format('H'), 0). 3v4l.org/K7uss W ten sposób, jeśli przekroczysz zakres o 1 sekundę, inna data nie zostanie utworzona.
A co jeśli między 2 godzinami a 30 minutami? Twoja odpowiedź potrwa 3 godziny. Myślę, że lepiej byłoby użyć podłogi, aby dać 2 godziny. Naprawdę zależy to od sytuacji.
Kapitein Witbaard,
14
Najłatwiejszym sposobem uzyskania prawidłowej liczby godzin między dwiema datami (datami), nawet w przypadku zmian czasu letniego, jest użycie różnicy w sygnaturach czasowych systemu Unix. Uniksowe znaczniki czasu to sekundy, które upłynęły od 1970-01-01T00: 00: 00 UTC, ignorując sekundy przestępne (jest to w porządku, ponieważ prawdopodobnie nie potrzebujesz tej precyzji i ponieważ dość trudno jest wziąć pod uwagę sekundy przestępne).
Najbardziej elastycznym sposobem konwersji ciągu z datą i godziną z opcjonalnymi informacjami o strefie czasowej na znacznik czasu systemu Unix jest skonstruowanie obiektu DateTime (opcjonalnie z DateTimeZone jako drugim argumentem w konstruktorze), a następnie wywołanie jego metody getTimestamp .
Z komentarza w instrukcji wynika, że ze względu na zgodność z datami sprzed epoki, format("U")jest lepsze niżgetTimestamp()
Arth
1
@Arth, nie wiem, kiedy to miało miejsce, ale w moim PHP 5.5.9 to już nie jest prawda. getTimestamp()teraz zwraca dokładnie taką samą wartość jak format("U"). Pierwsza jest liczbą całkowitą, podczas gdy druga jest łańcuchem (tutaj jest mniej wydajna).
Walter Tross
Fajnie, może to prawda we wcześniejszej wersji .. Tak, liczba całkowita byłaby czystsza, więc wolałbym, getTimestamp()gdybym był pewien.
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);
Niestety rozwiązanie dostarczone przez FaileN nie działa zgodnie z twierdzeniami Waltera Trossa .. dni mogą nie wynosić 24 godziny!
Lubię używać obiektów PHP tam, gdzie jest to możliwe, i dla nieco większej elastyczności wymyśliłem następującą funkcję:
/**
* @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 minutesif($cap =='H'){
$h = intval($working/3600);
$working -= $h *3600;
$cap ='M';}// If capped at minutes, calc and remove minutesif($cap =='M'){
$m = intval($working/60);
$working -= $m *60;}// Seconds remain
$s = $working;// Build interval and invert if necessary
$interval =newDateInterval('PT'.$h.'H'.$m.'M'.$s.'S');
$interval->invert=$invert;return $interval;}
To tak jak date_diff()tworzy DateTimeInterval, ale z najwyższą jednostką jako godziny, a nie lata .. można ją sformatować jak zwykle.
$interval = time_diff($date_a, $date_b);
echo $interval->format('%r%H');// For hours (with sign)
NB użyłem format('U')zamiast ze getTimestamp()względu na komentarz w instrukcji . Należy również pamiętać, że 64-bitowy jest wymagany w przypadku dat po epoce i przed epoką ujemną!
Uwaga Strefa czasowa zostanie przekazana i wyprowadzona tak, jak w +0:00przypadku użycia @w konstruktorze DateTime. Podczas korzystania z DateTime::modify()metody zostanie przekazany znacznik czasu jako +0:00i wyprowadzi bieżącą strefę czasową. Alternatywnie użyj $date = new DateTime(); $date->setTimestamp($unix_timestamp);patrz: 3v4l.org/BoAWI
Najpierw należy utworzyć obiekt interwału z zakresu dat. Już samo sformułowanie użyte w tym zdaniu pozwala łatwo zidentyfikować podstawowe potrzebne abstrakcje. Istnieje interwał jako koncepcja i kilka innych sposobów jej realizacji, w tym ten już wspomniany - z różnych dat. Tak więc interwał wygląda następująco:
Oprócz bardzo pomocnej odpowiedzi @ fyrye jest to dobre obejście wspomnianego błędu ( ten ), że DatePeriod odejmuje jedną godzinę przy wejściu w lato, ale nie dodaje jednej godziny przy wyjściu z lata (i dlatego marsz Europy / Berlina ma swój poprawne 743 godziny, ale październik ma 744 zamiast 745):
Liczenie godzin w miesiącu (lub w dowolnym przedziale czasowym), biorąc pod uwagę przejścia na czas letni w obu kierunkach
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 Jeśli zaznaczysz (dłuższy) okres czasu, który prowadzi do więcej niż tych dwóch przejść, moje obejście nie dotknie liczonych godzin, aby zmniejszyć potencjalne śmieszne efekty uboczne. W takich przypadkach trzeba wdrożyć bardziej skomplikowane rozwiązanie. Można iterować po wszystkich znalezionych przejściach i porównać prąd z ostatnim i sprawdzić, czy jest to jedno z DST prawda-> fałsz.
strftime()
i podziel różnicę przez 3600, ale czy to zawsze zadziała? Cholera, czas letni!strtotime()
jej użyjesz , BĘDZIE zawsze działać, o ile używasz domyślnej strefy czasowej LUB wyraźnie określisz przesunięcie strefy czasowej. Nie ma powodu, by przeklinać czas letni.Odpowiedzi:
Nowsze wersje PHP podać kilka nowych klas zwane
DateTime
,DateInterval
,DateTimeZone
iDatePeriod
. Fajną rzeczą w tych zajęciach jest to, że uwzględniają różne strefy czasowe, lata przestępne, sekundy przestępne, czas letni itp. A ponadto są bardzo łatwe w użyciu. Oto, czego chcesz przy pomocy tych obiektów:Zwracany obiekt DateInterval udostępnia również inne metody niż
format
. Jeśli chcesz uzyskać wynik tylko w godzinach, możesz coś takiego:A oto linki do dokumentacji:
Wszystkie te zajęcia oferują również proceduralny / funkcjonalny sposób operowania datami. Dlatego spójrz na przegląd: http://php.net/manual/book.datetime.php
źródło
$date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');
i$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
$diff->d
jest równe 0 (ponieważ próbuję obliczyć godziny między dwiema datami, które są dokładnie 2-miesięczne): Uruchomienievar_dump($diff)
pokazało mi inny parametr:,["days"]=>int(61)
więc skończyło się na używaniu$hours = $diff->days * 24;
i nadeszło zbliżone do "średniej" 1440 godzin z 2 30źródło
$diff / 3600
?Aby zapewnić inną metodę
DatePeriod
korzystania ze strefy czasowej UTC lub GMT .Liczenie godzin https://3v4l.org/Mu3HD
Wynik
Liczenie godzin z uwzględnieniem czasu letniego https://3v4l.org/QBQUB
Należy pamiętać, że
DatePeriod
wyklucza to godzinę czasu letniego, ale nie dodaje kolejnej godziny po zakończeniu czasu letniego. Dlatego jego użycie jest subiektywne w stosunku do pożądanego wyniku i zakresu dat.Zobacz aktualny raport o błędzie
Wynik
źródło
DateInterval
nie akceptuje wartości ułamkowych, jak w specyfikacji ISO 8601. WięcP1.2Y
nie jest to poprawny czas trwania w PHP.iterator_count
, jest to spowodowane brakiemDatePeriod
możliwości wygenerowania dat, gdy data początkowa przypada w przyszłości od daty zakończenia. Zobacz: 3v4l.org/Ypsp1 aby użyć daty ujemnej, musisz określić przedział ujemny,DateInterval::createFromDateString('-1 hour');
z datą początkową przypadającą w przeszłości lub od daty zakończenia.DatePeriod
, ponieważ domyślnie będzie zawierał datę rozpoczęcia między określonymi okresami, chyba że są one mniejsze lub równe dacie rozpoczęcia. W efekcie mówisz php, aby utworzył okres 1 godziny między dwiema datami, w ciągu 1 sekundy. Musisz usunąć minuty i sekundy z obiektów daty, ponieważ nie są one istotne w obliczeniach za pomocąDateTime::setTime(date->format('H'), 0)
. 3v4l.org/K7uss W ten sposób, jeśli przekroczysz zakres o 1 sekundę, inna data nie zostanie utworzona.Twoja odpowiedź brzmi:
round((strtotime($day2) - strtotime($day1))/(60*60))
źródło
Najłatwiejszym sposobem uzyskania prawidłowej liczby godzin między dwiema datami (datami), nawet w przypadku zmian czasu letniego, jest użycie różnicy w sygnaturach czasowych systemu Unix. Uniksowe znaczniki czasu to sekundy, które upłynęły od 1970-01-01T00: 00: 00 UTC, ignorując sekundy przestępne (jest to w porządku, ponieważ prawdopodobnie nie potrzebujesz tej precyzji i ponieważ dość trudno jest wziąć pod uwagę sekundy przestępne).
Najbardziej elastycznym sposobem konwersji ciągu z datą i godziną z opcjonalnymi informacjami o strefie czasowej na znacznik czasu systemu Unix jest skonstruowanie obiektu DateTime (opcjonalnie z DateTimeZone jako drugim argumentem w konstruktorze), a następnie wywołanie jego metody getTimestamp .
źródło
format("U")
jest lepsze niżgetTimestamp()
getTimestamp()
teraz zwraca dokładnie taką samą wartość jakformat("U")
. Pierwsza jest liczbą całkowitą, podczas gdy druga jest łańcuchem (tutaj jest mniej wydajna).getTimestamp()
gdybym był pewien.źródło
Domyślam się, że funkcja strtotime () akceptuje ten format daty.
źródło
źródło
Niestety rozwiązanie dostarczone przez FaileN nie działa zgodnie z twierdzeniami Waltera Trossa .. dni mogą nie wynosić 24 godziny!
Lubię używać obiektów PHP tam, gdzie jest to możliwe, i dla nieco większej elastyczności wymyśliłem następującą funkcję:
To tak jak
date_diff()
tworzyDateTimeInterval
, ale z najwyższą jednostką jako godziny, a nie lata .. można ją sformatować jak zwykle.NB użyłem
format('U')
zamiast zegetTimestamp()
względu na komentarz w instrukcji . Należy również pamiętać, że 64-bitowy jest wymagany w przypadku dat po epoce i przed epoką ujemną!źródło
Ta funkcja pomaga obliczyć dokładne lata i miesiące między dwiema podanymi datami
$doj1
i$doj
. Zwraca przykład 4.3 oznacza 4 lata i 3 miesiące.źródło
<php
należy ją zmienić na<?php
Lub zatwierdzić sugerowaną zmianę, co całkowicie usuwa błąd.To działa w moim projekcie. Myślę, że to ci pomoże.
Jeśli data jest w przeszłości, odwrócenie będzie równe 1.
Jeśli data jest w przyszłości, odwrócenie będzie równe 0.
źródło
Aby przekazać uniksowy znacznik czasu, użyj tej notacji
źródło
+0:00
przypadku użycia@
w konstruktorze DateTime. Podczas korzystania zDateTime::modify()
metody zostanie przekazany znacznik czasu jako+0:00
i wyprowadzi bieżącą strefę czasową. Alternatywnie użyj$date = new DateTime(); $date->setTimestamp($unix_timestamp);
patrz: 3v4l.org/BoAWIWęgiel też mógłby być dobrym rozwiązaniem.
Z ich strony internetowej:
Przykład:
Carbon rozszerza klasę DateTime, aby dziedziczyć metody, w tym
diff()
. Dodaje ładne cukry, takie jakdiffInHours
,diffInMintutes
,diffInSeconds
etcźródło
Najpierw należy utworzyć obiekt interwału z zakresu dat. Już samo sformułowanie użyte w tym zdaniu pozwala łatwo zidentyfikować podstawowe potrzebne abstrakcje. Istnieje interwał jako koncepcja i kilka innych sposobów jej realizacji, w tym ten już wspomniany - z różnych dat. Tak więc interwał wygląda następująco:
FromISO8601
ma tę samą semantykę: jest to utworzony obiekt typu data-godzinafrom iso8601-formatted string
, stąd nazwa.Gdy masz interwał, możesz go sformatować w dowolny sposób. Jeśli potrzebujesz pełnej liczby godzin, możesz to zrobić
Jeśli chcesz, aby całkowita liczba godzin była ograniczona, proszę bardzo:
Aby uzyskać więcej informacji na temat tego podejścia i kilka przykładów, zapoznaj się z tym wpisem .
źródło
Oprócz bardzo pomocnej odpowiedzi @ fyrye jest to dobre obejście wspomnianego błędu ( ten ), że DatePeriod odejmuje jedną godzinę przy wejściu w lato, ale nie dodaje jednej godziny przy wyjściu z lata (i dlatego marsz Europy / Berlina ma swój poprawne 743 godziny, ale październik ma 744 zamiast 745):
Liczenie godzin w miesiącu (lub w dowolnym przedziale czasowym), biorąc pod uwagę przejścia na czas letni w obu kierunkach
PS Jeśli zaznaczysz (dłuższy) okres czasu, który prowadzi do więcej niż tych dwóch przejść, moje obejście nie dotknie liczonych godzin, aby zmniejszyć potencjalne śmieszne efekty uboczne. W takich przypadkach trzeba wdrożyć bardziej skomplikowane rozwiązanie. Można iterować po wszystkich znalezionych przejściach i porównać prąd z ostatnim i sprawdzić, czy jest to jedno z DST prawda-> fałsz.
źródło
Możesz spróbować tego.
źródło