Avoid allocations in DateTime.Now
authorStephen Toub <stoub@microsoft.com>
Sat, 12 Nov 2016 12:51:08 +0000 (07:51 -0500)
committerStephen Toub <stoub@microsoft.com>
Sat, 12 Nov 2016 12:52:50 +0000 (07:52 -0500)
Every call to DateTime.Now ends up allocating a DaylightTime as it computes the offset from UtcNow.  DaylightTime is just a plain old object that stores two DateTimes and a Timespan.  Ideally we could just change it to be a struct, but it's public.  Instead, this commit adds an internal DaylightTimeStruct, which is the same as DaylightTime except as a value type, and changes some of the internal uses of DaylightTime to instead use DaylightTime.  As a result, DateTime.Now is allocation-free.

Commit migrated from https://github.com/dotnet/coreclr/commit/2457e2b3831d73ff5178961a62f7a6fcc332eceb

src/coreclr/src/mscorlib/src/System/Globalization/DaylightTime.cs
src/coreclr/src/mscorlib/src/System/TimeZoneInfo.cs

index 037d9ff..a164867 100644 (file)
@@ -2,12 +2,13 @@
 // The .NET Foundation licenses this file to you under the MIT license.
 // See the LICENSE file in the project root for more information.
 
-namespace System.Globalization {
-   
-    using System;
+using System.Runtime.InteropServices;
+
+namespace System.Globalization
+{
     // This class represents a starting/ending time for a period of daylight saving time.
     [Serializable]
-    [System.Runtime.InteropServices.ComVisible(true)]
+    [ComVisible(true)]
     public class DaylightTime
     {
         internal DateTime m_start;
@@ -46,4 +47,18 @@ namespace System.Globalization {
     
     }
 
+    // Value type version of DaylightTime
+    internal struct DaylightTimeStruct
+    {
+        public DaylightTimeStruct(DateTime start, DateTime end, TimeSpan delta)
+        {
+            Start = start;
+            End = end;
+            Delta = delta;
+        }
+
+        public DateTime Start { get; }
+        public DateTime End { get; }
+        public TimeSpan Delta { get; }
+    }
 }
index e41c0bd..85f28ca 100644 (file)
@@ -388,7 +388,7 @@ namespace System {
             Boolean isAmbiguous = false;
             AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime);
             if (rule != null && rule.HasDaylightSaving) {
-                DaylightTime daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+                DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
                 isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
             }
 
@@ -436,7 +436,7 @@ namespace System {
             Boolean isAmbiguous = false;
             AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime);
             if (rule != null && rule.HasDaylightSaving) {
-                DaylightTime daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+                DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
                 isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
             }
 
@@ -600,7 +600,7 @@ namespace System {
 
             AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
             if (rule != null && rule.HasDaylightSaving) {
-                DaylightTime daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+                DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
                 return GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
             }
             return false;
@@ -678,7 +678,7 @@ namespace System {
             //
             AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
             if (rule != null && rule.HasDaylightSaving) {
-                DaylightTime daylightTime = GetDaylightTime(adjustedTime.Year, rule);
+                DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
                 return GetIsDaylightSavings(adjustedTime, rule, daylightTime, flags);
             }
             else {
@@ -702,7 +702,7 @@ namespace System {
                 AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime);
 
                 if (rule != null && rule.HasDaylightSaving) {
-                    DaylightTime daylightTime = GetDaylightTime(dateTime.Year, rule);
+                    DaylightTimeStruct daylightTime = GetDaylightTime(dateTime.Year, rule);
                     isInvalid = GetIsInvalidTime(dateTime, rule, daylightTime);
                 }
                 else {
@@ -855,7 +855,7 @@ namespace System {
                 sourceOffset = sourceOffset + sourceRule.BaseUtcOffsetDelta;
                 if (sourceRule.HasDaylightSaving) {
                     Boolean sourceIsDaylightSavings = false;
-                    DaylightTime sourceDaylightTime = sourceTimeZone.GetDaylightTime(dateTime.Year, sourceRule);
+                    DaylightTimeStruct sourceDaylightTime = sourceTimeZone.GetDaylightTime(dateTime.Year, sourceRule);
 
                     // 'dateTime' might be in an invalid time range since it is in an AdjustmentRule
                     // period that supports DST 
@@ -1749,7 +1749,7 @@ namespace System {
         //
         // Helper function that returns a DaylightTime from a year and AdjustmentRule
         //
-        private DaylightTime GetDaylightTime(Int32 year, AdjustmentRule rule) {
+        private DaylightTimeStruct GetDaylightTime(Int32 year, AdjustmentRule rule) {
             TimeSpan delta = rule.DaylightDelta;
             DateTime startTime;
             DateTime endTime;
@@ -1770,7 +1770,7 @@ namespace System {
                 startTime = TransitionTimeToDateTime(year, rule.DaylightTransitionStart);
                 endTime = TransitionTimeToDateTime(year, rule.DaylightTransitionEnd);
             }
-            return new DaylightTime(startTime, endTime, delta);
+            return new DaylightTimeStruct(startTime, endTime, delta);
         }
 
         //
@@ -1779,7 +1779,7 @@ namespace System {
         // Helper function that checks if a given dateTime is in Daylight Saving Time (DST)
         // This function assumes the dateTime and AdjustmentRule are both in the same time zone
         //
-        static private Boolean GetIsDaylightSavings(DateTime time, AdjustmentRule rule, DaylightTime daylightTime, TimeZoneInfoOptions flags) {
+        static private Boolean GetIsDaylightSavings(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime, TimeZoneInfoOptions flags) {
             if (rule == null) {
                 return false;
             }
@@ -1874,7 +1874,7 @@ namespace System {
 
 
             // Get the daylight changes for the year of the specified time.
-            DaylightTime daylightTime = zone.GetDaylightTime(Year, rule);
+            DaylightTimeStruct daylightTime = zone.GetDaylightTime(Year, rule);
 
             // The start and end times represent the range of universal times that are in DST for that year.                
             // Within that there is an ambiguous hour, usually right at the end, but at the beginning in
@@ -1893,7 +1893,7 @@ namespace System {
             if (rule.IsStartDateMarkerForBeginningOfYear() && daylightTime.Start.Year > DateTime.MinValue.Year) {
                 AdjustmentRule previousYearRule = zone.GetAdjustmentRuleForTime(new DateTime(daylightTime.Start.Year - 1, 12, 31));
                 if (previousYearRule != null && previousYearRule.IsEndDateMarkerForEndOfYear()) {
-                    DaylightTime previousDaylightTime = zone.GetDaylightTime(daylightTime.Start.Year - 1, previousYearRule);
+                    DaylightTimeStruct previousDaylightTime = zone.GetDaylightTime(daylightTime.Start.Year - 1, previousYearRule);
                     startTime = previousDaylightTime.Start - utc - previousYearRule.BaseUtcOffsetDelta;
                     ignoreYearAdjustment = true;
                 } else {
@@ -1911,7 +1911,7 @@ namespace System {
                     if (nextYearRule.IsEndDateMarkerForEndOfYear()) {// next year end with daylight saving on too
                         endTime = new DateTime(daylightTime.End.Year + 1, 12, 31) - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta;
                     } else {
-                        DaylightTime nextdaylightTime = zone.GetDaylightTime(daylightTime.End.Year + 1, nextYearRule);
+                        DaylightTimeStruct nextdaylightTime = zone.GetDaylightTime(daylightTime.End.Year + 1, nextYearRule);
                         endTime = nextdaylightTime.End - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta;
                     }
                     ignoreYearAdjustment = true;
@@ -2017,7 +2017,7 @@ namespace System {
         // In this example, any DateTime values that fall into the [1AM - 1:59:59AM] range
         // are ambiguous; as it is unclear if these times are in Daylight Saving Time.
         //
-        static private Boolean GetIsAmbiguousTime(DateTime time, AdjustmentRule rule, DaylightTime daylightTime) {
+        static private Boolean GetIsAmbiguousTime(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime) {
             Boolean isAmbiguous = false;
             if (rule == null || rule.DaylightDelta == TimeSpan.Zero) {
                 return isAmbiguous;
@@ -2081,7 +2081,7 @@ namespace System {
         // A "time hole" is not limited to only occurring at the start of DST, and may occur at
         // the end of DST as well.
         //
-        static private Boolean GetIsInvalidTime(DateTime time, AdjustmentRule rule, DaylightTime daylightTime) {
+        static private Boolean GetIsInvalidTime(DateTime time, AdjustmentRule rule, DaylightTimeStruct daylightTime) {
             Boolean isInvalid = false;
             if (rule == null || rule.DaylightDelta == TimeSpan.Zero) {
                 return isInvalid;
@@ -2684,7 +2684,7 @@ namespace System {
             if (rule != null) {
                 baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
                 if (rule.HasDaylightSaving) {
-                    DaylightTime daylightTime = zone.GetDaylightTime(time.Year, rule);
+                    DaylightTimeStruct daylightTime = zone.GetDaylightTime(time.Year, rule);
                     Boolean isDaylightSavings = GetIsDaylightSavings(time, rule, daylightTime, flags);
                     baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
                 }