转换不同时区之间的时间

3
我正在构建一个Windows Store时钟应用程序,以显示用户当前时间以及世界各地的不同城市的时间。
最初,我计划完全使用来自Google等的时区Web服务来完成此操作,但在查看免费帐户中允许的请求数量以及获取付费帐户所涉及的成本后,我认为最好找到一种替代方案,而无需抵押房子。
在搜索周围时,我发现了由John Skeet和他的团队开发的优秀NodaTime库。在仔细阅读文档并在stackoverflow上进行研究后,我的头脑仍然充满了与日期时间和时区相关的术语和转换方法。无论如何,我认为我可以通过2个选项来完成此操作:
选项1:使用DateTime.Now获取当前系统时间,然后使用nodatime获取其他区域的时间,就像这样(基于Matt Johnson在SO上回答另一个问题提供的代码):
DateTimeZone homeZone = DateTimeZoneProviders.Tzdb["Asia/Colombo"];
LocalDateTime homeTime = LocalDateTime.FromDateTime(DateTime.Now);
ZonedDateTime homeTimeInZone = homeTime.InZoneStrictly(homeZone);
TbTime1.Text = "Home time is: " + homeTimeInZone.ToDateTimeOffset();

DateTimeZone timeZone1 = DateTimeZoneProviders.Tzdb["Australia/Perth"];
ZonedDateTime timeZone1Time = homeTimeInZone.WithZone(timeZone1);
TbTime2.Text = "Timezone 1 time is: " + timeZone1Time.ToDateTimeOffset();

第二种选择:经过进一步研究,我发现了这个解决方案,并且感觉它也可以很好地使用:

public ZonedDateTime GetTimeInTimeZone(string timeZone)
{
    // Timezone is olson timezone e.g. "Asia/Colombo"
    Instant now = SystemClock.Instance.Now;
    var zone = DateTimeZoneProviders.Tzdb[timeZone];
    ZonedDateTime zonedDateTime = now.InZone(zone);
    return zonedDateTime;
 }

现在让我试着回���这个问题:在以上两种选项中,都依赖于DateTime.NowSystemClock.Instance.Now来首先确定用户的系统时间,然后将其转换为所需时区的城市时间。但是如果用户的系统时间没有设置正确会怎样呢?我相信(如果我错了请纠正我)DateTime.Now和SystemClock.Instance.Now都使用系统时钟来获取当前时间?假设系统时间没有设置正确,那么任何时区转换都将由于我们依赖于用户的系统时间而显示其他城市的错误时间。

在这种情况下,如何确定用户的当前时间而不依赖于他们的系统时钟?我应该使用一个Web服务来获取用户的当前时区(使用lat/long),还是有更好的选择,例如NodaTime等可以离线工作的工具?谢谢。


如果用户的时间不正确,那么他们可能对时间的完全准确性没有要求(或者使用了无效的Windows副本)。您可以通过与网络时钟进行一次比较来解决这个问题。 - Sayse
@Sayse 作为一个时钟应用程序,用户期望它能显示正确的时间,即使他们的系统没有正确设置。我收到了足够多的支持请求,表明存在这个问题,因此提出这个问题。与网络时钟进行比较是一种选择,但我想知道是否有其他方法。 - Girish
2个回答

