Моим первоначальным ответом было бы преобразовать оба значения во временные метки strftime()и разделить разницу на 3600, но всегда ли это будет работать? Черт тебя побери, летнее время!
Pekka
@Pekka: нет, я думаю, это не всегда сработает ... Посмотри на мой ответ. Там я опубликовал решение, учитывающее часовые пояса, високосные годы, високосные секунды и летнее время :)
Fidi
@Pekka, если вы strtotime()его используете, БУДЕТ работать всегда, если вы используете часовой пояс по умолчанию ИЛИ явно указываете смещение часового пояса. Нет причин ругать DST.
Уолтер Тросс
Ответы:
206
Более новые версии PHP-предоставляют некоторые новые классы , называемые DateTime, DateInterval, DateTimeZoneи DatePeriod. Классная особенность этих классов заключается в том, что они учитывают разные часовые пояса, високосные годы, високосные секунды, летнее время и т. Д. И, кроме того, им очень легко пользоваться. Вот что вы хотите с помощью этих объектов:
// 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');
Возвращаемый объект DateInterval также предоставляет другие методы, кроме format. Если вы хотите получить результат только за часы, вы можете сделать что-то вроде этого:
+1 Отличная работа! Это выглядит солидно и представляет собой прекрасный обзор. Важно отметить, что расчеты могут варьироваться в зависимости от часового пояса из-за различных правил летнего времени, поэтому, вероятно, лучше всегда определять зону и не полагаться на настройки сервера.
Pekka
Ага. С помощью этого объекта вы даже можете вычислять даты в разных часовых поясах. $date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');и$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
Фиди
3
Если кто-то столкнется с той же проблемой, что и я, где $diff->dравно 0 (потому что я пытаюсь подсчитать часы между двумя датами, которые различаются ровно на 2 месяца): бег var_dump($diff)показал мне другой параметр:, ["days"]=>int(61)поэтому я в конечном итоге использовал $hours = $diff->days * 24;, и он пришел близко к "среднему" значению 1440 часов при 2 30-дневных месяцах, так что это выглядит намного лучше, чем результат 0. (Предполагаю, что моя версия PHP немного
устарела
2
Я имею в виду, что во многих частях света в году один 23-часовой день и один 25-часовой день.
Уолтер Тросс
4
@Amal Murali, значит, вы решили наградить этот ответ бонусом, который НЕПРАВИЛЬНО? Пытались ли вы с помощью этого ответа рассчитать количество часов между полуднем первого января и полуднем первого июня в любом часовом поясе с летним временем (DST)? Вы получите четный результат, а истинный результат будет нечетным.
$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)';
Обратите внимание, что это не DatePeriodвключает час для перехода на летнее время, но не добавляет еще один час после его окончания. Таким образом, его использование зависит от желаемого результата и диапазона дат.
Для тех, кто настолько сбит с толку, как я, увидев параметр конструктора DateInterval, форматом является продолжительность ISO 8601
TheKarateKid
Еще одно примечание: DateIntervalне допускаются дробные значения, как в спецификации ISO 8601. Так что P1.2Yэто недопустимая продолжительность в PHP.
fyrye
ПРИМЕЧАНИЕ: iterator_count вернет только положительные результаты. Если первая дата больше второй, результат сравнения будет 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 секунду, другая дата не будет создана.
Что, если между ними 2 часа 30 минут? На ваш ответ уйдет 3 часа. Думаю, лучше использовать пол, чтобы выходило 2 часа. На самом деле все зависит от ситуации.
Kapitein Witbaard
14
Самый простой способ получить правильное количество часов между двумя датами (datetime), даже при переходе на летнее время, - использовать разницу во временных метках Unix. Временные метки Unix - это секунды, прошедшие с 1970-01-01T00: 00: 00 UTC, без учета дополнительных секунд (это нормально, потому что вам, вероятно, не нужна эта точность, и потому что довольно сложно учитывать дополнительные секунды).
Самый гибкий способ преобразовать строку datetime с необязательной информацией о часовом поясе в временную метку Unix - это создать объект DateTime (необязательно с DateTimeZone в качестве второго аргумента в конструкторе), а затем вызвать его метод getTimestamp .
Из комментария в руководстве следует, что для совместимости с доэпохими датами format("U")предпочтительнееgetTimestamp()
Arth
1
@Arth, я не знаю, когда это было так, но в моем PHP 5.5.9 это уже не так. getTimestamp()теперь возвращает то же значение, что и format("U"). Однако первое - целое число, а второе - строка (здесь менее эффективно).
Уолтер Тросс
Круто, возможно, это было правдой в более ранней версии ... Да, целое число было бы чище, поэтому я бы предпочел, getTimestamp()чтобы я был уверен.
Арт
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);
К сожалению, решение, предоставленное FaileN, не работает, как указано Уолтером Троссом ... дни могут не быть 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 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;}
Это похоже на date_diff()создание DateTimeInterval, но с максимальной единицей измерения в часах, а не в годах .. его можно отформатировать как обычно.
$interval = time_diff($date_a, $date_b);
echo $interval->format('%r%H');// For hours (with sign)
NB Я использовал format('U')вместо getTimestamp()из-за комментария в руководстве . Также обратите внимание, что для дат пост-эпохи и до отрицательной эпохи требуется 64-разрядная версия!
Эта функция помогает рассчитать точные годы и месяцы между двумя заданными датами $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;}elseif($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
0
Это работает в моем проекте. Думаю, это будет вам полезно.
Если дата в прошлом, инвертировать будет 1.
Если дата в будущем, инвертировать будет 0.
Примечание . Часовой пояс будет передан и выведен как +0:00при использовании @в конструкторе DateTime. При использовании DateTime::modify()метода будет передана метка времени как +0:00и будет выведен текущий часовой пояс. В качестве альтернативы используйте $date = new DateTime(); $date->setTimestamp($unix_timestamp);см .: 3v4l.org/BoAWI
Carbon расширяет класс DateTime для наследования методов, включая diff(). Это добавляет хороший сахар , как diffInHours, diffInMintutes, и diffInSecondsт.д.
Во-первых, вы должны создать объект интервала из диапазона дат. Только по формулировке этого предложения можно легко определить необходимые основные абстракции. Существует интервал как концепция, и есть еще пара способов его реализации, включая уже упомянутый - из диапазона дат. Таким образом, интервал выглядит так:
В дополнение к @ очень полезный ответ fyrye в это okayish обходной путь для Названные ошибки ( это один ), что DatePeriod вычитает один час при переходе в летнее время, но не добавляет один час при выходе из летнего времени (и, таким образом, в марте в Европе / Берлине есть правильно 743 часа, но в октябре 744 вместо 745 часов):
Подсчет часов месяца (или любого промежутка времени) с учетом перехода на летнее время в обоих направлениях
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 Если вы проверите (более длительный) интервал времени, который приводит к большему количеству этих двух переходов, мой обходной путь не коснется подсчитанных часов, чтобы уменьшить вероятность забавных побочных эффектов. В таких случаях необходимо применять более сложное решение. Можно было перебрать все найденные переходы и сравнить текущий с последним и проверить, соответствует ли он переходу с DST true-> false.
strftime()
и разделить разницу на 3600, но всегда ли это будет работать? Черт тебя побери, летнее время!strtotime()
его используете, БУДЕТ работать всегда, если вы используете часовой пояс по умолчанию ИЛИ явно указываете смещение часового пояса. Нет причин ругать DST.Ответы:
Более новые версии PHP-предоставляют некоторые новые классы , называемые
DateTime
,DateInterval
,DateTimeZone
иDatePeriod
. Классная особенность этих классов заключается в том, что они учитывают разные часовые пояса, високосные годы, високосные секунды, летнее время и т. Д. И, кроме того, им очень легко пользоваться. Вот что вы хотите с помощью этих объектов:Возвращаемый объект DateInterval также предоставляет другие методы, кроме
format
. Если вы хотите получить результат только за часы, вы можете сделать что-то вроде этого:А вот ссылки на документацию:
Все эти классы также предлагают процедурный / функциональный способ работы с датами. Поэтому взгляните на обзор: http://php.net/manual/book.datetime.php
источник
$date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');
и$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
$diff->d
равно 0 (потому что я пытаюсь подсчитать часы между двумя датами, которые различаются ровно на 2 месяца): бегvar_dump($diff)
показал мне другой параметр:,["days"]=>int(61)
поэтому я в конечном итоге использовал$hours = $diff->days * 24;
, и он пришел близко к "среднему" значению 1440 часов при 2 30-дневных месяцах, так что это выглядит намного лучше, чем результат 0. (Предполагаю, что моя версия PHP немногоисточник
$diff / 3600
?Чтобы предоставить другой метод
DatePeriod
использования часового пояса UTC или GMT .Считать часы https://3v4l.org/Mu3HD
Результат
Считайте часы с переходом на летнее время https://3v4l.org/QBQUB
Обратите внимание, что это не
DatePeriod
включает час для перехода на летнее время, но не добавляет еще один час после его окончания. Таким образом, его использование зависит от желаемого результата и диапазона дат.Посмотреть текущий отчет об ошибке
Результат
источник
DateInterval
не допускаются дробные значения, как в спецификации ISO 8601. Так чтоP1.2Y
это недопустимая продолжительность в PHP.iterator_count
, она связана сDatePeriod
невозможностью генерировать даты, когда дата начала находится в будущем от даты окончания. См.: 3v4l.org/Ypsp1, чтобы использовать отрицательную дату, вам нужно указать отрицательный интервалDateInterval::createFromDateString('-1 hour');
с датой начала в прошлом или с даты окончания.DatePeriod
, так как по умолчанию он будет включать дату начала между указанными периодами, если они не меньше или равны дате начала. Фактически вы указываете php создать период в 1 час между двумя датами в пределах 1 секунды времени. Вам нужно будет удалить минуты и секунды из ваших объектов даты, поскольку они не имеют отношения к вычислению, используяDateTime::setTime(date->format('H'), 0)
. 3v4l.org/K7uss Таким образом, если вы выйдете за пределы диапазона на 1 секунду, другая дата не будет создана.ваш ответ:
round((strtotime($day2) - strtotime($day1))/(60*60))
источник
Самый простой способ получить правильное количество часов между двумя датами (datetime), даже при переходе на летнее время, - использовать разницу во временных метках Unix. Временные метки Unix - это секунды, прошедшие с 1970-01-01T00: 00: 00 UTC, без учета дополнительных секунд (это нормально, потому что вам, вероятно, не нужна эта точность, и потому что довольно сложно учитывать дополнительные секунды).
Самый гибкий способ преобразовать строку datetime с необязательной информацией о часовом поясе в временную метку Unix - это создать объект DateTime (необязательно с DateTimeZone в качестве второго аргумента в конструкторе), а затем вызвать его метод getTimestamp .
источник
format("U")
предпочтительнееgetTimestamp()
getTimestamp()
теперь возвращает то же значение, что иformat("U")
. Однако первое - целое число, а второе - строка (здесь менее эффективно).getTimestamp()
чтобы я был уверен.источник
Я предполагаю, что функция strtotime () принимает этот формат даты.
источник
источник
К сожалению, решение, предоставленное FaileN, не работает, как указано Уолтером Троссом ... дни могут не быть 24 часами!
Мне нравится использовать объекты PHP там, где это возможно, и для большей гибкости я придумал следующую функцию:
Это похоже на
date_diff()
созданиеDateTimeInterval
, но с максимальной единицей измерения в часах, а не в годах .. его можно отформатировать как обычно.NB Я использовал
format('U')
вместоgetTimestamp()
из-за комментария в руководстве . Также обратите внимание, что для дат пост-эпохи и до отрицательной эпохи требуется 64-разрядная версия!источник
Эта функция помогает рассчитать точные годы и месяцы между двумя заданными датами
$doj1
и$doj
. Он возвращает пример 4.3, что означает 4 года и 3 месяца.источник
<php
необходимо изменить на<?php
Или утвердить предлагаемое изменение, которое устраняет ошибку полностью.Это работает в моем проекте. Думаю, это будет вам полезно.
Если дата в прошлом, инвертировать будет 1.
Если дата в будущем, инвертировать будет 0.
источник
Чтобы передать временную метку unix, используйте это обозначение
источник
+0:00
при использовании@
в конструкторе DateTime. При использованииDateTime::modify()
метода будет передана метка времени как+0:00
и будет выведен текущий часовой пояс. В качестве альтернативы используйте$date = new DateTime(); $date->setTimestamp($unix_timestamp);
см .: 3v4l.org/BoAWIУглерод также может быть хорошим вариантом.
Со своего сайта:
Пример:
Carbon расширяет класс DateTime для наследования методов, включая
diff()
. Это добавляет хороший сахар , какdiffInHours
,diffInMintutes
, иdiffInSeconds
т.д.источник
Во-первых, вы должны создать объект интервала из диапазона дат. Только по формулировке этого предложения можно легко определить необходимые основные абстракции. Существует интервал как концепция, и есть еще пара способов его реализации, включая уже упомянутый - из диапазона дат. Таким образом, интервал выглядит так:
FromISO8601
имеет ту же семантику: это созданный объект datetimefrom iso8601-formatted string
, отсюда и название.Когда у вас есть интервал, вы можете отформатировать его как хотите. Если вам нужно несколько полных часов, вы можете
Если вам нужно общее количество часов, то вот вам:
Чтобы узнать больше об этом подходе и некоторых примерах, ознакомьтесь с этой записью .
источник
В дополнение к @ очень полезный ответ fyrye в это okayish обходной путь для Названные ошибки ( это один ), что DatePeriod вычитает один час при переходе в летнее время, но не добавляет один час при выходе из летнего времени (и, таким образом, в марте в Европе / Берлине есть правильно 743 часа, но в октябре 744 вместо 745 часов):
Подсчет часов месяца (или любого промежутка времени) с учетом перехода на летнее время в обоих направлениях
PS Если вы проверите (более длительный) интервал времени, который приводит к большему количеству этих двух переходов, мой обходной путь не коснется подсчитанных часов, чтобы уменьшить вероятность забавных побочных эффектов. В таких случаях необходимо применять более сложное решение. Можно было перебрать все найденные переходы и сравнить текущий с последним и проверить, соответствует ли он переходу с DST true-> false.
источник
Вы можете попробовать это.
источник