如果您只有一个本地时间,并且该时间不明确,那么您无法将其转换为精确的 UTC 时刻。这就是为什么我们说“模棱两可”。
例如,在具有 IANA 区域名称America/Chicago
和 Windows 区域 ID的美国中部时区中Central Standard Time
- 涵盖“中部标准时间”和“中部夏令时间”。如果我只知道是 2013 年 11 月 3 日凌晨 1:00,那么这个时间是模棱两可的,绝对无法知道这是否是美国中部夏令时间的第一个凌晨 1:00 (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();
如您所见,有很多选择。一切都是有效的,它只取决于你想做什么。
如果您想完全避免歧义,请始终保留 a DateTimeOffset
,或者在使用 NodaTime 时使用 aZonedDateTime
或OffsetDateTime
。如果您使用DateTime
or LocalDateTime
,则无法避免歧义。