4
几点说明:

  • Avoid using DateTime.Now

    • It's already translated to the local time zone, so during DST fall-back transitions the result can be ambiguous.
    • If you need to get an exact unambiguous moment in time without using Noda Time, then use DateTime.UtcNow.
    • You could also use DateTimeOffset.UtcNow, or DateTimeOffset.Now. When the offset is included, there is no ambiguity.
    • See also The Case Against DateTime.Now on my blog.

  • In Noda Time, realize that SystemClock.Instance is an implementation of the IClock interface. Whenever possible, you should code against the interface, such that you can replace the implementation in your unit tests if desired. Though in the simplest of examples, there's nothing wrong with calling SystemClock.Instance.Now, it just isn't as testable.

  • As far as which time zone input to use, that is entirely based on your application requirements.

    • If you can depend on the system to be set to the correct zone, you can retrieve it with

      DateTimeZone tz = DateTimeZoneProviders.Tzdb.GetSystemDefault();
      
    • If you can't depend on the system time zone to be set correctly, you might consider some other source of input.

    • You mentioned GPS coordinates. If you have that, perhaps from a mobile device, there are solutions for resolving it to a time zone. See here for some options. Noda Time can't help you in this regard.

    • You might also consider asking the user of your app to pick a time zone from either a drop-down list or a map. There are some map-based time zone pickers for HTML/JS here and here. (I am unsure if they will work in a WinJS based Windows Store App or not, and I don't know of any XAML based solutions off hand.)

  • If the actual time of their clock is set wrong, there's not a lot you can do, other than try to reach out to another server to retrieve a time stamp. In most cases, you should rely on the operating system to already be synchronized with a time server.

    • If you do require synchronization with an external service, that can be challenging. You need something that implements NTP properly, including measuring and compensating for transmission delay. That's not easy, and probably requires an external library. I don't know of one off-hand to recommend.

    • Reaching out to a web service and returning the current time isn't necessarily going to be as accurate as you might think, as that does not compensate for the time it takes for the server to transmit that response to you.

    • If you are running on a device with a GPS receiver, then it is technically possible that it can provide an accurate time stamp received from the GPS signal. Whether or not that is retrievable and usable via the Windows Store API, I am not sure. I checked a few references on MSDN but came up empty.

  • Regarding the two code samples you provided, they're doing slightly different things, but go with option 2. It's much cleaner, and is pure Noda Time.


谢谢Matt。我会采纳你建议的解决方案(提供时区下拉框)而不使用Web服务。顺便说一下,我看到你提供的代码,可以根据国家代码获取时区列表,链接在这里:https://dev59.com/sWcs5IYBdhLWcg3wMhCz#17098904 - 这似乎是一个不错的解决方案,让用户输入城市、从下拉框中选择国家,然后为所选国家选择时区。 - Girish
需要快速澄清一下,当您说不要使用DateTime.Now时,即使在定时器每秒更新的时钟应用程序中显示当前时间,这也适用吗?是否应改用DateTimeOffset.Now? - Girish
如果你只是在显示它,并且它在设备本身上被调用,那么 DateTime.Now 不会对你造成影响。但是,如果你可能需要将其调整为其他时区或进行任何数学计算,则应改用 DateTimeOffset.Now - Matt Johnson-Pint

2
你通常可以依赖于显示在时钟上的本地时间的准确性。任何Windows机器都设置为连接到时间服务器,默认为time.windows.com。用户会注意到他家里的其它时钟与之不一致。错误地获取夏令时转换日期是有可能的。但是您不想推翻用户的设置,他可能住在靠近时区边界并选择了免除夏令时的县,或者像美国印第安部落地区等地方制定了自己的规则。

对世界上偏远地区做出这样的决策是完全不同的挑战。值得注意的是萨摩亚岛,它在2011年跨越了时区,甚至从UTC-11跨越了日期线到UTC+13。夏令时规则总是受到当地政治决策的影响,在您当地的报纸上并不是头条新闻。它们有时会出现在Web服务中。将城市映射到时区本身就需要一个广泛的数据库。如果需要绝对的准确性,那么您确实需要一个服务。


你所说的广泛数据库是IANA TZDB。它已经被整合到Noda Time中,并且可以随着新的TZDB版本的发布而更新。大多数服务都基于相同的数据进行响应,但不一定会像你在自己的应用程序中所需的那样频繁更新。 - Matt Johnson-Pint
谢谢Hans。夏令时的处理很麻烦,因此我正在考虑使用Web服务。由于涉及成本(以及响应速度),像NodaTime这样的离线解决方案似乎是理想的选择,因为它已经内置了数据库。 - Girish

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