.ToUniversalTime() 不正确?

.ToUniversalTime() Is Incorrect?

    DateTime dt = new DateTime(1972, 4, 24, 0, 0, 0);
    Response.Write("dt: " + dt.ToString("M/d/yyyy h:mm:ss tt") + "<br />");
    Response.Write("dt.Kind: " + dt.Kind.ToString() + "<br />");
    Response.Write("dt.ToUniversalTime(): " + dt.ToUniversalTime().ToString("M/d/yyyy h:mm:ss tt") + "<br />");

显示

dt: 4/24/1972 12:00:00 AM
dt.Kind: Unspecified
dt.ToUniversalTime(): 4/24/1972 7:00:00 AM

这是不正确的。太平洋时间 1972 年 4 月 24 日中午 12 点实际上是世界标准时间 1972 年 4 月 24 日上午 8 点。

我已经通过 iOS 的内部 UTC 日期对话和 www.timeanddate.com 确认了正确的 UTC 转换,UTC 时间应该是早上 8 点。我做错了什么吗?

服务器 运行 在 "Pacific Time" 时区,因此 ToUniversalTime 应该从太平洋时区转换为 UTC,因为未指定被视为本地时区。

如果您使用的计算机不是太平洋标准时间,您可以使用以下代码查看此行为:

DateTime dt = new DateTime(1972, 4, 24, 0, 0, 0);    
TimeZoneInfo tz = TimeZoneInfo.FindSystemTimeZoneById("Pacific Standard Time");

Console.WriteLine (TimeZoneInfo.ConvertTimeToUtc(dt, tz));
// 4/24/1972 7:00:00 AM

如果您查看 navy.mil's history of daylight saving time,您会注意到以下段落:

The Uniform Time Act of 1966 provided standardization in the dates of beginning and end of daylight time in the U.S. but allowed for local exemptions from its observance. The act provided that daylight time begin on the last Sunday in April and end on the last Sunday in October, with the changeover to occur at 2 a.m. local time.

稍后:

In 1986, a law was passed that shifted the starting date of daylight time to the first Sunday in April, beginning in 1987

因此 DST 转换不是 1987 年 4 月的第一个 星期日,但出于某种原因,.NET 的行为就好像它是。

Timeanddate.com's history of DST seems to agree,并列出 1972 年 4 月 30 日(4 月的最后一个星期日),因为日期时钟拨快了一小时(UTC-7)。

微软 1987 年之前的夏令时调整规则似乎有误 (and I'm not the only one who thinks so)。

以下是 TimeZoneInfo 列出的 PST 规则:

基本上,Microsoft 忽略了历史规则并选择使用 1987 年生效的规则,这些规则发生在这些规则甚至存在之前。

基本上你的日期(1972 年)被微软的 TimeZoneInfo 调整规则错误地处理了。

如果您正在寻找可以更好地处理这些类型时区规则的库,请查看 NodaTime,它可以正确处理这种特殊情况:

var pacific = DateTimeZoneProviders.Tzdb["America/Los_Angeles"];

LocalDateTime localDateTime = new LocalDateTime(1972, 4, 24, 0, 0);
ZonedDateTime zonedDateTime = pacific.AtStrictly(localDateTime);

DateTime utcDateTime = zonedDateTime.ToDateTimeUtc();

Console.WriteLine(utcDateTime);
// 4/24/1972 8:00:00 AM