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é!