你如何处理.net中的“模糊时间”?

时间:2013-06-07 21:59:05

标签: c# timezone nodatime

我需要将来自不同时区的'DateTime'值转换为UTC,反之亦然。我使用TimeZoneInfo来做到这一点。但是,问题在于“日间节能”时间的变化。

例如,今年,下一次时间变化发生在11月3日凌晨2点[CDT]。所以11月3日凌晨1点[CDT]转换为6AM,随着时间的推移发生,下一个小时我们得到1AM [现在它的CST再次,它也转换为6AM。我尝试了this页面上的代码,但它没有说明如何处理这个问题。那么如何处理这个问题???

编辑:

我尝试了NodaTime,当我进行转换时,如

 DateTimeZoneProviders.Tzdb["America/Chicago"].AtStrictly(<localDateTime>) 

它会抛出AmbiguousTimeException。这很好,我也可以使用TimeZoneInfo来做到这一点。但是我怎么知道我需要选择哪个localTime值?

编辑2:

这是与Matt聊天讨论的link

1 个答案:

答案 0 :(得分:9)

如果您拥有的只是当地时间,且时间不明确,那么您无法将其转换为精确的UTC时刻。这就是为什么我们说“含糊不清”。

例如,在美国中部时区,其中包含IANA区域名称America/Chicago和Windows区域标识Central Standard Time - 涵盖“中央标准时间”和“中央夏令时”。如果我所知道的是它是2013年11月3日凌晨1点,那么这个时间是模棱两可的,并且绝对没有办法知道这是否是在中部夏令时的第一个凌晨1点的实例(UTC-5)或中央标准时间(UTC-6)。

当要求将模糊时间转换为UTC时,不同平台会执行不同的操作。有些人会使用第一个实例,通常是白天时间。有些人使用标准时间,通常是第二次。有些人抛出异常,有些人(比如NodaTime)会让你选择你想要发生的事情。

让我们先从TimeZoneInfo开始。

// Despite the name, this zone covers both CST and CDT.
var tz = TimeZoneInfo.FindSystemTimeZoneById("Central Standard Time");
var dt = new DateTime(2013, 11, 3, 1, 0, 0);
var utc = TimeZoneInfo.ConvertTimeToUtc(dt, tz);
Debug.WriteLine(utc); // 11/3/2013 7:00:00 AM

如您所见,.net选择使用“标准”时间,即UTC-6。 (在凌晨1点增加6小时可以到达上午7点)。它没有给你任何警告说时间不明确。您可以检查自己,如下所示:

if (tz.IsAmbiguousTime(dt))
{
    throw new Exception("Ambiguous Time!");
}

但是没有任何东西可以强制执行。你必须自己检查一下。

没有歧义的唯一方法是使用DateTime类型。相反,您可以使用DateTimeOffset。观察:

// Central Standard Time
var dto = new DateTimeOffset(2013, 11, 3, 1, 0, 0, TimeSpan.FromHours(-6));
var utc = dto.ToUniversalTime();
Debug.WriteLine(utc); // 11/3/2013 7:00:00 AM +00:00

// Central Daylight Time
var dto = new DateTimeOffset(2013, 11, 3, 1, 0, 0, TimeSpan.FromHours(-5));
var utc = dto.ToUniversalTime();
Debug.WriteLine(utc); // 11/3/2013 6:00:00 AM +00:00

现在,将此与NodaTime进行比较:

var tz = DateTimeZoneProviders.Tzdb["America/Chicago"];
var ldt = new LocalDateTime(2013, 11, 3, 1, 0, 0);

// will throw an exception, only because the value is ambiguous.
var zdt = tz.AtStrictly(ldt);

// will pick the standard time, like TimeZoneInfo did
var zdt = tz.AtLeniently(ldt);

// manually specify the offset for CST
var zdt = new ZonedDateTime(ldt, tz, Offset.FromHours(-6));

// manually specify the offset for CDT
var zdt = new ZonedDateTime(ldt, tz, Offset.FromHours(-5));


// with any of the above, once you have a ZonedDateTime
// you can get an instant which represents UTC
var instant = zdt.ToInstant();

如您所见,有很多选择。一切都是有效的,这取决于你想做什么。

如果您想完全避免歧义,请始终保持DateTimeOffset,或者在使用NodaTime时使用ZonedDateTimeOffsetDateTime。如果您使用DateTimeLocalDateTime,则无法避免含糊不清。