DateTime.ToUniversalTime 和 TimeZoneInfo.ConvertTimeToUtc 有什么区别

问题描述 投票:0回答:4

我刚刚开始考虑推出一个网络应用程序,该应用程序需要在用户一天开始时(比如早上 6 点)为用户做一些事情。也在他们的日子结束时。

我到处都读到人们说了很多只是为了使用 .ToUniversalTime 将时间存储在 UTC 中,但是当我尝试这个(正如我怀疑的那样)时,它不起作用,它只是将时间移动了大约一个小时(我在英国,所以我认为这与 GMT 到 UTC 的一些偏移有关,尽管这对我来说没有意义,因为夏令时此时应该关闭)。

我在数据库中有一个存储用户时区的字段,因此当我开始使用 ConvertTimeToUtc 和 fromUtc 时,它开始执行我期望的操作。虽然我再次不确定我是否必须自己构建一些逻辑来进行夏令时转换,或者它应该为我做这件事。

我主要想知道为什么每个人都在谈论 .ToUniversalTime,因为它似乎确实对我没有帮助,而且我无法理解它怎么可能知道偏移多少时间以将其转换为 UTC,而第二个方式很有意义。

有人可以解释一下每种方法有何用处吗?

.net datetime timezone utc
4个回答
14
投票

这两者之间实际上区别。

在 .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
标志,它与仅仅调用TimeZoneInfo.ConvertimeToUtc
不一样

它调用的方法是 ConvertTimeToUtc

internal
重载,它带有
TimeZoneInfoOptions
标志。该方法的 public 版本使用
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
合约不允许这样做,因此框架选择返回一个值而不是抛出。

它选择的值是根据使用标准时间偏移量计算的,就好像夏令时转换没有发生一样。


3
投票

编辑:我错了。请参阅 Matt Johnson 的 answer,了解

DateTime.ToUniversalTime
TimeZoneInfo.ConvertTimeToUtc
实现之间细微差别的解释。

对于标题中提出具体问题的任何人:DateTime.ToUniversalTime 和 TimeZoneInfo.ConvertTimeToUtc 有什么区别?

答案是:没有区别

使用JustDecompile检查.NET 4.5中

DateTime.ToUniversalTime
的实现,我们看到它直接使用了
TimeZoneInfo.ConvertTimeToUtc

    public DateTime ToUniversalTime()
    {
        return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
    }

2
投票

如果您在不同时区的机器上运行代码,您的计算仍然有效吗?这就是人们将所有日期时间存储并视为 UTC 的原因 - 它消除了任何歧义。您不需要存储用户的时区。任何地方的任何机器都可以从数据库中提取日期并轻松地将其与当地时间进行转换。

如果您将时间存储在其他时区,那么您必须将其取出,计算所需时区的偏移量,包括考虑夏令时和国际日期变更线的考虑因素。就您而言,您还存储了额外的不必要的信息。


0
投票

从基本使用的角度来看,当转换的

ConvertTimeToUtc
不在您机器的时区中时,您将使用
DateTime
。例如,此测试代码通过了,但
utcDate1
utcDate2
的值不同,即使它们使用相同的原始值。

public void timezone()
{
    var tzi = TimeZoneInfo.FindSystemTimeZoneById("Mountain Standard Time");

    // set testDate for November 3rd at 12 o'clock. This date is in DST by the way.
    var testDate = new DateTime(2022, 11, 3, 12, 0, 0, DateTimeKind.Unspecified);

    // method 1
    var utcDate1 = TimeZoneInfo.ConvertTimeToUtc(testDate, tzi);

    // method 2
    var utcDate2 = testDate.ToUniversalTime(); // machine this code is running on, is set for CST
    try
    {
        System.Console.WriteLine(utcDate1.Hour.ToString(), "utcDate1 = {0}");
        NUnit.Framework.Assert.True(utcDate1.Hour == 18);
        System.Console.WriteLine(utcDate2.Hour.ToString(), "utcDate2 = {0}");
        NUnit.Framework.Assert.True(utcDate2.Hour == 17);
    }
    catch (NUnit.Framework.AssertionException)
    {
        //the expected exception occurred
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.