基于坐标获取当地时间

11

我正在编写一个应用程序,根据您提供的坐标(纬度和经度),应该给出当地时间。

我只知道两种方法可以实现:

1:获取时区名称,然后找到它的本地时间。 2:使用Google API,以偏移量和UTC而不是本地时间接收时间。

我决定使用第一种方法,因为它似乎更容易,所以我决定使用GeoTimeZone获取时区...问题是,然后我不知道如何在该时区获取本地时间...这是我编写的获取时区名称的代码:

string tz = TimeZoneLookup.GetTimeZone(lat, lon).Result;

变量latlon当然是坐标。

谢谢!

编辑:我的问题是如何获取该时区的本地时间?


TimeZoneLookup.GetTimeZone(lat, lon).Result; 这是什么意思?TimeZoneLookup 是什么?GetTimeZone 返回什么?Result 的值是多少? - sab669
@sab669,你可以在这里找到一些相关信息:https://github.com/mj1856/GeoTimeZone - CTABUYO
1
请参考CodeProject:http://www.codeproject.com/Questions/758354/get-timezone-and-location-name-using-latitude-and,获取使用纬度和经度获取时区和位置名称。 - jdweng
请参见以下链接:http://stackoverflow.com/questions/3537151/time-zone-conversion-in-c-sharp - Matthew Strawbridge
6个回答

22

这是我的解决方案。它可以离线工作(因此不需要调用 API)。速度快,且包广泛使用且可在 Nuget 上获得。

string tzIana = TimeZoneLookup.GetTimeZone(lat, lng).Result;
TimeZoneInfo tzInfo = TZConvert.GetTimeZoneInfo(tzIana);
DateTimeOffset convertedTime = TimeZoneInfo.ConvertTime(DateTimeOffset.UtcNow, tzInfo);

1
这是哪个NuGet包? - HaBo
4
NuGet包似乎是GeoTimeZone和TimeZoneConvertor。 - Kenneth Evans
3
它们确实是GeoTimeZoneTimeZoneConverter。我更新了代码,使其在所有支持的平台上都能简化并运行,并返回完整的DateTimeOffset - Matt Johnson-Pint
这绝对是一个很好的解决方案。我想知道当世界各地的时区随着时间改变会发生什么?例如,一个国家可能决定放弃夏令时并全年保持夏季时间。这些软件包会使用Windows更新中的信息吗,还是需要更新软件包? - Flyingkiwi
阅读文档后,似乎如果时区数据发生更改,则需要更新包。在这种情况下,仅更新Windows是不够的。 - Flyingkiwi

7
您可以使用Google API来识别当前时区。
.Net Fiddle示例:

public class Program
{
    public static DateTime GetLocalDateTime(double latitude, double longitude, DateTime utcDate)
    {
        var client = new RestClient("https://maps.googleapis.com");
        var request = new RestRequest("maps/api/timezone/json", Method.GET);
        request.AddParameter("location", latitude + "," + longitude);
        request.AddParameter("timestamp", utcDate.ToTimestamp());
        request.AddParameter("sensor", "false");
        var response = client.Execute<GoogleTimeZone>(request);

        return utcDate.AddSeconds(response.Data.rawOffset + response.Data.dstOffset);
    }

    public static void Main()
    {
        var myDateTime = GetLocalDateTime(33.8323, -117.8803, DateTime.UtcNow);
        Console.WriteLine(myDateTime.ToString());
    }
}

public class GoogleTimeZone 
{
    public double dstOffset { get; set; }
    public double rawOffset { get; set; }
    public string status { get; set; }
    public string timeZoneId { get; set; }
    public string timeZoneName { get; set; }
}

public static class ExtensionMethods 
{
    public static double ToTimestamp(this DateTime date)
    {
        DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
        TimeSpan diff = date.ToUniversalTime() - origin;
        return Math.Floor(diff.TotalSeconds);
    }
}

然后,您可以像上面的示例一样轻松使用GetLocalDateTime(double latitude, double longitude, DateTime utcDate)方法:

public static void Main()
{
    var myDateTime = GetLocalDateTime(33.8323, -117.8803, DateTime.UtcNow);
    Console.WriteLine(myDateTime.ToString());
}

