diff --git a/src/pocketmine/PocketMine.php b/src/pocketmine/PocketMine.php index 246d8beff..5ad5050f8 100644 --- a/src/pocketmine/PocketMine.php +++ b/src/pocketmine/PocketMine.php @@ -74,6 +74,7 @@ namespace pocketmine { use pocketmine\utils\MainLogger; use pocketmine\utils\ServerKiller; use pocketmine\utils\Terminal; + use pocketmine\utils\Timezone; use pocketmine\utils\Utils; use pocketmine\wizard\SetupWizard; use raklib\RakLib; @@ -234,176 +235,16 @@ namespace pocketmine { mkdir(\pocketmine\DATA, 0777, true); } - //Logger has a dependency on timezone, so we'll set it to UTC until we can get the actual timezone. - date_default_timezone_set("UTC"); + //Logger has a dependency on timezone + $tzError = Timezone::init(); $logger = new MainLogger(\pocketmine\DATA . "server.log"); $logger->registerStatic(); - do{ - $timezone = ini_get("date.timezone"); - if($timezone !== ""){ - /* - * This is here so that people don't come to us complaining and fill up the issue tracker when they put - * an incorrect timezone abbreviation in php.ini apparently. - */ - if(strpos($timezone, "/") === false){ - $default_timezone = timezone_name_from_abbr($timezone); - if($default_timezone !== false){ - ini_set("date.timezone", $default_timezone); - date_default_timezone_set($default_timezone); - break; - }else{ - //Bad php.ini value, try another method to detect timezone - $logger->warning("Timezone \"$timezone\" could not be parsed as a valid timezone from php.ini, falling back to auto-detection"); - } - }else{ - date_default_timezone_set($timezone); - break; - } - } - - if(($timezone = detect_system_timezone()) and date_default_timezone_set($timezone)){ - //Success! Timezone has already been set and validated in the if statement. - //This here is just for redundancy just in case some program wants to read timezone data from the ini. - ini_set("date.timezone", $timezone); - break; - } - - if($response = Utils::getURL("http://ip-api.com/json") //If system timezone detection fails or timezone is an invalid value. - and $ip_geolocation_data = json_decode($response, true) - and $ip_geolocation_data['status'] !== 'fail' - and date_default_timezone_set($ip_geolocation_data['timezone']) - ){ - //Again, for redundancy. - ini_set("date.timezone", $ip_geolocation_data['timezone']); - break; - } - - ini_set("date.timezone", "UTC"); - date_default_timezone_set("UTC"); - $logger->warning("Timezone could not be automatically determined or was set to an invalid value. An incorrect timezone will result in incorrect timestamps on console logs. It has been set to \"UTC\" by default. You can change it on the php.ini file."); - }while(false); - - - function detect_system_timezone(){ - switch(Utils::getOS()){ - case 'win': - $regex = '/(UTC)(\+*\-*\d*\d*\:*\d*\d*)/'; - - /* - * wmic timezone get Caption - * Get the timezone offset - * - * Sample Output var_dump - * array(3) { - * [0] => - * string(7) "Caption" - * [1] => - * string(20) "(UTC+09:30) Adelaide" - * [2] => - * string(0) "" - * } - */ - exec("wmic timezone get Caption", $output); - - $string = trim(implode("\n", $output)); - - //Detect the Time Zone string - preg_match($regex, $string, $matches); - - if(!isset($matches[2])){ - return false; - } - - $offset = $matches[2]; - - if($offset == ""){ - return "UTC"; - } - - return parse_offset($offset); - case 'linux': - // Ubuntu / Debian. - if(file_exists('/etc/timezone')){ - $data = file_get_contents('/etc/timezone'); - if($data){ - return trim($data); - } - } - - // RHEL / CentOS - if(file_exists('/etc/sysconfig/clock')){ - $data = parse_ini_file('/etc/sysconfig/clock'); - if(!empty($data['ZONE'])){ - return trim($data['ZONE']); - } - } - - //Portable method for incompatible linux distributions. - - $offset = trim(exec('date +%:z')); - - if($offset == "+00:00"){ - return "UTC"; - } - - return parse_offset($offset); - case 'mac': - if(is_link('/etc/localtime')){ - $filename = readlink('/etc/localtime'); - if(strpos($filename, '/usr/share/zoneinfo/') === 0){ - $timezone = substr($filename, 20); - return trim($timezone); - } - } - - return false; - default: - return false; - } - } - - /** - * @param string $offset In the format of +09:00, +02:00, -04:00 etc. - * - * @return string|bool - */ - function parse_offset($offset){ - //Make signed offsets unsigned for date_parse - if(strpos($offset, '-') !== false){ - $negative_offset = true; - $offset = str_replace('-', '', $offset); - }else{ - if(strpos($offset, '+') !== false){ - $negative_offset = false; - $offset = str_replace('+', '', $offset); - }else{ - return false; - } - } - - $parsed = date_parse($offset); - $offset = $parsed['hour'] * 3600 + $parsed['minute'] * 60 + $parsed['second']; - - //After date_parse is done, put the sign back - if($negative_offset == true){ - $offset = -abs($offset); - } - - //And then, look the offset up. - //timezone_name_from_abbr is not used because it returns false on some(most) offsets because it's mapping function is weird. - //That's been a bug in PHP since 2008! - foreach(timezone_abbreviations_list() as $zones){ - foreach($zones as $timezone){ - if($timezone['offset'] == $offset){ - return $timezone['timezone_id']; - } - } - } - - return false; + foreach($tzError as $e){ + $logger->warning($e); } + unset($tzError); if(isset($opts["enable-profiler"])){ if(function_exists("profiler_enable")){ diff --git a/src/pocketmine/utils/Timezone.php b/src/pocketmine/utils/Timezone.php new file mode 100644 index 000000000..292cf260a --- /dev/null +++ b/src/pocketmine/utils/Timezone.php @@ -0,0 +1,201 @@ + + * string(7) "Caption" + * [1] => + * string(20) "(UTC+09:30) Adelaide" + * [2] => + * string(0) "" + * } + */ + exec("wmic timezone get Caption", $output); + + $string = trim(implode("\n", $output)); + + //Detect the Time Zone string + preg_match($regex, $string, $matches); + + if(!isset($matches[2])){ + return false; + } + + $offset = $matches[2]; + + if($offset == ""){ + return "UTC"; + } + + return self::parseOffset($offset); + case 'linux': + // Ubuntu / Debian. + if(file_exists('/etc/timezone')){ + $data = file_get_contents('/etc/timezone'); + if($data){ + return trim($data); + } + } + + // RHEL / CentOS + if(file_exists('/etc/sysconfig/clock')){ + $data = parse_ini_file('/etc/sysconfig/clock'); + if(!empty($data['ZONE'])){ + return trim($data['ZONE']); + } + } + + //Portable method for incompatible linux distributions. + + $offset = trim(exec('date +%:z')); + + if($offset == "+00:00"){ + return "UTC"; + } + + return self::parseOffset($offset); + case 'mac': + if(is_link('/etc/localtime')){ + $filename = readlink('/etc/localtime'); + if(strpos($filename, '/usr/share/zoneinfo/') === 0){ + $timezone = substr($filename, 20); + return trim($timezone); + } + } + + return false; + default: + return false; + } + } + + + /** + * @param string $offset In the format of +09:00, +02:00, -04:00 etc. + * + * @return string|bool + */ + private static function parseOffset($offset){ + //Make signed offsets unsigned for date_parse + if(strpos($offset, '-') !== false){ + $negative_offset = true; + $offset = str_replace('-', '', $offset); + }else{ + if(strpos($offset, '+') !== false){ + $negative_offset = false; + $offset = str_replace('+', '', $offset); + }else{ + return false; + } + } + + $parsed = date_parse($offset); + $offset = $parsed['hour'] * 3600 + $parsed['minute'] * 60 + $parsed['second']; + + //After date_parse is done, put the sign back + if($negative_offset == true){ + $offset = -abs($offset); + } + + //And then, look the offset up. + //timezone_name_from_abbr is not used because it returns false on some(most) offsets because it's mapping function is weird. + //That's been a bug in PHP since 2008! + foreach(timezone_abbreviations_list() as $zones){ + foreach($zones as $timezone){ + if($timezone['offset'] == $offset){ + return $timezone['timezone_id']; + } + } + } + + return false; + } +}