这两者其实是有区别的。
在 .NET 3.5 及更低版本中,Datetime.ToUniversalTime
实现为:
public DateTime ToUniversalTime() {
return TimeZone.CurrentTimeZone.ToUniversalTime(this);
}
因为它使用了TimeZone
该类,所以它遇到了 MSDN 文档中提到的相同问题:
该类TimeZone
仅支持本地时区的单一夏令时调整规则。因此,TimeZone
该类可以准确地报告夏令时信息或仅在最新调整规则生效期间在 UTC 和本地时间之间进行转换。相比之下,TimeZoneInfo
该类支持多个调整规则,这使得处理历史时区数据成为可能。
在 .NET 4.0 及更高版本中,Datetime.ToUniversalTime
实现为:
public DateTime ToUniversalTime() {
return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
}
修复了不支持历史调整规则的问题,但是由于NoThrowOnInvalidTime
flag的原因,和刚才调用不一样TimeZoneInfo.ConvertimeToUtc
。
它调用的方法是一个带有标志的内部重载。公开版的方法用,而这个用的。ConvertTimeToUtc
TimeZoneInfoOptions
TimeZoneInfoOptions.None
TimeZoneInfoOptions.NoThrowOnInvalidTime
区别如下。时区设置为美国太平洋时间:
DateTime dt = new DateTime(2015, 3, 8, 2, 0, 0, DateTimeKind.Local);
DateTime utc = dt.ToUniversalTime();
Console.WriteLine(utc); // "3/8/2015 10:00:00 AM"
对比:
DateTime dt = new DateTime(2015, 3, 8, 2, 0, 0, DateTimeKind.Local);
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(dt); // throws exception!
Console.WriteLine(utc);
由于在这个日期,在这个时区,时间从 1:59:59 跳到 3:00:00,提供 2:00:00 的本地时间是无效的。正确的做法是抛出异常(如第二种情况)。但是,由于DateTime.ToUniversalTime
早期框架版本的现有合约不允许这样做,因此框架选择返回值而不是抛出。
它选择的值是根据使用标准时间偏移量计算的,就好像没有发生 DST 转换一样。