Ma réponse initiale aurait été de transformer les deux valeurs en horodatages en utilisant strftime()et de diviser la différence par 3600, mais cela fonctionnera-t-il toujours? Damn you, heure d'été!
Pekka
@Pekka: non ça ne marchera pas toujours je suppose ... Jetez un œil à ma réponse. Là, j'ai posté une solution en tenant compte des fuseaux horaires, des années bissextiles, des secondes bissextiles et dst :)
Fidi
@Pekka, si vous l'utilisez strtotime()fonctionnera toujours, tant que vous utilisez le fuseau horaire par défaut OU spécifiez explicitement le décalage du fuseau horaire. Aucune raison de maudire le DST.
Walter Tross
Réponses:
205
Les versions PHP plus récentes fournissent de nouvelles classes appelées DateTime, DateInterval, DateTimeZoneet DatePeriod. Ce qui est cool à propos de ces cours, c'est qu'ils prennent en compte différents fuseaux horaires, années bissextiles, secondes intercalaires, heure d'été, etc. Et en plus de cela, il est très facile à utiliser. Voici ce que vous voulez avec l'aide de ces objets:
// 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');
L'objet DateInterval, qui est retourné, fournit également d'autres méthodes que format. Si vous voulez le résultat en heures seulement, vous pouvez faire quelque chose comme ceci:
Toutes ces classes offrent également une manière procédurale / fonctionnelle d'opérer avec des dates. Jetez donc un œil à l'aperçu: http://php.net/manual/book.datetime.php
+1 Bon travail! Cela semble solide et constitue un bon aperçu. Il est important de noter que les calculs peuvent varier en fonction du fuseau horaire en raison de différentes règles d'heure d'été, c'est donc probablement une bonne idée de toujours définir la zone et de ne pas se fier aux paramètres du serveur.
Pekka
Oui. Avec cet objet, vous pouvez même calculer entre des dates dans différents fuseaux horaires. $date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');et$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
Fidi
3
Si quelqu'un rencontre le même problème que moi, où $diff->dest égal à 0 (parce que j'essaie de calculer les heures entre deux dates qui sont exactement à 2 mois d'intervalle): Running var_dump($diff)m'a montré un autre paramètre:, ["days"]=>int(61)alors j'ai fini par utiliser $hours = $diff->days * 24;, et il est venu proche de la "moyenne" de 1440 heures pour 2 mois de 30 jours, donc cela semble beaucoup mieux qu'un résultat de 0. (Je suppose que ma version PHP est un peu ancienne ...)
semmelbroesel
2
Je veux dire, dans de nombreuses régions du monde, une année compte une journée de 23 heures et une journée de 25 heures.
Walter Tross
4
@Amal Murali, vous avez donc décidé d'attribuer le bonus à cette réponse, qui est FAUX? Avez-vous essayé de calculer avec cette réponse le nombre d'heures entre midi du premier janvier et midi du premier juin, dans n'importe quel fuseau horaire qui a DST (heure d'été)? Vous obtiendrez un résultat pair, tandis que le vrai résultat est étrange.
$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)';
Veuillez noter que cela DatePeriodexclut une heure pour l'heure d'été mais n'ajoute pas une autre heure lorsque l'heure d'été se termine. Son utilisation est donc subjective par rapport au résultat souhaité et à la plage de dates.
Pour toute personne aussi confuse que moi en voyant le paramètre du constructeur DateInterval, le format est une durée ISO 8601
TheKarateKid
Une autre note est que DateIntervaln'accepte pas les valeurs fractionnaires comme dans la spécification ISO 8601. Ce P1.2Yn'est donc pas une durée valide en PHP.
fyrye
REMARQUE: iterator_count renverra uniquement des résultats positifs. Si la première date est plus grande que la seconde, le résultat de la différence sera 0.
SubjectDelta
1
@SubjectDelta le problème n'est pas lié iterator_count, il est dû à l' DatePeriodimpossibilité de générer des dates lorsque la date de début est dans le futur à partir de la date de fin. Voir: 3v4l.org/Ypsp1 pour utiliser une date négative, vous devez spécifier un intervalle négatif, DateInterval::createFromDateString('-1 hour');avec une date de début dans le passé de à partir de la date de fin.
fyrye
1
@SubjectDelta c'est une autre nuance de DatePeriod, car par défaut, il inclura la date de début entre les périodes spécifiées à moins qu'elles ne soient inférieures ou égales à la date de début. En effet, vous dites à php de créer une période d'une heure entre les deux dates, dans un délai d'une seconde. Vous devrez supprimer les minutes et les secondes de vos objets de date, car ils ne sont pas pertinents dans le calcul, en utilisant DateTime::setTime(date->format('H'), 0). 3v4l.org/K7uss De cette façon, si vous dépassez la plage de 1 seconde, une autre date n'est pas créée.
Et s'il y a 2 heures et 30 minutes entre les deux? Votre réponse se traduira par 3 heures. Je pense qu'il serait préférable d'utiliser le sol pour que cela donne 2 heures. Cela dépend vraiment de la situation.
Kapitein Witbaard
14
Le moyen le plus simple d'obtenir le nombre d'heures correct entre deux dates (datetimes), même en cas de changement d'heure d'été, est d'utiliser la différence d'horodatage Unix. Les horodatages Unix sont des secondes écoulées depuis 1970-01-01T00: 00: 00 UTC, ignorant les secondes intercalaires (c'est OK parce que vous n'avez probablement pas besoin de cette précision, et parce qu'il est assez difficile de prendre en compte les secondes intercalaires).
Le moyen le plus flexible de convertir une chaîne datetime avec des informations de fuseau horaire facultatives en un horodatage Unix est de construire un objet DateTime (éventuellement avec un DateTimeZone comme second argument dans le constructeur), puis d'appeler sa méthode getTimestamp .
D'après un commentaire dans le manuel, il semble que, pour la compatibilité avec les dates antérieures à l'époque, il format("U")est préférable degetTimestamp()
Arth
1
@Arth, je ne sais pas quand c'était le cas, mais dans mon PHP 5.5.9 ce n'est plus vrai. getTimestamp()renvoie maintenant exactement la même valeur que format("U"). Le premier est un entier, tandis que le second est une chaîne (moins efficace ici).
Walter Tross
Cool, c'était peut-être vrai dans une version antérieure. Oui, un entier serait plus propre, donc je préférerais getTimestamp()que je puisse être sûr.
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);
Ceci est également une copie du formulaire de réponse 2010.
Daniel W.
2
Malheureusement, la solution fournie par FaileN ne fonctionne pas comme indiqué par Walter Tross. Les jours peuvent ne pas être 24 heures!
J'aime utiliser les objets PHP dans la mesure du possible et pour un peu plus de flexibilité, j'ai proposé la fonction suivante:
/**
* @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;}
Cela date_diff()crée un DateTimeInterval, mais avec l'unité la plus élevée en heures plutôt qu'en années .. il peut être formaté comme d'habitude.
$interval = time_diff($date_a, $date_b);
echo $interval->format('%r%H');// For hours (with sign)
NB je l'ai utilisé format('U')au lieu de à getTimestamp()cause du commentaire dans le manuel . Notez également que 64 bits est requis pour les dates post-époque et pré-période négative!
Cette fonction vous aide à calculer les années et les mois exacts entre deux dates données, $doj1et $doj. Il renvoie l'exemple 4.3 signifie 4 ans et 3 mois.
<?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*/}?>
La modification suggérée est trop mineure, mais <phpdoit être remplacée par <?phpOu approuver la modification suggérée, ce qui supprime le bogue dans son ensemble.
anishsane
0
Cela fonctionne dans mon projet. Je pense que cela vous sera utile.
Si la date est dans le passé, alors inverser sera 1.
Si la date est dans le futur, alors inverser sera 0.
Remarque Le fuseau horaire sera passé et affiché comme +0:00lors de son utilisation @dans le constructeur DateTime. Lors de l'utilisation, la DateTime::modify()méthode transmettra l'horodatage +0:00et affichera le fuseau horaire actuel. Vous pouvez également utiliser $date = new DateTime(); $date->setTimestamp($unix_timestamp);voir: 3v4l.org/BoAWI
fyrye
0
Le carbone pourrait également être une bonne solution.
Carbon étend la classe DateTime pour hériter des méthodes, y compris diff(). Il ajoute des sucres comme beaux diffInHours, diffInMintutes, diffInSecondsetc.
Tout d'abord, vous devez créer un objet intervalle à partir d'une plage de dates. Par le libellé utilisé dans cette phrase seulement, on peut facilement identifier les abstractions de base nécessaires. Il existe un intervalle en tant que concept, et quelques autres façons de le mettre en œuvre, incluent celui déjà mentionné - à partir d'une plage de dates. Ainsi, un intervalle ressemble à ça:
FromISO8601a la même sémantique: c'est un objet datetime créé from iso8601-formatted string, d'où le nom.
Lorsque vous avez un intervalle, vous pouvez le formater comme vous le souhaitez. Si vous avez besoin d'un certain nombre d'heures complètes, vous pouvez avoir
(newTotalFullHours($interval))->value();
Si vous voulez un nombre total d'heures plafonné, allez-y:
(newTotalCeiledHours($interval))->value();
Pour plus d'informations sur cette approche et quelques exemples, consultez cette entrée .
En plus de la réponse très utile de @ fyrye, il s'agit d'une solution de contournement correcte pour le bogue mentionné ( celui-ci ), que DatePeriod soustrait une heure lors de l'entrée en été, mais n'ajoute pas une heure en quittant l'heure d'été (et donc la marche d'Europe / Berlin a son correct 743 heures mais octobre a 744 heures au lieu de 745 heures):
Compter les heures d'un mois (ou n'importe quel intervalle de temps), en tenant compte des transitions DST dans les deux sens
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 Si vous cochez une période (plus longue), ce qui conduit à plus que ces deux transitions, ma solution de contournement ne touchera pas les heures comptées pour réduire le potentiel d'effets secondaires amusants. Dans de tels cas, une solution plus compliquée doit être mise en œuvre. On pourrait parcourir toutes les transitions trouvées et comparer le courant avec le dernier et vérifier s'il en est un avec DST true-> false.
strftime()
et de diviser la différence par 3600, mais cela fonctionnera-t-il toujours? Damn you, heure d'été!strtotime()
fonctionnera toujours, tant que vous utilisez le fuseau horaire par défaut OU spécifiez explicitement le décalage du fuseau horaire. Aucune raison de maudire le DST.Réponses:
Les versions PHP plus récentes fournissent de nouvelles classes appelées
DateTime
,DateInterval
,DateTimeZone
etDatePeriod
. Ce qui est cool à propos de ces cours, c'est qu'ils prennent en compte différents fuseaux horaires, années bissextiles, secondes intercalaires, heure d'été, etc. Et en plus de cela, il est très facile à utiliser. Voici ce que vous voulez avec l'aide de ces objets:L'objet DateInterval, qui est retourné, fournit également d'autres méthodes que
format
. Si vous voulez le résultat en heures seulement, vous pouvez faire quelque chose comme ceci:Et voici les liens vers la documentation:
Toutes ces classes offrent également une manière procédurale / fonctionnelle d'opérer avec des dates. Jetez donc un œil à l'aperçu: http://php.net/manual/book.datetime.php
la source
$date1 = new DateTime('2006-04-12T12:30:00 Europe/Berlin');
et$date2 = new DateTime('2006-04-14T11:30:00 America/New_York');
$diff->d
est égal à 0 (parce que j'essaie de calculer les heures entre deux dates qui sont exactement à 2 mois d'intervalle): Runningvar_dump($diff)
m'a montré un autre paramètre:,["days"]=>int(61)
alors j'ai fini par utiliser$hours = $diff->days * 24;
, et il est venu proche de la "moyenne" de 1440 heures pour 2 mois de 30 jours, donc cela semble beaucoup mieux qu'un résultat de 0. (Je suppose que ma version PHP est un peu ancienne ...)la source
$diff / 3600
?Pour fournir une autre méthode
DatePeriod
lors de l'utilisation du fuseau horaire UTC ou GMT .Compter les heures https://3v4l.org/Mu3HD
Résultat
Compter les heures avec l'heure d'été https://3v4l.org/QBQUB
Veuillez noter que cela
DatePeriod
exclut une heure pour l'heure d'été mais n'ajoute pas une autre heure lorsque l'heure d'été se termine. Son utilisation est donc subjective par rapport au résultat souhaité et à la plage de dates.Voir le rapport de bogue actuel
Résultat
la source
DateInterval
n'accepte pas les valeurs fractionnaires comme dans la spécification ISO 8601. CeP1.2Y
n'est donc pas une durée valide en PHP.iterator_count
, il est dû à l'DatePeriod
impossibilité de générer des dates lorsque la date de début est dans le futur à partir de la date de fin. Voir: 3v4l.org/Ypsp1 pour utiliser une date négative, vous devez spécifier un intervalle négatif,DateInterval::createFromDateString('-1 hour');
avec une date de début dans le passé de à partir de la date de fin.DatePeriod
, car par défaut, il inclura la date de début entre les périodes spécifiées à moins qu'elles ne soient inférieures ou égales à la date de début. En effet, vous dites à php de créer une période d'une heure entre les deux dates, dans un délai d'une seconde. Vous devrez supprimer les minutes et les secondes de vos objets de date, car ils ne sont pas pertinents dans le calcul, en utilisantDateTime::setTime(date->format('H'), 0)
. 3v4l.org/K7uss De cette façon, si vous dépassez la plage de 1 seconde, une autre date n'est pas créée.votre réponse est:
round((strtotime($day2) - strtotime($day1))/(60*60))
la source
Le moyen le plus simple d'obtenir le nombre d'heures correct entre deux dates (datetimes), même en cas de changement d'heure d'été, est d'utiliser la différence d'horodatage Unix. Les horodatages Unix sont des secondes écoulées depuis 1970-01-01T00: 00: 00 UTC, ignorant les secondes intercalaires (c'est OK parce que vous n'avez probablement pas besoin de cette précision, et parce qu'il est assez difficile de prendre en compte les secondes intercalaires).
Le moyen le plus flexible de convertir une chaîne datetime avec des informations de fuseau horaire facultatives en un horodatage Unix est de construire un objet DateTime (éventuellement avec un DateTimeZone comme second argument dans le constructeur), puis d'appeler sa méthode getTimestamp .
la source
format("U")
est préférable degetTimestamp()
getTimestamp()
renvoie maintenant exactement la même valeur queformat("U")
. Le premier est un entier, tandis que le second est une chaîne (moins efficace ici).getTimestamp()
que je puisse être sûr.la source
Je suppose que la fonction strtotime () accepte ce format de date.
la source
la source
Malheureusement, la solution fournie par FaileN ne fonctionne pas comme indiqué par Walter Tross. Les jours peuvent ne pas être 24 heures!
J'aime utiliser les objets PHP dans la mesure du possible et pour un peu plus de flexibilité, j'ai proposé la fonction suivante:
Cela
date_diff()
crée unDateTimeInterval
, mais avec l'unité la plus élevée en heures plutôt qu'en années .. il peut être formaté comme d'habitude.NB je l'ai utilisé
format('U')
au lieu de àgetTimestamp()
cause du commentaire dans le manuel . Notez également que 64 bits est requis pour les dates post-époque et pré-période négative!la source
Cette fonction vous aide à calculer les années et les mois exacts entre deux dates données,
$doj1
et$doj
. Il renvoie l'exemple 4.3 signifie 4 ans et 3 mois.la source
<php
doit être remplacée par<?php
Ou approuver la modification suggérée, ce qui supprime le bogue dans son ensemble.Cela fonctionne dans mon projet. Je pense que cela vous sera utile.
Si la date est dans le passé, alors inverser sera 1.
Si la date est dans le futur, alors inverser sera 0.
la source
Pour transmettre un horodatage unix, utilisez cette notation
la source
+0:00
lors de son utilisation@
dans le constructeur DateTime. Lors de l'utilisation, laDateTime::modify()
méthode transmettra l'horodatage+0:00
et affichera le fuseau horaire actuel. Vous pouvez également utiliser$date = new DateTime(); $date->setTimestamp($unix_timestamp);
voir: 3v4l.org/BoAWILe carbone pourrait également être une bonne solution.
Depuis leur site Web:
Exemple:
Carbon étend la classe DateTime pour hériter des méthodes, y compris
diff()
. Il ajoute des sucres comme beauxdiffInHours
,diffInMintutes
,diffInSeconds
etc.la source
Tout d'abord, vous devez créer un objet intervalle à partir d'une plage de dates. Par le libellé utilisé dans cette phrase seulement, on peut facilement identifier les abstractions de base nécessaires. Il existe un intervalle en tant que concept, et quelques autres façons de le mettre en œuvre, incluent celui déjà mentionné - à partir d'une plage de dates. Ainsi, un intervalle ressemble à ça:
FromISO8601
a la même sémantique: c'est un objet datetime crééfrom iso8601-formatted string
, d'où le nom.Lorsque vous avez un intervalle, vous pouvez le formater comme vous le souhaitez. Si vous avez besoin d'un certain nombre d'heures complètes, vous pouvez avoir
Si vous voulez un nombre total d'heures plafonné, allez-y:
Pour plus d'informations sur cette approche et quelques exemples, consultez cette entrée .
la source
En plus de la réponse très utile de @ fyrye, il s'agit d'une solution de contournement correcte pour le bogue mentionné ( celui-ci ), que DatePeriod soustrait une heure lors de l'entrée en été, mais n'ajoute pas une heure en quittant l'heure d'été (et donc la marche d'Europe / Berlin a son correct 743 heures mais octobre a 744 heures au lieu de 745 heures):
Compter les heures d'un mois (ou n'importe quel intervalle de temps), en tenant compte des transitions DST dans les deux sens
PS Si vous cochez une période (plus longue), ce qui conduit à plus que ces deux transitions, ma solution de contournement ne touchera pas les heures comptées pour réduire le potentiel d'effets secondaires amusants. Dans de tels cas, une solution plus compliquée doit être mise en œuvre. On pourrait parcourir toutes les transitions trouvées et comparer le courant avec le dernier et vérifier s'il en est un avec DST true-> false.
la source
Vous pouvez essayer celui-ci.
la source