我刚刚注意到我们的一个单元测试占用了十秒钟。在尝试了它之后,我创建了一个最小的 linqpad 示例来重现它:
void Main()
{
Stopwatch sw = new Stopwatch();
sw.Start();
var timeZone = TimeZoneInfo.Local;
for(int i=0; i<10000; i++)
{
//var dateTime = TimeZoneInfo.ConvertTime(DateTime.UtcNow, timeZone);
var dateTime = TimeZoneInfo.ConvertTime(DateTime.MinValue, timeZone);
}
sw.Stop();
(sw.ElapsedMilliseconds + " ms").Dump();
}
在我的电脑或我们的内部电脑上,这需要 40 秒。如果我使用DateTime.UtcNow,则需要15毫秒。
有什么原因或解决方法吗?
编辑:正如评论中所建议的,我反编译了TimeZoneInfo,并且在DateTime.MinValue上有一个特殊情况:
static public DateTime ConvertTime(DateTime dateTime, TimeZoneInfo destinationTimeZone) {
// Special case to give a way clearing the cache without exposing ClearCachedData()
if (dateTime.Ticks == 0) {
ClearCachedData();
}
看起来像(测试?(代码,每次使用 DateTime.MinValue 调用缓存时都会清除缓存。
留下了为什么会发生这种情况的问题。
没有必要反编译。 您可以在 .NET Framework 参考源代码或 GitHub 上的 CoreCLR 源代码中找到此信息。
此特殊情况是在 .NET 4.6 中添加的,作为清除当时未公开 ClearCachedData
方法的环境(如 WinRT(中的时区缓存的非官方解决方法。 我在这里进一步描述这一点。
偶尔清除时区缓存通常不会产生明显效果。 你看到它是因为紧密的循环。 如果这是您的用例的问题,那么我建议使用 ConvertTimeFromUtc
方法或与 DateTimeOffset
一起使用的ConvertTime
版本。 这些代码路径不会遇到特殊情况。
是的,我同意这应该更好地记录下来。 我看看我能不能把它弄进去。
请注意,从逻辑上讲,将时区转换为DateTime.MinValue
没有多大意义,因为我们今天所知道的时区在第 1 年不存在。