给定一个位置的经度和纬度,如何知道该位置的有效时区?

在大多数情况下,我们正在寻找IANA/Olson时区id,尽管有些服务可能只返回UTC偏移量或其他一些时区标识符。详细信息请阅读时区标签信息。


当前回答

我们在Teleport刚刚开始开放我们的API,其中一个用例也是为坐标公开TZ信息。

例如,可以通过以下方式请求我们所有可用的TZ信息的坐标:

curl -s https://api.teleport.org/api/locations/59.4372,24.7453/?embed=location:nearest-cities/location:nearest-city/city:timezone/tz:offsets-now | jq '._embedded."location:nearest-cities"[0]._embedded."location:nearest-city"._embedded."city:timezone"'

这将返回以下内容

{
  "_embedded": {
    "tz:offsets-now": {
      "_links": {
        "self": {
          "href": "https://api.teleport.org/api/timezones/iana:Europe%2FTallinn/offsets/?date=2015-09-07T11%3A20%3A09Z"
        }
      },
      "base_offset_min": 120,
      "dst_offset_min": 60,
      "end_time": "2015-10-25T01:00:00Z",
      "short_name": "EEST",
      "total_offset_min": 180,
      "transition_time": "2015-03-29T01:00:00Z"
    }
  },
  "_links": {
    "self": {
      "href": "https://api.teleport.org/api/timezones/iana:Europe%2FTallinn/"
    },
    "tz:offsets": {
      "href": "https://api.teleport.org/api/timezones/iana:Europe%2FTallinn/offsets/{?date}",
      "templated": true
    },
    "tz:offsets-now": {
      "href": "https://api.teleport.org/api/timezones/iana:Europe%2FTallinn/offsets/?date=2015-09-07T11%3A20%3A09Z"
    }
  },
  "iana_name": "Europe/Tallinn"
}

在本例中,我使用./jq进行JSON解析。

其他回答

It's indeed important to recognize that this a more complicated problem than most would suspect. In practice many of us are also willing to accept a working set of code that works for "as many cases as possible", where at least its fatal issues can be identified and minimized collectively. So I post this with all of that and the spirit of the OP in mind. Finally, for practical value to others who are trying to convert GPS to timezone with the end goal of having a location-sensitive time object (and more importantly to help advance the quality of average implementations with time objects that follow from this wiki) here is what I generated in Python (please feel free to edit):

import pytz
from datetime import datetime
from tzwhere import tzwhere

def timezoned_unixtime(latitude, longitude, dt):
    tzw = tzwhere.tzwhere()
    timezone_str = tzw.tzNameAt(latitude, longitude)
    timezone = pytz.timezone(timezone_str)
    timezone_aware_datetime = timezone.localize(dt, is_dst=None)
    unix_time = (timezone_aware_datetime - datetime(1970, 1, 1, tzinfo=pytz.utc)).total_seconds()
    return unix_time

dt = datetime(year=2017, month=1, day=17, hour=12, minute=0, second=0)
print timezoned_unixtime(latitude=40.747854, longitude=-74.004733, dt=dt)

从古比鱼:

import geocoders
g = geocoders.GoogleV3()
place, (lat, lng) = g.geocode('Fairbanks')
print place, (lat, lng)
Fairbanks, AK, USA (64.8377778, -147.7163889)
timezone = g.timezone((lat, lng))
print timezone.dst

美国/安克雷奇。DstTzInfo的dst

美国/主播LMT-1天,STD 1400:00

你可以使用geolocator.js轻松获得时区和更多…

它使用需要密钥的谷歌api。首先你配置geolocator

geolocator.config({
    language: "en",
    google: {
        version: "3",
        key: "YOUR-GOOGLE-API-KEY"
    }
});

获取TimeZone,如果你有坐标:

geolocator.getTimeZone(options, function (err, timezone) {
    console.log(err || timezone);
});

示例输出:

{
    id: "Europe/Paris",
    name: "Central European Standard Time",
    abbr: "CEST",
    dstOffset: 0,
    rawOffset: 3600,
    timestamp: 1455733120
}

定位,然后获得时区和更多

如果没有坐标,可以先定位用户位置。

下面的例子将首先尝试HTML5 Geolocation API来获取坐标。如果失败或被拒绝,它将通过Geo-IP查找获得坐标。最后,它将获得时区和更多…

var options = {
    enableHighAccuracy: true,
    timeout: 6000,
    maximumAge: 0,
    desiredAccuracy: 30,
    fallbackToIP: true, // if HTML5 fails or rejected
    addressLookup: true, // this will get full address information
    timezone: true,
    map: "my-map" // this will even create a map for you
};
geolocator.locate(options, function (err, location) {
    console.log(err || location);
});

示例输出:

{
    coords: {
        latitude: 37.4224764,
        longitude: -122.0842499,
        accuracy: 30,
        altitude: null,
        altitudeAccuracy: null,
        heading: null,
        speed: null
    },
    address: {
        commonName: "",
        street: "Amphitheatre Pkwy",
        route: "Amphitheatre Pkwy",
        streetNumber: "1600",
        neighborhood: "",
        town: "",
        city: "Mountain View",
        region: "Santa Clara County",
        state: "California",
        stateCode: "CA",
        postalCode: "94043",
        country: "United States",
        countryCode: "US"
    },
    formattedAddress: "1600 Amphitheatre Parkway, Mountain View, CA 94043, USA",
    type: "ROOFTOP",
    placeId: "ChIJ2eUgeAK6j4ARbn5u_wAGqWA",
    timezone: {
        id: "America/Los_Angeles",
        name: "Pacific Standard Time",
        abbr: "PST",
        dstOffset: 0,
        rawOffset: -28800
    },
    flag: "//cdnjs.cloudflare.com/ajax/libs/flag-icon-css/2.3.1/flags/4x3/us.svg",
    map: {
        element: HTMLElement,
        instance: Object, // google.maps.Map
        marker: Object, // google.maps.Marker
        infoWindow: Object, // google.maps.InfoWindow
        options: Object // map options
    },
    timestamp: 1456795956380
}

下面是如何使用谷歌的脚本编辑器来获取gsheet中的timezoneName和timeZoneId。

步骤1。获取谷歌的时区API的API键

步骤2。创建一个新的gsheet。在“工具”菜单下单击“脚本编辑器”。添加如下代码:

function getTimezone(lat, long) {  
  var apiKey = 'INSERTAPIKEYHERE'
  var url = 'https://maps.googleapis.com/maps/api/timezone/json?location=' + lat + ',' + long + '&timestamp=1331161200&key=' + apiKey 
  var response = UrlFetchApp.fetch(url);
  var data = JSON.parse(response.getContentText());
  return data["timeZoneName"];
}

步骤3。保存并发布getTimezone()函数,并如上图所示使用它。

我写了一个包https://github.com/ringsaturn/tzf支持获取时区在Go&Python和非常快:

package main

import (
    "fmt"

    "github.com/ringsaturn/tzf"
)

func main() {
    finder, err := tzf.NewDefaultFinder()
    if err != nil {
        panic(err)
    }
    fmt.Println(finder.GetTimezoneName(116.6386, 40.0786))
}

Python https://github.com/ringsaturn/tzfpy sample:

from tzfpy import get_tz

print(get_tz(121.4737, 31.2305))

Rust https://github.com/ringsaturn/tzf-rs样品:

use tzf_rs::DefaultFinder;

fn main() {
    let finder = DefaultFinder::new();

    print!("{:?}\n", DefaultFinder.get_tz_name(116.3883, 39.9289));
}