You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
System.Tests.Perf_DateTime.GetNow gets slower every summer due to daylight savings #67932
Is that with DST, or without? If GetDatePart is larger in DST, then it's perhaps because it's getting Year out of the DateTime in that block. The code before already gets Day and Month and Year, though. I wonder if there is opportunity to have GetDatePart() return all of those together rather than repeat its work. (Edit - I see that's GetDate())
Ah, ignore my flamegraph - it's for my local time where we don't have daylight savings. When I change my tz to the pacific one I now see a different picture - it seems there are quite a few things to optimize
is the codegen for GetDatePart() reasonable?
more or less, but we might need to extract it for Year separately since it's on a hot path and inline into Year property.
Also, it's executed from 3 different places in this flamegraph
changed the title [-]System.Tests.Perf_DateTime.GetNow has regressed across multiple configurations[/-][+]System.Tests.Perf_DateTime.GetNow gets slower every summer due to daylight savings[/+]on Aug 17, 2022
I measured the impact locally, including old frameworks for interest. Difference is only about 4% now, in .NET 6 it was 9% which is what the graph above shows from earlier. The recent #72712 will have contributed to that, because the DST path adds calls to Year, Month, Day.
@tarekgh it is OK to assume that timezone rules aren't changing during the lifetime of the process, right? So possibly the current rule and timezone can be cached, and if the timezone matches what was cached, we can continue to use that rule, and maybe short circuit simple cases. I'll leave as help wanted in case someone wants to take a look at some kind of caching of that sort. But otherwise I am not sure it is worth further attention.
usingSystem;usingSystem.ComponentModel;usingSystem.Runtime.InteropServices;usingBenchmarkDotNet.Attributes;usingBenchmarkDotNet.Configs;usingBenchmarkDotNet.Environments;usingBenchmarkDotNet.Jobs;usingBenchmarkDotNet.Running;[MemoryDiagnoser]publicclassProgram{// run as admin publicstaticvoidMain(string[]args){varconfig=DefaultConfig.Instance.AddJob(Job.Default.WithRuntime(CoreRuntime.Core31)).AddJob(Job.Default.WithRuntime(CoreRuntime.Core60)).AddJob(Job.Default.WithRuntime(CoreRuntime.CreateForNewVersion("net7.0",".NET 7.0"))).AddJob(Job.Default.WithRuntime(ClrRuntime.Net48));BenchmarkSwitcher.FromAssembly(typeof(Program).Assembly).Run(args,config);}[GlobalSetup(Target=nameof(DateTimeNow_NoDST))]publicvoidNoDST()=>SetSystemTime(newDateTime(2022,12,1));// assuming US[GlobalSetup(Target=nameof(DateTimeNow_DST))]publicvoidDST()=>SetSystemTime(newDateTime(2022,6,1));// assuming US[Benchmark(Baseline=true)]publicDateTimeDateTimeNow_NoDST()=>DateTime.Now;[Benchmark]publicDateTimeDateTimeNow_DST()=>DateTime.Now;publicvoidSetSystemTime(DateTimedt){SYSTEMTIMEst=newSYSTEMTIME();st.wYear=(short)dt.Year;st.wMonth=(short)dt.Month;st.wDay=(short)dt.Day;SetSystemTime(refst);interrorCode=Marshal.GetLastWin32Error();if(errorCode!=0)thrownewWin32Exception(errorCode);}[DllImport("kernel32.dll",SetLastError=true)]privatestaticexternboolSetSystemTime(refSYSTEMTIMEst);}[StructLayout(LayoutKind.Sequential)]publicstructSYSTEMTIME{publicshortwYear;publicshortwMonth;publicshortwDayOfWeek;publicshortwDay;publicshortwHour;publicshortwMinute;publicshortwSecond;publicshortwMilliseconds;}
Here's an idea of where the time is spent. There's not a clear "single place".
it is OK to assume that timezone rules aren't changing during the lifetime of the process, right?
Not really, users can change the local time zone and clear the cached Local TZ. But we can handle flushing the current rule if the local TZ cache is flushed. By the way, on Windows, we already cache the rule
The better idea here is not catching the rule and only cache the UTC offset and when the daylight will change. Doing that will make calculation of the Now much faster and simpler to handle too. I can try that when I get some chance.
Activity
EgorBo commentedon Apr 12, 2022
This is expected,
DateTime.Nowregresses and improves once a year at fixed dates due to DST ;-) https://twitter.com/EgorBo/status/1460697206667763714danmoseley commentedon Apr 12, 2022
this code only runs when we're in DST: https://github.com/dotnet/runtime/blob/67761043f1b0e1514a1b8c1c6d99a353737a24a1/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs#L1383-L1412
But, that doesn't seem like much. would need profiling.
EgorBo commentedon Apr 12, 2022
@danmoseley I don't think it executes that code as part ofDateTime.Now, I assume the regression is internal in kernel32EgorBo commentedon Apr 12, 2022
oh, or maybe it does?

danmoseley commentedon Apr 13, 2022
Is that with DST, or without? If GetDatePart is larger in DST, then it's perhaps because it's getting Year out of the DateTime in that block. The code before already gets Day and Month and Year, though. I wonder if there is opportunity to have GetDatePart() return all of those together rather than repeat its work. (Edit - I see that's GetDate())
is the codegen for GetDatePart() reasonable?
EgorBo commentedon Apr 13, 2022
Ah, ignore my flamegraph - it's for my local time where we don't have daylight savings. When I change my tz to the pacific one I now see a different picture - it seems there are quite a few things to optimize

more or less, but we might need to extract it for Year separately since it's on a hot path and inline into
Yearproperty.Also, it's executed from 3 different places in this flamegraph
EgorBo commentedon Apr 13, 2022
I'll take a look when I have some spare time 🙂
[-]System.Tests.Perf_DateTime.GetNow has regressed across multiple configurations[/-][+]System.Tests.Perf_DateTime.GetNow gets slower every summer due to daylight savings[/+]danmoseley commentedon Aug 20, 2022
I measured the impact locally, including old frameworks for interest. Difference is only about 4% now, in .NET 6 it was 9% which is what the graph above shows from earlier. The recent #72712 will have contributed to that, because the DST path adds calls to Year, Month, Day.
@tarekgh it is OK to assume that timezone rules aren't changing during the lifetime of the process, right? So possibly the current rule and timezone can be cached, and if the timezone matches what was cached, we can continue to use that rule, and maybe short circuit simple cases. I'll leave as help wanted in case someone wants to take a look at some kind of caching of that sort. But otherwise I am not sure it is worth further attention.
benchmark
Here's an idea of where the time is spent. There's not a clear "single place".

tarekgh commentedon Aug 20, 2022
Not really, users can change the local time zone and clear the cached Local TZ. But we can handle flushing the current rule if the local TZ cache is flushed. By the way, on Windows, we already cache the rule
runtime/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs
Line 374 in 6d60d06
The better idea here is not catching the rule and only cache the UTC offset and when the daylight will change. Doing that will make calculation of the
Nowmuch faster and simpler to handle too. I can try that when I get some chance.stephentoub commentedon Oct 11, 2025
@tarekgh, is this still relevant with your recent improvements?
tarekgh commentedon Oct 11, 2025
Yes, this should be addressed by #119662. Thanks @stephentoub for pointing out at this issue.