使用 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
关于 InZoneLeniently
、InZoneStrictly
或 InZone(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
我想通过提供国家代码,使用 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
关于 InZoneLeniently
、InZoneStrictly
或 InZone(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