From b8b7580153f47ebb90f8237be49e0e6c98a655e2 Mon Sep 17 00:00:00 2001 From: Sunch <120295462+Sunch233@users.noreply.github.com> Date: Sun, 28 Sep 2025 00:10:19 +0800 Subject: [PATCH] Fixed timezone error caused by WMIC deprecation in new Windows (#6721) Timezone information is now fetched from the registry instead of using WMIC --- src/utils/Timezone.php | 82 ++++++++++++++++++++++++++++-------------- 1 file changed, 56 insertions(+), 26 deletions(-) diff --git a/src/utils/Timezone.php b/src/utils/Timezone.php index 6723b12eb..849daadc5 100644 --- a/src/utils/Timezone.php +++ b/src/utils/Timezone.php @@ -26,9 +26,11 @@ namespace pocketmine\utils; use function abs; use function date_default_timezone_set; use function date_parse; +use function escapeshellarg; use function exec; use function file_get_contents; -use function implode; +use function floor; +use function hexdec; use function ini_get; use function ini_set; use function is_array; @@ -37,6 +39,7 @@ use function json_decode; use function parse_ini_file; use function preg_match; use function readlink; +use function sprintf; use function str_contains; use function str_replace; use function str_starts_with; @@ -105,40 +108,67 @@ abstract class Timezone{ public static function detectSystemTimezone() : string|false{ switch(Utils::getOS()){ case Utils::OS_WINDOWS: - $regex = '/(UTC)(\+*\-*\d*\d*\:*\d*\d*)/'; + $keyPath = 'HKLM\\SYSTEM\\CurrentControlSet\\Control\\TimeZoneInformation'; /* - * wmic timezone get Caption - * Get the timezone offset + * Get the timezone offset through the registry * * Sample Output var_dump - * array(3) { - * [0] => - * string(7) "Caption" - * [1] => - * string(20) "(UTC+09:30) Adelaide" - * [2] => - * string(0) "" - * } + * array(13) { + * [0]=> + * string(0) "" + * [1]=> + * string(71) "HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control\TimeZoneInformation" + * [2]=> + * string(35) " Bias REG_DWORD 0xfffffe20" + * [3]=> + * string(43) " DaylightBias REG_DWORD 0xffffffc4" + * [4]=> + * string(45) " DaylightName REG_SZ @tzres.dll,-571" + * [5]=> + * string(67) " DaylightStart REG_BINARY 00000000000000000000000000000000" + * [6]=> + * string(36) " StandardBias REG_DWORD 0x0" + * [7]=> + * string(45) " StandardName REG_SZ @tzres.dll,-572" + * [8]=> + * string(67) " StandardStart REG_BINARY 00000000000000000000000000000000" + * [9]=> + * string(52) " TimeZoneKeyName REG_SZ China Standard Time" + * [10]=> + * string(51) " DynamicDaylightTimeDisabled REG_DWORD 0x0" + * [11]=> + * string(45) " ActiveTimeBias REG_DWORD 0xfffffe20" + * [12]=> + * string(0) "" + * } */ - exec("wmic timezone get Caption", $output); + exec("reg query " . escapeshellarg($keyPath), $output); - $string = trim(implode("\n", $output)); + foreach($output as $line){ + if(preg_match('/ActiveTimeBias\s+REG_DWORD\s+0x([0-9a-fA-F]+)/', $line, $matches) > 0){ + $offsetMinutes = Binary::signInt((int) hexdec(trim($matches[1]))); - //Detect the Time Zone string - preg_match($regex, $string, $matches); + if($offsetMinutes === 0){ + return "UTC"; + } - if(!isset($matches[2])){ - return false; + $sign = $offsetMinutes <= 0 ? '+' : '-'; //windows timezone + and - are opposite + $absMinutes = abs($offsetMinutes); + $hours = floor($absMinutes / 60); + $minutes = $absMinutes % 60; + + $offset = sprintf( + "%s%02d:%02d", + $sign, + $hours, + $minutes + ); + + return self::parseOffset($offset); + } } - - $offset = $matches[2]; - - if($offset === ""){ - return "UTC"; - } - - return self::parseOffset($offset); + return false; case Utils::OS_LINUX: // Ubuntu / Debian. $data = @file_get_contents('/etc/timezone');