Mi respuesta inicial habría sido, convertir ambos valores en marcas de tiempo usando strftime()y dividir la diferencia por 3600, pero ¿funcionará siempre? ¡Maldito seas, horario de verano!
Pekka
@Pekka: no, no siempre funcionará, supongo ... Echa un vistazo a mi respuesta. Allí publiqué una solución considerando, zonas horarias, años bisiestos, segundos bisiestos y dst :)
Fidi
@Pekka, si lo usa strtotime(), siempre funcionará, siempre que use la zona horaria predeterminada O especifique explícitamente el desplazamiento de la zona horaria. No hay razón para maldecir el horario de verano.
Walter Tross
Respuestas:
205
Las nuevas versiones de PHP-proporcionan algunas nuevas clases llamadas DateTime, DateInterval, DateTimeZoney DatePeriod. Lo bueno de estas clases es que considera diferentes zonas horarias, años bisiestos, segundos bisiestos, horario de verano, etc. Y además es muy fácil de usar. Esto es lo que quiere con la ayuda de estos objetos:
// 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');
El objeto DateInterval, que se devuelve también proporciona otros métodos distintos a format. Si desea obtener el resultado solo en horas, puede hacer algo como esto:
Todas estas clases también ofrecen una forma procedimental / funcional de operar con fechas. Por lo tanto, eche un vistazo a la descripción general: http://php.net/manual/book.datetime.php
+1 ¡Buen trabajo! Esto parece sólido y es una buena descripción general. Es importante tener en cuenta que los cálculos pueden variar según la zona horaria debido a las diferentes reglas de DST, por lo que probablemente sea una buena idea definir siempre la zona y no depender de la configuración del servidor.
Pekka
Sí. Con este objeto incluso podría calcular entre fechas en diferentes zonas horarias. $date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');y$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
Fidi
3
Si alguien se encuentra con el mismo problema que acabo de hacer, donde $diff->des igual a 0 (porque estoy tratando de calcular las horas entre dos fechas que tienen exactamente 2 meses de diferencia): Running var_dump($diff)me mostró otro parámetro:, ["days"]=>int(61)así que terminé usando $hours = $diff->days * 24;, y llegó cerca del "promedio" de 1440 horas dados 2 meses de 30 días, por lo que se ve mucho mejor que un resultado de 0. (Adivinando que mi versión de PHP es un poco vieja ...)
semmelbroesel
2
Quiero decir, en muchas partes del mundo un año tiene un día de 23 horas y un día de 25 horas.
Walter Tross
4
@Amal Murali, entonces decidiste otorgar el bono a esta respuesta, ¿cuál es INCORRECTA? ¿Ha intentado calcular con esta respuesta el número de horas entre el mediodía del primero de enero y el mediodía del primero de junio, en cualquier zona horaria que tenga DST (horario de verano)? Obtendrá un resultado par, mientras que el resultado real es impar.
$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)';
Tenga en cuenta que DatePeriodexcluye una hora para el horario de verano pero no agrega otra hora cuando finaliza el horario de verano. Por lo tanto, su uso es subjetivo al resultado deseado y al rango de fechas.
Para cualquiera que esté tan confundido como yo al ver el parámetro del constructor DateInterval, el formato es una duración ISO 8601
TheKarateKid
Otra nota es que DateIntervalno acepta valores fraccionarios como en la especificación ISO 8601. Entonces P1.2Yno es una duración válida en PHP.
fyrye
NOTA: iterator_count solo devolverá resultados positivos. Si la primera fecha es mayor que la segunda, el resultado de la diferencia será 0.
SubjectDelta
1
@SubjectDelta el problema no está relacionado iterator_count, se debe a que DatePeriodno se pueden generar fechas cuando la fecha de inicio es posterior a la fecha de finalización. Consulte: 3v4l.org/Ypsp1 para usar una fecha negativa, debe especificar un intervalo negativo, DateInterval::createFromDateString('-1 hour');con una fecha de inicio anterior a la fecha de finalización.
fyrye
1
@SubjectDelta este es otro matiz de DatePeriod, ya que por defecto incluirá la fecha de inicio entre los períodos especificados a menos que sean menores o iguales a la fecha de inicio. En efecto, le está diciendo a php que cree un período de 1 hora entre las dos fechas, dentro de 1 segundo de tiempo. Debería eliminar los minutos y segundos de sus objetos de fecha, ya que no son relevantes en el cálculo, utilizando DateTime::setTime(date->format('H'), 0). 3v4l.org/K7uss De esta manera, si está por encima del rango por 1 segundo, no se crea otra fecha.
¿Qué pasa si hay 2 horas y 30 minutos entre? Su respuesta resultará en 3 horas. Creo que sería mejor usar piso para que diera 2 horas. Sin embargo, realmente depende de la situación.
Kapitein Witbaard
14
La forma más fácil de obtener el número correcto de horas entre dos fechas (fecha y hora), incluso en los cambios de horario de verano, es usar la diferencia en las marcas de tiempo de Unix. Las marcas de tiempo de Unix son segundos transcurridos desde 1970-01-01T00: 00: 00 UTC, ignorando los segundos intercalares (esto está bien porque probablemente no necesite esta precisión y porque es bastante difícil tener en cuenta los segundos intercalares).
La forma más flexible de convertir una cadena de fecha y hora con información de zona horaria opcional en una marca de tiempo de Unix es construir un objeto DateTime (opcionalmente con una DateTimeZone como segundo argumento en el constructor) y luego llamar a su método getTimestamp .
De un comentario en el manual parece que, para compatibilidad con fechas anteriores a la época, format("U")es preferible agetTimestamp()
Arth
1
@ Arthur, no sé cuándo fue este el caso, pero en mi PHP 5.5.9 ya no es cierto. getTimestamp()ahora devuelve exactamente el mismo valor que format("U"). Sin embargo, el primero es un número entero, mientras que el segundo es una cadena (menos eficiente aquí).
Walter Tross
Genial, tal vez era cierto en una versión anterior ... Sí, un número entero sería más limpio, así que preferiría getTimestamp()estar seguro.
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);
Esta es también una copia del formulario de respuesta 2010.
Daniel W.
2
Desafortunadamente, la solución proporcionada por FaileN no funciona como lo dijo Walter Tross ... ¡los días pueden no ser 24 horas!
Me gusta usar los objetos PHP siempre que sea posible y, para un poco más de flexibilidad, se me ocurrió la siguiente función:
/**
* @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;}
Esto date_diff()crea como un DateTimeInterval, pero con la unidad más alta como horas en lugar de años ... se puede formatear como de costumbre.
$interval = time_diff($date_a, $date_b);
echo $interval->format('%r%H');// For hours (with sign)
NB Lo he usado en format('U')lugar de getTimestamp()por el comentario en el manual . También tenga en cuenta que se requieren 64 bits para fechas posteriores a la época y anteriores a la época negativa.
Esta función le ayuda a calcular los años y meses exactos entre dos fechas determinadas $doj1y $doj. Devuelve el ejemplo 4.3 significa 4 años y 3 meses.
<?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*/}?>
Nota La zona horaria se pasará y se generará como +0:00cuando se usa @en el constructor DateTime. Al usar el DateTime::modify()método, se pasará la marca de tiempo +0:00y se generará la zona horaria actual. Uso alternativo, $date = new DateTime(); $date->setTimestamp($unix_timestamp);ver: 3v4l.org/BoAWI
fyrye
0
El carbono también podría ser una buena forma de hacerlo.
Carbon extiende la clase DateTime para heredar métodos, incluidos diff(). Se añade agradables azúcares como diffInHours, diffInMintutes, diffInSecondsetc.
Primero, debe crear un objeto de intervalo a partir de un rango de fechas. Solo por la redacción utilizada en esta oración, uno puede identificar fácilmente las abstracciones básicas necesarias. Hay un intervalo como concepto, y un par de formas más de implementarlo, incluido el que ya se mencionó, de un rango de fechas. Por lo tanto, un intervalo se ve así:
Además de la respuesta muy útil de @ fyrye, esta es una solución aceptable para el error mencionado ( este ), que DatePeriod resta una hora cuando ingresa al verano, pero no agrega una hora al salir del verano (y por lo tanto, la marcha de Europa / Berlín tiene su 743 horas correctas, pero octubre tiene 744 en lugar de 745 horas):
Contar las horas de un mes (o cualquier período de tiempo), considerando las transiciones DST en ambas direcciones
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
PD: Si marca un período de tiempo (más largo), que conduce a más de esas dos transiciones, mi solución no tocará las horas contadas para reducir el potencial de efectos secundarios divertidos. En tales casos, se debe implementar una solución más complicada. Uno podría iterar sobre todas las transiciones encontradas y comparar la actual con la última y verificar si es una con DST verdadero-> falso.
strftime()
y dividir la diferencia por 3600, pero ¿funcionará siempre? ¡Maldito seas, horario de verano!strtotime()
, siempre funcionará, siempre que use la zona horaria predeterminada O especifique explícitamente el desplazamiento de la zona horaria. No hay razón para maldecir el horario de verano.Respuestas:
Las nuevas versiones de PHP-proporcionan algunas nuevas clases llamadas
DateTime
,DateInterval
,DateTimeZone
yDatePeriod
. Lo bueno de estas clases es que considera diferentes zonas horarias, años bisiestos, segundos bisiestos, horario de verano, etc. Y además es muy fácil de usar. Esto es lo que quiere con la ayuda de estos objetos:El objeto DateInterval, que se devuelve también proporciona otros métodos distintos a
format
. Si desea obtener el resultado solo en horas, puede hacer algo como esto:Y aquí están los enlaces para la documentación:
Todas estas clases también ofrecen una forma procedimental / funcional de operar con fechas. Por lo tanto, eche un vistazo a la descripción general: http://php.net/manual/book.datetime.php
fuente
$date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');
y$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
$diff->d
es igual a 0 (porque estoy tratando de calcular las horas entre dos fechas que tienen exactamente 2 meses de diferencia): Runningvar_dump($diff)
me mostró otro parámetro:,["days"]=>int(61)
así que terminé usando$hours = $diff->days * 24;
, y llegó cerca del "promedio" de 1440 horas dados 2 meses de 30 días, por lo que se ve mucho mejor que un resultado de 0. (Adivinando que mi versión de PHP es un poco vieja ...)fuente
$diff / 3600
?Para proporcionar otro método para
DatePeriod
cuando se usa la zona horaria UTC o GMT .Cuenta horas https://3v4l.org/Mu3HD
Resultado
Cuente las horas con el horario de verano https://3v4l.org/QBQUB
Tenga en cuenta que
DatePeriod
excluye una hora para el horario de verano pero no agrega otra hora cuando finaliza el horario de verano. Por lo tanto, su uso es subjetivo al resultado deseado y al rango de fechas.Ver el informe de errores actual
Resultado
fuente
DateInterval
no acepta valores fraccionarios como en la especificación ISO 8601. EntoncesP1.2Y
no es una duración válida en PHP.iterator_count
, se debe a queDatePeriod
no se pueden generar fechas cuando la fecha de inicio es posterior a la fecha de finalización. Consulte: 3v4l.org/Ypsp1 para usar una fecha negativa, debe especificar un intervalo negativo,DateInterval::createFromDateString('-1 hour');
con una fecha de inicio anterior a la fecha de finalización.DatePeriod
, ya que por defecto incluirá la fecha de inicio entre los períodos especificados a menos que sean menores o iguales a la fecha de inicio. En efecto, le está diciendo a php que cree un período de 1 hora entre las dos fechas, dentro de 1 segundo de tiempo. Debería eliminar los minutos y segundos de sus objetos de fecha, ya que no son relevantes en el cálculo, utilizandoDateTime::setTime(date->format('H'), 0)
. 3v4l.org/K7uss De esta manera, si está por encima del rango por 1 segundo, no se crea otra fecha.tu respuesta es:
round((strtotime($day2) - strtotime($day1))/(60*60))
fuente
La forma más fácil de obtener el número correcto de horas entre dos fechas (fecha y hora), incluso en los cambios de horario de verano, es usar la diferencia en las marcas de tiempo de Unix. Las marcas de tiempo de Unix son segundos transcurridos desde 1970-01-01T00: 00: 00 UTC, ignorando los segundos intercalares (esto está bien porque probablemente no necesite esta precisión y porque es bastante difícil tener en cuenta los segundos intercalares).
La forma más flexible de convertir una cadena de fecha y hora con información de zona horaria opcional en una marca de tiempo de Unix es construir un objeto DateTime (opcionalmente con una DateTimeZone como segundo argumento en el constructor) y luego llamar a su método getTimestamp .
fuente
format("U")
es preferible agetTimestamp()
getTimestamp()
ahora devuelve exactamente el mismo valor queformat("U")
. Sin embargo, el primero es un número entero, mientras que el segundo es una cadena (menos eficiente aquí).getTimestamp()
estar seguro.fuente
Supongo que la función strtotime () acepta este formato de fecha.
fuente
fuente
Desafortunadamente, la solución proporcionada por FaileN no funciona como lo dijo Walter Tross ... ¡los días pueden no ser 24 horas!
Me gusta usar los objetos PHP siempre que sea posible y, para un poco más de flexibilidad, se me ocurrió la siguiente función:
Esto
date_diff()
crea como unDateTimeInterval
, pero con la unidad más alta como horas en lugar de años ... se puede formatear como de costumbre.NB Lo he usado en
format('U')
lugar degetTimestamp()
por el comentario en el manual . También tenga en cuenta que se requieren 64 bits para fechas posteriores a la época y anteriores a la época negativa.fuente
Esta función le ayuda a calcular los años y meses exactos entre dos fechas determinadas
$doj1
y$doj
. Devuelve el ejemplo 4.3 significa 4 años y 3 meses.fuente
<php
debe cambiarse a<?php
O aprobar la edición sugerida, que elimina el error por completo.Esto está funcionando en mi proyecto. Creo que esto te será útil.
Si la fecha es pasada, invertirá 1.
Si la fecha es futura, invertirá 0.
fuente
Para pasar una marca de tiempo de Unix, use esta notación
fuente
+0:00
cuando se usa@
en el constructor DateTime. Al usar elDateTime::modify()
método, se pasará la marca de tiempo+0:00
y se generará la zona horaria actual. Uso alternativo,$date = new DateTime(); $date->setTimestamp($unix_timestamp);
ver: 3v4l.org/BoAWIEl carbono también podría ser una buena forma de hacerlo.
Desde su sitio web:
Ejemplo:
Carbon extiende la clase DateTime para heredar métodos, incluidos
diff()
. Se añade agradables azúcares comodiffInHours
,diffInMintutes
,diffInSeconds
etc.fuente
Primero, debe crear un objeto de intervalo a partir de un rango de fechas. Solo por la redacción utilizada en esta oración, uno puede identificar fácilmente las abstracciones básicas necesarias. Hay un intervalo como concepto, y un par de formas más de implementarlo, incluido el que ya se mencionó, de un rango de fechas. Por lo tanto, un intervalo se ve así:
FromISO8601
tiene la misma semántica: es un objeto de fecha y hora creadofrom iso8601-formatted string
, de ahí el nombre.Cuando tenga un intervalo, puede formatearlo como desee. Si necesita varias horas completas, puede tener
Si quieres un total de horas máximo, aquí tienes:
Para obtener más información sobre este enfoque y algunos ejemplos, consulte esta entrada .
fuente
Además de la respuesta muy útil de @ fyrye, esta es una solución aceptable para el error mencionado ( este ), que DatePeriod resta una hora cuando ingresa al verano, pero no agrega una hora al salir del verano (y por lo tanto, la marcha de Europa / Berlín tiene su 743 horas correctas, pero octubre tiene 744 en lugar de 745 horas):
Contar las horas de un mes (o cualquier período de tiempo), considerando las transiciones DST en ambas direcciones
PD: Si marca un período de tiempo (más largo), que conduce a más de esas dos transiciones, mi solución no tocará las horas contadas para reducir el potencial de efectos secundarios divertidos. En tales casos, se debe implementar una solución más complicada. Uno podría iterar sobre todas las transiciones encontradas y comparar la actual con la última y verificar si es una con DST verdadero-> falso.
fuente
$ diff_min = (strtotime ($ día2) - strtotime ($ día1)) / 60/60; $ tiempo_total = $ diff_min;
Puedes probar este.
fuente