这是一个非常好的想法。请检查我在您下面收到的其他答案上的评论。有没有办法修复它?我更喜欢使用另一种方法而不是这种方法,因为这有点繁琐。但是,如果没有其他办法,我将使用您的方法。 - CTABUYO
@ctabuyo 我想没有其他方法... 要获取当地时间,我们需要知道在框架和环境之间标准化的当前位置名称。这就是为什么使用第三方服务或带有位置->时区数据的预定义数据库看起来很不错。实际上,我不知道您的情况,以建议更有用的方法。但我相信这是处理此业务逻辑的好方法,因为Google服务是高可用性服务。 - Andrii Tsok
据我所知,所有的Google API在24小时内都有限制 - 尤其是请求次数。如果你计划在生产环境中使用这段代码,你应该检查这些限制,以避免意外中断。 - Denys Krasnoshchok
仅补充一下,这段代码不再像之前那样能够正常工作。现在的Google Maps API需要一个账户,并且您需要将API密钥作为参数发送。 - G.So

2

最终我是这样解决的,我需要使用TimeZoneDb库将IANA TimeZone转换为Microsoft格式,以下是代码:

string tz1 = TimeZoneLookup.GetTimeZone(lat, lon).Result;

                var timeZoneDbUseCases = new TimeZoneDbUseCases();
                var allTimeZones = timeZoneDbUseCases.GetAllTimeZones();
                var timeZone = timeZoneDbUseCases.GetTimeZoneWithIanaId(tz1);

                var timeZone1 = TimeZoneInfo.FindSystemTimeZoneById(timeZone.MicrosoftId);
                var localTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, timeZone1);

感谢所有帮助过我的人,这两个解决方案都对我帮助很大,也许没有它们我就无法完成任务。
非常感谢!

3
TimeZoneDb非常慢。而且这个包存在一些依赖问题。 - Éric Bergeron

0
您可以使用以下代码将当前的UTC时间转换为本地时间:
var tz = "Eastern Standard Time"; // local time zone
var timeZone = TimeZoneInfo.FindSystemTimeZoneById(tz);
var localTime = TimeZoneInfo.ConvertTimeFromUtc(DateTime.UtcNow, timeZone);

//Console.WriteLine(localTime.ToString("G"));
//Console.ReadLine();

这是我迄今为止尝试过的,问题在于我使用的livery获取时区名称,给出的名称是这样的:"America/Chicago",然后FindSystemTimeZoneById报错,因为它找不到它。 - CTABUYO
@ctabuyo 是的,你只能使用这种方法来处理Windows时区,而无法处理不同的IANA时区。 - Denys Krasnoshchok

0

这是我的解决方案,基于混合解决方案。需要使用RestSharp和NodaTime(都来自nuget)。

    private static string WindowsToIana(string windowsZoneId)
    {
        if (windowsZoneId.Equals("UTC", StringComparison.Ordinal))
            return "Etc/UTC";

        var tzdbSource = NodaTime.TimeZones.TzdbDateTimeZoneSource.Default;
        var tzi = TimeZoneInfo.FindSystemTimeZoneById(windowsZoneId);
        if (tzi == null) return null;
        var tzid = tzdbSource.MapTimeZoneId(tzi);
        if (tzid == null) return null;
        return tzdbSource.CanonicalIdMap[tzid];
    }

    private static string IanaToWindows(string ianaZoneId)
    {
        var utcZones = new[] { "Etc/UTC", "Etc/UCT", "Etc/GMT" };
        if (utcZones.Contains(ianaZoneId, StringComparer.Ordinal))
            return "UTC";

        var tzdbSource = NodaTime.TimeZones.TzdbDateTimeZoneSource.Default;

        // resolve any link, since the CLDR doesn't necessarily use canonical IDs
        var links = tzdbSource.CanonicalIdMap
            .Where(x => x.Value.Equals(ianaZoneId, StringComparison.Ordinal))
            .Select(x => x.Key);

        // resolve canonical zones, and include original zone as well
        var possibleZones = tzdbSource.CanonicalIdMap.ContainsKey(ianaZoneId)
            ? links.Concat(new[] { tzdbSource.CanonicalIdMap[ianaZoneId], ianaZoneId })
            : links;

        // map the windows zone
        var mappings = tzdbSource.WindowsMapping.MapZones;
        var item = mappings.FirstOrDefault(x => x.TzdbIds.Any(possibleZones.Contains));
        if (item == null) return null;
        return item.WindowsId;
    }

    private static string GetIanaTimeZone(double latitude, double longitude, DateTime date)
    {
        RestClient client;
        string location;
        RestRequest request;
        RestResponse response;
        TimeSpan time_since_midnight_1970;
        double time_stamp;
        string time_zone = "";

        try
        {
            const string GOOGLE_API = "https://maps.googleapis.com";
            const string GOOGLE_TIMEZONE_REQUEST = "maps/api/timezone/xml";


            client = new RestClient(GOOGLE_API);
            request = new RestRequest(GOOGLE_TIMEZONE_REQUEST,
                                        Method.GET);
            location = String.Format("{0},{1}",
                                       latitude.ToString(CultureInfo.InvariantCulture),
                                       longitude.ToString(CultureInfo.InvariantCulture));

            DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0);
            time_since_midnight_1970 = date - origin;
            time_stamp = Math.Floor(time_since_midnight_1970.TotalSeconds);

            request.AddParameter("location", location);
            request.AddParameter("timestamp", time_stamp);
            request.AddParameter("sensor", "false");
            //request.AddParameter("key", yourgooglekey);

            response = (RestResponse)client.Execute(request);
            if (response.StatusDescription.Equals("OK"))
            {
                XmlNode node;
                XmlDocument xml_document = new XmlDocument();

                xml_document.LoadXml(response.Content);
                node = xml_document.SelectSingleNode(
                            "/TimeZoneResponse/time_zone_id");
                if (node != null)
                {
                    time_zone = node.InnerText;
                }
                else
                {

                }
            }
            else
            {

            }
        }
        catch (Exception ex)
        {

        }

        return time_zone;
    }

    public static DateTime? GetDateTimeFromCoordinates(DateTime? utc, double? latitude, double? longitude)
    {
        if (utc == null || latitude == null || longitude == null)
            return null;

        try
        {
            string iana_timezone = GetIanaTimeZone((double)latitude, (double)longitude, (DateTime)utc);
            if (string.IsNullOrWhiteSpace(iana_timezone))
                return null;

            string time_zone = IanaToWindows(iana_timezone);
            TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById(time_zone);
            DateTime date = TimeZoneInfo.ConvertTimeFromUtc((DateTime)utc, tz);
            return date;
        }
        catch (Exception ex)
        {

            return null;
        }

    }
}

