【问题标题】:What is faster alternative to TimeZoneInfo.ConvertTime?什么是 TimeZoneInfo.ConvertTime 的更快替代方案?
【发布时间】:2014-04-03 13:17:45
【问题描述】:

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

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

我将其比作 TimeSpan 的简单减法。请参阅下面的 SSCCE。

如果您注释掉 OPTION 1 或 OPTION 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 ;


        }
    }
}

【问题讨论】:

  • 你不能为你做一个选项 1 的辅助方法吗?这对我来说似乎是两全其美。
  • 问题是不同日期的时区变化。 TimeZoneInfo 负责所有这些
  • 说选项 1“更快”并不公平,因为这两个选项做不同的事情(正如您所指出的,选项 1 不考虑夏令时)
  • 这些基准测试的运行时间是多少?
  • 运行一亿次可以让任何代码变慢。 ConvertTime() 后面有相当多的时间,但在我的笔记本电脑上它仍然只需要 0.5 微秒。当它需要大约 800 个 CPU 周期时,没有太大的改进空间。这应该使程序阅读倍的瓶颈,I/O往往是真正的限制因素。读取数十亿条 dbase 记录或文本文件行需要一段时间。

标签: c# .net datetime timezone


【解决方案1】:

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

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

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

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

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

edtZone.BaseUtcOffset

而不是硬连线:

new TimeSpan(4,0,0)

使您的代码更具可读性。

【讨论】:

  • 我知道 - 这只是一个比较。因此我说我想要 TimeZoneInfo 的功能!
  • 同意...您可以使用慢速方法预先计算它们并将 EST 值缓存在某处吗?如果你需要这个可靠地正确,我不会推出你自己的。如果可以“可能”正确,那么是的,没什么大不了的。而且我知道你说过你只想要 EST,但如果你从那里扩展,你就会给自己一个受伤的世界。几个月前我看过这个,它让我害怕滚动我自己的时区转换器:youtu.be/HIToWgDVg54
【解决方案2】:

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

如果您实际上是按小时(或分钟或更小)执行 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;


    }
}

【讨论】:

  • 我不能使用字典,因为每个时间戳都是唯一的,因为我需要毫秒。
  • 你的工作范围是多少?您仍然可以使用查找表,因为夏令时仅在一小时或半小时内更改。 (除非有人想纠正我)。假设您需要精确到 0CE 到 3000CE 之间的分钟,您可以创建一个自定义 uint 哈希,其中 12 位表示年份,4 位表示月份,5 表示天,5 表示小时,6 表示分钟。您最终只会使用其中的一小部分。
【解决方案3】:

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

另外,TimeZoneInfo 的转换可能不如直接的TimeSpan 减法快,但正如其他人所指出的那样 - 他们没有做同样的事情。另请阅读the timezone tag wiki 中的“时区!= 偏移量”。

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

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

【讨论】:

  • >你真的会在这里看到几毫秒的性能显着差异吗? OP 提到他需要转换 十亿 个日期。 10 亿毫秒超过 11 天。所以看来 OP 可能会注意到 11 天的性能提升
猜你喜欢
  • 2021-03-14
  • 1970-01-01
  • 2012-10-06
  • 2018-12-11
  • 2012-07-05
  • 2012-01-23
  • 2013-07-13
  • 2011-02-27
相关资源
最近更新 更多