什么是TimeZoneInfo.ConvertTime的更快替代方案

本文关键字:方案 TimeZoneInfo ConvertTime 什么 | 更新日期: 2023-09-27 17:56:20

我需要定期将数十亿个日期时间从UTC转换为EDT。

TimeZoneInfo.ConvertTime非常方便,但非常非常慢。

我比较了时间跨度的简单减法。请参阅下面的SSCCE。

如果您注释掉选项 1 或选项 2(如代码所示),您将看到截然不同的运行时间。

我想要该功能,但需要更快的速度,有没有办法做到这一点?

using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Diagnostics;
using System.Drawing;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.IO;
using System.Globalization;

namespace BinTest2
{
    public partial class Form1 : Form
    {
        static TimeZoneInfo edtZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
        static TimeZoneInfo gmtZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
        static TimeZoneInfo utcZone = TimeZoneInfo.FindSystemTimeZoneById("UTC");
        public static CultureInfo ci = CultureInfo.InvariantCulture;
        private void button5_Click(object sender, EventArgs e)
        {
            Stopwatch sw = new Stopwatch();
            sw.Start();
            DateTime DT = new DateTime(2013,01,01);
            DateTime TDT;
            TimeSpan TS = new TimeSpan(4,0,0);
            for (int i = 0; i < 100000000; i++)
            {
                //TDT = DT - TS;   //OPTION 1
                TDT = TimeZoneInfo.ConvertTime(DT, utcZone, edtZone);  //OPTION 2
            }
            sw.Stop();
            label1.Text = "Time taken: " + sw.ElapsedMilliseconds ;

        }
    }
}

什么是TimeZoneInfo.ConvertTime的更快替代方案

在考虑性能之前,请考虑正确性。

你的两个例子给出了不同的结果。

东部时间在夏令时比 UTC 晚 4 小时;不运行时为 5 小时。 您的简单减法没有考虑到这一点(实际上,对于您指定的采样日期给出了错误的结果 - 差值是 2013 年 1 月 1 日的 5h)。

此外,您还需要警惕自制的基准测试,这可能会产生不切实际的结果,尤其是在您的应用程序不是在发布模式下构建的情况下。

如果想要的结果不考虑夏令时,可以使用:

edtZone.BaseUtcOffset

而不是硬连线:

new TimeSpan(4,0,0)

使代码更具可读性。

根据您要处理的日期范围,您可以使用字典来完成。 这样,第二次这样做时,它会更快。 当然,这只有在您重复执行有限天数时才有效。 如果你有一个更大的范围,你每天做一次(很难想象数十亿个 - 地球只有这么老:)那么这不会那么好用。 另外,您必须牢记记忆。

如果您实际上是按小时(

或使用分钟或更精细)执行 DT,则需要使用涵盖小时和分钟的整数索引,以便您可以正确处理夏令时更改。

public partial class Form1 : Form
{
    Dictionary<DateTime,TimeSpan> lut = new Dictionary<DateTime, TimeSpan>(); 
    public Form1()
    {
        InitializeComponent();
    }
    static TimeZoneInfo edtZone = TimeZoneInfo.FindSystemTimeZoneById("Eastern Standard Time");
    static TimeZoneInfo gmtZone = TimeZoneInfo.FindSystemTimeZoneById("GMT Standard Time");
    static TimeZoneInfo utcZone = TimeZoneInfo.FindSystemTimeZoneById("UTC");
    public static CultureInfo ci = CultureInfo.InvariantCulture;
    private void button5_Click(object sender, EventArgs e)
    {
        Stopwatch sw = new Stopwatch();
        sw.Start();
        DateTime DT = new DateTime(2013, 01, 01);
        DateTime TDT;
        TimeSpan TS = new TimeSpan(5, 0, 0);
        for (int i = 0; i < 100000000; i++)
        {
            if (!lut.ContainsKey(DT))
            {
                lut[DT] = DT - TimeZoneInfo.ConvertTime(DT, utcZone, edtZone);                    
            }
            TDT = DT - lut[DT];
        }
        sw.Stop();
        label1.Text = "Time taken: " + sw.ElapsedMilliseconds;

    }
}

总的来说,我认为您正在尝试进行微优化。 您真的会在这里通过提高几毫秒来看到性能的显着差异吗? 不太可能。

此外,TimeZoneInfo转换可能不如直接TimeSpan减法快,但正如其他人指出的那样 - 他们没有做同样的事情。 另请阅读时区标签维基中的"时区!=偏移量"。

如果你正在寻找TimeZoneInfo的替代品,我建议你在野田时间评估DateTimeZone类。 Jon Skeet是它的主要作者,他当然专注于性能调整。 我不是 100% 确定它是否比TimeZoneInfo更快,但我知道它肯定更实用、更准确。

另外,您可能已经意识到这一点,但值得指出。 "GMT Standard Time"不是格林威治标准时间的时区。 它是"都柏林、爱丁堡、里斯本、伦敦"的 Windows 时区 ID。 它在夏季的GMT(UTC+00:00)和冬季的BST(UTC+01:00)之间交替。 同样,Windows 时区标识符"Eastern Standard Time"表示 EST (UTC-05:00) 和 EDT (UTC-04:00)。 这就是为什么时区转换比简单的减法更复杂的原因。

即使在

最新版本的 .NET 6 中,将时间转换为 UTC 或从 UTC 转换时间也很慢的原因是"DST" - 夏令时。

您不能像这样添加偏移量:

//superfast but won't work with DST
var result = utcDate + myTimeZone.BaseUtcOffset;

您需要弄清楚日期是否属于DST范围,为此您需要调用IsDaylightSavingTime()这是所有缓慢的原因。

MS 的 Github for .NET 运行时存在一个未解决的问题,但它没有得到太多关注 https://github.com/dotnet/runtime/issues/25075

唯一的解决方案是放弃 DST(如果可以的话)或提出自己的 DST 缓存/计算机制。