static void Main(string[] args)
{
    double latitude = -11.2026920;
    double longitude = 17.8738870;
    DateTime uct = DateTime.UtcNow;

   DateTime? ret = GetDateTimeFromCoordinates(utc,latitude,longitude);

}

-1

function jsonpRequest(url, data)
{
    let params = "";
    for (let key in data)
    {
        if (data.hasOwnProperty(key))
        {
            if (params.length == 0)
            {
                params += "?";
            }
            else
            {
                params += "&";
            }
            let encodedKey = encodeURIComponent(key);
            let encodedValue = encodeURIComponent(data[key]);
            params += encodedKey + "=" + encodedValue;
         }
    }
    let script = document.createElement('script');
    script.src = url + params;
    document.body.appendChild(script);
}

function getLocation() {
  if (navigator.geolocation) {
    navigator.geolocation.getCurrentPosition(showPosition);
  } else {
    x.innerHTML = "Geolocation is not supported by this browser.";
  }
}
let lat_ini=[]; let lon_ini=[];
function showPosition(position) {
  lat_ini= position.coords.latitude;
  lon_ini= position.coords.longitude;
}
////delay time between lines
function sleep(ms) {
  return new Promise(resolve => setTimeout(resolve, ms));
}
///////
function getGMT()
{
  getfinalGMT()
  getLocation()
  async function sample() {
    await sleep(2000);
let lat_str=lat_ini.toString();
let lng_str=" "+lon_ini.toString();

  let url = "https://api.opencagedata.com/geocode/v1/json";
  let data = {
    callback: "displayGMT",
    q: lat_str + lng_str,
    key: "fac4471073a347019196c1291e6a97d7"
  }
  jsonpRequest(url, data)
}
 sample();
 }
let your_GMT=[];
function displayGMT(data)
{
your_GMT=(Number(data.results[0].annotations.timezone.offset_string))
console.log(your_GMT)
}
/////
function getfinalGMT()
{
let lat=document.getElementById("lat_id").value; let lng=document.getElementById("lng_id").value;
let lat_str=lat.toString();
let lng_str=" "+lng.toString();

  let url = "https://api.opencagedata.com/geocode/v1/json";
  let data = {
    callback: "displayfinalGMT",
    q: lat + lng_str,
    key: "fac4471073a347019196c1291e6a97d7"
  }
  jsonpRequest(url, data)
 }
let final_GMT=[];
function displayfinalGMT(data)
{
final_GMT=(Number(data.results[0].annotations.timezone.offset_string))
console.log(final_GMT)
}
/////clock


const hourHand = document.querySelector('[data-hour-hand]')
const minuteHand = document.querySelector('[data-minute-hand]')
const secondHand = document.querySelector('[data-second-hand]')
  let dif_overall=[];
