使用 NodaTime 将特定国家日期时间转换为 UTC

Convert specific country date time to UTC with NodaTime

我想通过提供国家代码,使用 NodaTime 将特定日期时间值转换为 UTC。

例如,国家是土耳其,国家代码是 TR,具体日期时间是 "Feb 5, 2016 7:45 PM" 可以是 "Feb 5, 2016 5:45 PM" 吗?

而且我的 windows 位置不是土耳其。

提前致谢?

好吧,您不能只使用国家代码 - 您需要一个时区。在一些国家有多个时区。

一旦您将时区设置为 DateTimeZone(通过 BCL TimeZoneInfo 或来自 TZDB 时区提供商),您将:

  • 根据您得到的值构造一个 LocalDateTime,例如使用 LocalDateTimePattern 解析文本
  • 致电 LocalDateTime.InZoneLeniently(或类似的 - 稍后会详细介绍)以获取 ZonedDateTime
  • 使用 WithZone(DateTimeZone.Utc) 将其转换为基于 UTC 的 ZonedDateTime

关于 InZoneLenientlyInZoneStrictlyInZone(DateTimeZone, ZoneLocalMappingResolver) 的部分是因为本地日期和时间可能在 DST 转换期间出现两次或根本不出现。有关详细信息,请参阅 user guide

示例代码:

using System;
using NodaTime;
using NodaTime.Text;

class Test
{
    static void Main()
    {
        var text = "Feb 5, 2016 7:45 PM";
        var zone = DateTimeZoneProviders.Tzdb["Europe/Istanbul"];
        var pattern = LocalDateTimePattern.CreateWithInvariantCulture("MMM d, uuuu h:mm tt");
        var local = pattern.Parse(text).Value;
        var zoned = local.InZoneStrictly(zone);
        var utc = zoned.WithZone(DateTimeZone.Utc);
        Console.WriteLine(utc); // 2016-02-05T17:45:00 UTC (+00)
    }
}

现在,为了从国家代码中查找时区,TZDB (IANA) 数据库附带了有关时区位置的信息,这些信息在 Noda Time 中公开。例如,如果您有一个 ISO-3166 2 字母国家代码,您可以使用:

using NodaTime;
using NodaTime.TimeZones;
using System.Linq;
...
var code = "TR"; // Turkey
var zoneId = TzdbDateTimeZoneSource.Default.ZoneLocations
                                   .Single(loc => loc.CountryCode == code)
                                   .ZoneId;
var zone = DateTimeZoneProviders.Tzdb[zoneId];

如果有多个区域具有给定的国家代码(或 none),Single 调用将失败。

如果您要经常查找区域,您可能需要构建字典:

var zonesByCountryCode = TzdbDateTimeZoneSource.Default
   .ZoneLocations
   .GroupBy(loc => loc.CountryCode)
   .Where(g => g.Count() == 1) // Single-zone countries only
   .ToDictionary(g => g.Key, g => g.First());

我正在尝试对 Jon 上面的回答添加评论。

这是与上面使用 NodaTime 3.0.9 nuget 包的代码等效的 F# - 请注意,我使用的是 yyyy 而不是 YYYY:

let text = "Feb 5, 2016 7:45 PM"
let zone = NodaTime.DateTimeZoneProviders.Tzdb.["Europe/Istanbul"]
let pattern = NodaTime.Text.LocalDateTimePattern.CreateWithInvariantCulture("MMM d, uuuu h:mm tt");
let local = pattern.Parse(text).Value;
let zoned = local.InZoneStrictly(zone);
let utc = zoned.WithZone(NodaTime.DateTimeZone.Utc);
System.Console.WriteLine(utc); // 2016-02-05T17:45:00 UTC (+00)

当我使用 YYYY 时,我收到消息 NodaTime.Text.InvalidPatternException: The character Y is not a format specifier, and should be quoted to act as a literal.

你也可以把上面的代码减'letty'如下

let local = ("Feb 5, 2016 7:45 PM" |> ("MMM d, uuuu h:mm tt" |> NodaTime.Text.LocalDateTimePattern.CreateWithInvariantCulture).Parse).Value;
let utc = NodaTime.DateTimeZone.Utc 
      |> (NodaTime.DateTimeZoneProviders.Tzdb.["Europe/Istanbul"] |> local.InZoneStrictly).WithZone
      |> System.Console.WriteLine