求PHP代码检测时区变更中的冲突时间(重复/无效时间)
检测PHP中时区变更导致的时间冲突(无效/重复时间)
时区变更会引发两类时间冲突问题:
- 重复时间:如中欧时区十月最后周日的3点调回至2点,此时
02:30这个本地时间对应两个不同的UTC时间(变更前的夏令时、变更后的冬令时) - 无效时间:如中欧时区三月最后周日的2点调快至3点,
2024-03-31 02:30这类本地时间实际不存在(2点到3点的区间被跳过)
以下是改进后的PHP代码,可检测这两类冲突并给出预警,同时保留原有功能:
<?php $timezoneStr = $_GET["tzname"] ?? "Europe/Paris"; // 增加默认值避免无参数报错 $timezone = new DateTimeZone($timezoneStr); echo "TimeZone = " . $timezoneStr; $datetime = "2024-03-31 02:30:00"; print '<br>'; echo "Local = " . $datetime; // 检测无效时间(时区调快导致的缺失时间) function isInvalidLocalTime(string $localDatetime, DateTimeZone $timezone): bool { $original = DateTime::createFromFormat('Y-m-d H:i:s', $localDatetime, $timezone); if (!$original) return false; // PHP会自动修正无效时间,通过转UTC再转回原时区对比判断 $adjusted = (clone $original)->setTimezone(new DateTimeZone('UTC'))->setTimezone($timezone); return $original->format('Y-m-d H:i:s') !== $adjusted->format('Y-m-d H:i:s'); } // 检测重复时间(时区调回导致的歧义时间) function isAmbiguousLocalTime(string $localDatetime, DateTimeZone $timezone): bool { $current = DateTime::createFromFormat('Y-m-d H:i:s', $localDatetime, $timezone); if (!$current) return false; // 对比当前时间与前一分钟的时区偏移量,调回时偏移量会减少 $currentOffset = $current->getOffset(); $prevMinute = (clone $current)->modify('-1 minute'); $prevOffset = $prevMinute->getOffset(); return $prevOffset > $currentOffset; } // 输出冲突预警 $isInvalid = isInvalidLocalTime($datetime, $timezone); $isAmbiguous = isAmbiguousLocalTime($datetime, $timezone); if ($isInvalid) { print '<br><span style="color:red;">预警:该本地时间不存在(时区调快导致时间区间缺失)</span>'; } elseif ($isAmbiguous) { print '<br><span style="color:orange;">预警:该本地时间对应两个实际时间(时区调回导致时间重复)</span>'; } // 保留原有时间转换与处理逻辑 $given = new DateTime($datetime, $timezone); $given->setTimezone(new DateTimeZone("UTC")); $output = $given->format("Y-m-d H:i:s"); print '<br>'; echo "UTC = " . $output; $ano = substr($output, 0, 4); $mes = substr($output, 5, 2); $dia = substr($output, 8, 2); $hor = substr($output, 11, 2); $min = substr($output, 14, 2); $sec = substr($output, 17, 2); print '<br>'; echo "Year = " . $ano; print '<br>'; echo "Month = " . $mes; print '<br>'; echo "Day = " . $dia; print '<br>'; echo "Hours = " . $hor; print '<br>'; echo "Minutes = " . $min; print '<br>'; echo "Seconds = " . $sec; $meses = ["Enero", "Febrero", "Marzo", "Abril", "Mayo", "Junio", "Julio", "Agosto", "Septiembre", "Octubre", "Noviembre", "Diciembre"]; $mesStr = $meses[intval($mes) - 1]; print '<br>'; echo $dia . "-" . $mesStr . "-" . $ano . " a las " . $hor . ":" . $min . ":" . $sec; $secondsInTime = intval($hor) * 3600 + intval($min) * 60 + intval($sec); // 修正原代码类型转换错误 $secondsInTimeDec = $secondsInTime / 86400; $jd = GregorianToJD($mes, $dia, $ano); $jdDec = intval($jd) + $secondsInTimeDec; print '<br>'; echo "JulianDay = " . $jdDec; ?>
关键逻辑说明
- 无效时间检测:PHP会自动将不存在的本地时间修正为下一个有效时间,通过将原时间转UTC再转回原时区,对比前后时间是否一致即可判断是否为无效时间。
- 重复时间检测:时区调回(夏令时转冬令时)时,时区偏移量会减少,对比当前时间与前一分钟的偏移量,即可判断是否处于重复时间区间。
- 原代码修复:修正了
$secondsInTime计算时的类型转换错误,增加时区默认值避免无参数报错。
内容的提问来源于stack exchange,提问作者Jaime Reynolds
相关产品推荐
相关产品推荐

