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

发布于 2024-08-11 08:02:31 字数 472 浏览 4 评论 0原文

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

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

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

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

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

I'm just starting to think properly about rolling out a webapp that will need to do things to users at the start of their day, say 6am. Also at the end of their days.

Everywhere I've been reading about people saying a lot just to use .ToUniversalTime to store the time in UTC, but when I tried this (as I suspected) it didn't work, and it just moved the time about by an hour (I'm in the UK, so I thought this was to do with some offset from GMT to UTC, although that doesn't make sense to me, as day light saving should be off at the moment).

I have a field in the db that stores the users timezone, and so when I started using ConvertTimeToUtc and fromUtc , it started doing what I was expecting it to do. Although again I'm not sure if I have to build in some logic myself to do daylight saving conversions, or it should do it for me.

I am mainly wondering why everyone was talking about .ToUniversalTime, as it really didn't seem to help me, and I couldn't understand how it could possibly know how much to offset the time to shift it to UTC, whereas the second way made sense.

Could someone explain how each methods could be useful?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

§对你不离不弃 2024-08-18 08:02:31

这两者之间实际上是有区别的。

在 .NET 3.5 及更低版本中,Datetime.ToUniversalTime 实现为:

public DateTime ToUniversalTime() {
    return TimeZone.CurrentTimeZone.ToUniversalTime(this);
}

由于它使用了 TimeZone 类,因此遇到了 Datetime.ToUniversalTime 中提到的相同问题a href="https://msdn.microsoft.com/en-us/library/system.timezone.aspx" rel="noreferrer">MSDN 文档:

TimeZone 类仅支持本地时区的单个夏令时调整规则。因此,TimeZone 类只能在最新调整规则生效期间准确报告夏令时信息或在 UTC 和本地时间之间进行转换。相比之下,TimeZoneInfo 类支持多种调整规则,这使得可以使用历史时区数据。

在 .NET 4.0 及更高版本中,Datetime.ToUniversalTime 实现为:

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

这修复了不支持历史调整规则的问题,但由于 NoThrowOnInvalidTime > 标志,它与仅调用 TimeZoneInfo.ConvertimeToUtc 不同

它调用的方法是采用 TimeZoneInfoOptions 标志的 ConvertTimeToUtc内部重载。该方法的公共版本使用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"

Vs:

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 转换一样。

There actually is a difference between these two.

In .NET 3.5 and lower, Datetime.ToUniversalTime is implemented as:

public DateTime ToUniversalTime() {
    return TimeZone.CurrentTimeZone.ToUniversalTime(this);
}

Because it used the TimeZone class, it suffered the same problems mentioned in the MSDN docs:

The TimeZone class supports only a single daylight saving time adjustment rule for the local time zone. As a result, the TimeZone class can accurately report daylight saving time information or convert between UTC and local time only for the period in which the latest adjustment rule is in effect. In contrast, the TimeZoneInfo class supports multiple adjustment rules, which makes it possible to work with historic time zone data.

In .NET 4.0 and higher, Datetime.ToUniversalTime is implemented as:

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

This fixes the problem about not supporting historical adjustment rules, but due to the NoThrowOnInvalidTime flag, it is not the same as just calling TimeZoneInfo.ConvertimeToUtc.

The method it calls is an internal overload of ConvertTimeToUtc that takes a TimeZoneInfoOptions flag. The public version of the method uses TimeZoneInfoOptions.None, while this one uses TimeZoneInfoOptions.NoThrowOnInvalidTime.

The difference can be illustrated as follow. With the time zone set to US Pacific Time:

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"

Vs:

DateTime dt = new DateTime(2015, 3, 8, 2, 0, 0, DateTimeKind.Local);
DateTime utc = TimeZoneInfo.ConvertTimeToUtc(dt);  // throws exception!
Console.WriteLine(utc);

Since on this date, in this time zone, the time skips from 1:59:59 to 3:00:00, supplying a local time of 2:00:00 isn't valid. The correct thing to do is to throw an exception (as in the second case). However since the existing contract of DateTime.ToUniversalTime from earlier framework versions did not allow this, the framework chooses to return a value instead of throwing.

The value it chooses is calculated based on using the standard time offset, as if the DST transition didn't occur.

世界等同你 2024-08-18 08:02:31

编辑:我错了。请参阅 Matt Johnson 的答案,了解 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);
    }

Edit: I was wrong. See Matt Johnson's answer for an explanation of the subtle differences between implementations of DateTime.ToUniversalTime and TimeZoneInfo.ConvertTimeToUtc.

For anyone else that has the specific question phrased in the title: What is the difference between DateTime.ToUniversalTime and TimeZoneInfo.ConvertTimeToUtc?

The answer is: There is no difference.

Using JustDecompile to inspect the implementation of DateTime.ToUniversalTime in .NET 4.5, we see that it uses TimeZoneInfo.ConvertTimeToUtc directly:

    public DateTime ToUniversalTime()
    {
        return TimeZoneInfo.ConvertTimeToUtc(this, TimeZoneInfoOptions.NoThrowOnInvalidTime);
    }
Bonjour°[大白 2024-08-18 08:02:31

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

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

If you run the code on a machine in a different timezone, are your calculations still going to work? This is the reason people store and treat all DateTimes as UTC - it removes any ambiguity. You wouldn't need to store the user's timezone. Any machine, anywhere, can pull a date from the database and convert it to and from local times with ease.

If you're storing times in some other timezone, then you have to pull it out, calculate the offset to the desired timezone, including factoring in daylight savings times and international dateline considerations. In your case, you're also storing extra unnecessary information.

忆悲凉 2024-08-18 08:02:31

从基本使用的角度来看,当要转换的 DateTime 不在您计算机的时区中时,您可以使用 ConvertTimeToUtc。例如,此测试代码通过了,但 utcDate1utcDate2 的值不同,即使它们使用相同的原始值。

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
    }
}

From a basic usage standpoint, you would use ConvertTimeToUtc when the DateTime being converted is not in your machine's timezone. For example, this test code passes, but the values of utcDate1 and utcDate2 are different even though they are working with the same original value.

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
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文