function setClock() {
   let gmt_diff=Number(your_GMT-final_GMT)/100
   if (gmt_diff>12){
      dif_overall=gmt_diff-12
   }
   else{
     dif_overall=gmt_diff
   }
    console.log(dif_overall)
  const currentDate = new Date()
  const secondsRatio = currentDate.getSeconds() / 60
  const minutesRatio = (secondsRatio + currentDate.getMinutes()) / 60
  const hoursRatio = (minutesRatio + currentDate.getHours() - dif_overall ) / 12
  setRotation(secondHand, secondsRatio)
  setRotation(minuteHand, minutesRatio)
  setRotation(hourHand, hoursRatio)
}

function setRotation(element, rotationRatio) {
  element.style.setProperty('--rotation', rotationRatio * 360)
}
function activate_clock(){
setClock()
setInterval(setClock, 1000)
}
*, *::after, *::before {
  box-sizing: border-box;
}

body {
  background: linear-gradient(to right, hsl(200, 100%, 50%), hsl(175, 100%, 50%));
  display: flex;
  justify-content: center;
  align-items: center;
  min-height: 100vh;
  overflow: hidden;
}

.clock {
  width: 200px;
  height: 200px;
  background-color: rgba(255, 255, 255, .8);
  border-radius: 50%;
  border: 2px solid black;
  position: relative;
}

.clock .number {
  --rotation: 0;
  position: absolute;
  width: 100%;
  height: 100%;
  text-align: center;
  transform: rotate(var(--rotation));
  font-size: 1.5rem;
}

.clock .number1 { --rotation: 30deg; }
.clock .number2 { --rotation: 60deg; }
.clock .number3 { --rotation: 90deg; }
.clock .number4 { --rotation: 120deg; }
.clock .number5 { --rotation: 150deg; }
.clock .number6 { --rotation: 180deg; }
.clock .number7 { --rotation: 210deg; }
.clock .number8 { --rotation: 240deg; }
.clock .number9 { --rotation: 270deg; }
.clock .number10 { --rotation: 300deg; }
.clock .number11 { --rotation: 330deg; }

.clock .hand {
  --rotation: 0;
  position: absolute;
  bottom: 50%;
  left: 50%;
  border: 1px solid white;
  border-top-left-radius: 10px;
  border-top-right-radius: 10px;
  transform-origin: bottom;
  z-index: 10;
  transform: translateX(-50%) rotate(calc(var(--rotation) * 1deg));
}

.clock::after {
  content: '';
  position: absolute;
  background-color: black;
  z-index: 11;
  width: 15px;
  height: 15px;
  top: 50%;
  left: 50%;
  transform: translate(-50%, -50%);
  border-radius: 50%;
}

.clock .hand.second {
  width: 3px;
  height: 45%;
  background-color: red;
}

.clock .hand.minute {
  width: 7px;
  height: 40%;
  background-color: black;
}

.clock .hand.hour {
  width: 10px;
  height: 35%;
  background-color: black;
}














/* Background Styles Only */

@import url('https://fonts.googleapis.com/css?family=Raleway');

* {
    font-family: Raleway;
}

.side-links {
  position: absolute;
  top: 15px;
  right: 15px;
}

.side-link {
  display: flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  margin-bottom: 10px;
  color: white;
  width: 180px;
  padding: 10px 0;
  border-radius: 10px;
}

.side-link-youtube {
  background-color: red;
}

.side-link-twitter {
  background-color: #1DA1F2;
}

.side-link-github {
  background-color: #6e5494;
}

.side-link-text {
  margin-left: 10px;
  font-size: 18px;
}

.side-link-icon {
  color: white;
  font-size: 30px;
}
<input type="text" id="lat_id" placeholder="lat"><br><br>
  <input type="text" id="lng_id" placeholder="lng"><br><br>
<button class="text" onClick="getLocation()">Location</button>
<button class="text" onClick="getGMT()"> GMT</button>
<button class="text" onClick="activate_clock()"> Activate</button>
<div class="clock">
  <div class="hand hour" data-hour-hand></div>
  <div class="hand minute" data-minute-hand></div>
  <div class="hand second" data-second-hand></div>
  <div class="number number1">1</div>
  <div class="number number2">2</div>
  <div class="number number3">3</div>
  <div class="number number4">4</div>
  <div class="number number5">5</div>
  <div class="number number6">6</div>
  <div class="number number7">7</div>
  <div class="number number8">8</div>
  <div class="number number9">9</div>
  <div class="number number10">10</div>
  <div class="number number11">11</div>
  <div class="number number12">12</div>
</div>


你们需要复制并粘贴代码,在新的浏览器中运行,以便首先跟踪您的当前位置,因为代码片段不允许我们提示。 - Maximus Su
希望你们能够欣赏。 - Maximus Su

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