从纬度和经度获取PHP时区名称?

23

有没有一种方法可以通过用户的纬度和经度获取他们所在的时区?不仅仅是偏移量,而是他们实际所在的时区。

本质上来说,我正在寻找DateTimeZone :: getLocation的相反极点,它返回某个时区的纬度和经度。


是的,我之前也查过geonames,但据我所知它并没有提供实际的时区名称。我错了,现在感觉很愚蠢。 - Navarr
6个回答

33

对于那些想要从国家代码、纬度和经度获取时区的人。(如果您的服务器上安装了geoip模块,则很容易获得它)

试试这个,我添加了一个距离计算 - 仅适用于具有多个时区的国家。哦,国家代码是两个字母的ISO代码。

// ben@jp

function get_nearest_timezone($cur_lat, $cur_long, $country_code = '') {
    $timezone_ids = ($country_code) ? DateTimeZone::listIdentifiers(DateTimeZone::PER_COUNTRY, $country_code)
                                    : DateTimeZone::listIdentifiers();

    if($timezone_ids && is_array($timezone_ids) && isset($timezone_ids[0])) {

        $time_zone = '';
        $tz_distance = 0;

        //only one identifier?
        if (count($timezone_ids) == 1) {
            $time_zone = $timezone_ids[0];
        } else {

            foreach($timezone_ids as $timezone_id) {
                $timezone = new DateTimeZone($timezone_id);
                $location = $timezone->getLocation();
                $tz_lat   = $location['latitude'];
                $tz_long  = $location['longitude'];

                $theta    = $cur_long - $tz_long;
                $distance = (sin(deg2rad($cur_lat)) * sin(deg2rad($tz_lat))) 
                + (cos(deg2rad($cur_lat)) * cos(deg2rad($tz_lat)) * cos(deg2rad($theta)));
                $distance = acos($distance);
                $distance = abs(rad2deg($distance));
                // echo '<br />'.$timezone_id.' '.$distance; 

                if (!$time_zone || $tz_distance > $distance) {
                    $time_zone   = $timezone_id;
                    $tz_distance = $distance;
                } 

            }
        }
        return  $time_zone;
    }
    return 'unknown';
}
//timezone for one NY co-ordinate
echo get_nearest_timezone(40.772222,-74.164581) ;
// more faster and accurate if you can pass the country code 
echo get_nearest_timezone(40.772222, -74.164581, 'US') ;

这可能是一个更好的答案。我需要进行一些测试。为什么要有国家代码?是否存在在多个国家的位置? - Navarr
好的解决方案。谢谢。 - ineersa
很好,有时区问题,但在使用国家代码时准确无误。 - rushil

5

3
GeoNames会在其数据库中找到最近的记录并确定其所属的时区。如果该记录位于时区边界的另一侧,那么就可能会出现错误。 - James D

4
一个好的资源是谷歌时区API。
文档:https://developers.google.com/maps/documentation/timezone/ 它需要纬度经度,并返回如下的数组:
array(
    'dstOffset' => (int) 3600,
    'rawOffset' => (int) -18000,
    'status' => 'OK',
    'timeZoneId' => 'America/New_York',
    'timeZoneName' => 'Eastern Daylight Time'
)

...但是有一些限制:

[2019年更新] Google时区API已经设置了使用限制。基本上,您的项目必须启用计费,但每个月会应用200美元的“Google Maps平台信用额度”(因此在大多数情况下,您的前40,000个时区API调用/月将免费)。


0

我最近在一次长达8小时的黑客马拉松中解决了一个时区问题。虽然这个解决方案是很快地拼凑出来的,但我很想进一步开发它并将其作为产品销售。但由于没有办法实现这一点,我已经在我的Github上开源了它。

还有一个演示,但如果它超过资源限制,它可能会停止运行。这是一个免费的Google App Engine网络应用程序。

您可以根据需要对其进行优化/增强,以适应运行时间、空间和数据等方面的需求。


4
问题中是否漏掉了“PHP”?Python 已经有一个更好的解决方案:https://github.com/MrMinimal64/timezonefinder。 - hackel

-3

如何找到最接近所有时区位置列表中的点?我想知道这有多准确?

更新: 最终,我想出了这个适合我的代码片段。这对于所有位置都可以正常工作,但对于靠近边界的位置可能不太准确。

  /**
   * Attempts to find the closest timezone by coordinates
   *
   * @static
   * @param $lat
   * @param $lng
   */
  public static function getClosestTimezone($lat, $lng)
  {
    $diffs = array();
    foreach(DateTimeZone::listIdentifiers() as $timezoneID) {
      $timezone = new DateTimeZone($timezoneID);
      $location = $timezone->getLocation();
      $tLat = $location['latitude'];
      $tLng = $location['longitude'];
      $diffLat = abs($lat - $tLat);
      $diffLng = abs($lng - $tLng);
      $diff = $diffLat + $diffLng;
      $diffs[$timezoneID] = $diff;

    }

    //asort($diffs);
    $timezone = array_keys($diffs, min($diffs));


    return $timezone[0];

  }

7
这非常不正确,因为它将每个时区视为具有单一的经度和纬度。在接近边界的地方可能会有一点不准确,但在全球范围内会非常不准确。 - James D
1
@James D,我同意如果您需要精确的时区识别解决方案,那么您不能依赖此解决方案,但是建议用户从最接近他所在位置的时区列表中选择是完全可以的。我认为这种方法比仅提供一个巨大的时区列表更好。 - Dziamid
时区并不仅仅基于经纬度,还涉及政治考虑。新加坡与香港处于同一时区,尽管它明显位于香港以西,而且尽管它与雅加达在同一经度上,但比雅加达早一个小时。仅仅通过经纬度进行时区计算是一个糟糕的想法。 - Shreeni

-3

1
雅虎去年停止支持其GPS产品,并将技术授权给了诺基亚。但是,诺基亚未来的前景并不是很乐观,除非他们解雇微软派驻的开发主管,并在公司失去活力之前加入Android阵营。 - tony gil

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接