Skip to content
This repository was archived by the owner on Jan 23, 2023. It is now read-only.

Commit e6a9c16

Browse files
authored
Merge pull request #10948 from eerhardt/GetPreviousAdjRulePerf
Fix perf of DateTime.Now on Unix
2 parents 9a4e8f5 + d9820dd commit e6a9c16

File tree

2 files changed

+80
-36
lines changed

2 files changed

+80
-36
lines changed

src/mscorlib/src/System/TimeZoneInfo.Win32.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -398,7 +398,7 @@ internal static TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out bool
398398
baseOffset = baseOffset + match.Rule.BaseUtcOffsetDelta;
399399
if (match.Rule.HasDaylightSaving)
400400
{
401-
isDaylightSavings = GetIsDaylightSavingsFromUtc(time, timeYear, match.Offset, match.Rule, out isAmbiguousLocalDst, Local);
401+
isDaylightSavings = GetIsDaylightSavingsFromUtc(time, timeYear, match.Offset, match.Rule, null, out isAmbiguousLocalDst, Local);
402402
baseOffset += (isDaylightSavings ? match.Rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
403403
}
404404
}

src/mscorlib/src/System/TimeZoneInfo.cs

Lines changed: 79 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -172,10 +172,11 @@ public TimeSpan[] GetAmbiguousTimeOffsets(DateTimeOffset dateTimeOffset)
172172
DateTime adjustedTime = ConvertTime(dateTimeOffset, this).DateTime;
173173

174174
bool isAmbiguous = false;
175-
AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime);
175+
int? ruleIndex;
176+
AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime, out ruleIndex);
176177
if (rule != null && rule.HasDaylightSaving)
177178
{
178-
DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
179+
DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
179180
isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
180181
}
181182

@@ -232,10 +233,11 @@ public TimeSpan[] GetAmbiguousTimeOffsets(DateTime dateTime)
232233
}
233234

234235
bool isAmbiguous = false;
235-
AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime);
236+
int? ruleIndex;
237+
AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets(adjustedTime, out ruleIndex);
236238
if (rule != null && rule.HasDaylightSaving)
237239
{
238-
DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
240+
DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
239241
isAmbiguous = GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
240242
}
241243

@@ -263,15 +265,15 @@ public TimeSpan[] GetAmbiguousTimeOffsets(DateTime dateTime)
263265
}
264266

265267
// note the time is already adjusted
266-
private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets(DateTime adjustedTime)
268+
private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets(DateTime adjustedTime, out int? ruleIndex)
267269
{
268-
AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
270+
AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
269271
if (rule != null && rule.NoDaylightTransitions && !rule.HasDaylightSaving)
270272
{
271273
// When using NoDaylightTransitions rules, each rule is only for one offset.
272274
// When looking for the Daylight savings rules, and we found the non-DST rule,
273275
// then we get the rule right before this rule.
274-
return GetPreviousAdjustmentRule(rule);
276+
return GetPreviousAdjustmentRule(rule, ruleIndex);
275277
}
276278

277279
return rule;
@@ -282,12 +284,23 @@ private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets(DateTime adjustedTim
282284
/// If the specified rule is the first AdjustmentRule, or it isn't in _adjustmentRules,
283285
/// then the specified rule is returned.
284286
/// </summary>
285-
private AdjustmentRule GetPreviousAdjustmentRule(AdjustmentRule rule)
287+
private AdjustmentRule GetPreviousAdjustmentRule(AdjustmentRule rule, int? ruleIndex)
286288
{
289+
Debug.Assert(rule.NoDaylightTransitions, "GetPreviousAdjustmentRule should only be used with NoDaylightTransitions rules.");
290+
291+
if (ruleIndex.HasValue && 0 < ruleIndex.Value && ruleIndex.Value < _adjustmentRules.Length)
292+
{
293+
return _adjustmentRules[ruleIndex.Value - 1];
294+
}
295+
287296
AdjustmentRule result = rule;
288297
for (int i = 1; i < _adjustmentRules.Length; i++)
289298
{
290-
if (rule.Equals(_adjustmentRules[i]))
299+
// use ReferenceEquals here instead of AdjustmentRule.Equals because
300+
// ReferenceEquals is much faster. This is safe because all the callers
301+
// of GetPreviousAdjustmentRule pass in a rule that was retrieved from
302+
// _adjustmentRules. A different approach will be needed if this ever changes.
303+
if (ReferenceEquals(rule, _adjustmentRules[i]))
291304
{
292305
result = _adjustmentRules[i - 1];
293306
break;
@@ -407,10 +420,11 @@ internal bool IsAmbiguousTime(DateTime dateTime, TimeZoneInfoOptions flags)
407420
dateTime.Kind == DateTimeKind.Utc ? ConvertTime(dateTime, s_utcTimeZone, this, flags, cachedData) :
408421
dateTime;
409422

410-
AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
423+
int? ruleIndex;
424+
AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
411425
if (rule != null && rule.HasDaylightSaving)
412426
{
413-
DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
427+
DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
414428
return GetIsAmbiguousTime(adjustedTime, rule, daylightTime);
415429
}
416430
return false;
@@ -492,10 +506,11 @@ private bool IsDaylightSavingTime(DateTime dateTime, TimeZoneInfoOptions flags,
492506
//
493507
// handle the normal cases...
494508
//
495-
AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime);
509+
int? ruleIndex;
510+
AdjustmentRule rule = GetAdjustmentRuleForTime(adjustedTime, out ruleIndex);
496511
if (rule != null && rule.HasDaylightSaving)
497512
{
498-
DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule);
513+
DaylightTimeStruct daylightTime = GetDaylightTime(adjustedTime.Year, rule, ruleIndex);
499514
return GetIsDaylightSavings(adjustedTime, rule, daylightTime, flags);
500515
}
501516
else
@@ -515,11 +530,12 @@ public bool IsInvalidTime(DateTime dateTime)
515530
(dateTime.Kind == DateTimeKind.Local && s_cachedData.GetCorrespondingKind(this) == DateTimeKind.Local))
516531
{
517532
// only check Unspecified and (Local when this TimeZoneInfo instance is Local)
518-
AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime);
533+
int? ruleIndex;
534+
AdjustmentRule rule = GetAdjustmentRuleForTime(dateTime, out ruleIndex);
519535

520536
if (rule != null && rule.HasDaylightSaving)
521537
{
522-
DaylightTimeStruct daylightTime = GetDaylightTime(dateTime.Year, rule);
538+
DaylightTimeStruct daylightTime = GetDaylightTime(dateTime.Year, rule, ruleIndex);
523539
isInvalid = GetIsInvalidTime(dateTime, rule, daylightTime);
524540
}
525541
else
@@ -661,7 +677,8 @@ private static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZo
661677
// performance for the normal case at the expense of the 'ArgumentException'
662678
// case and Loss-less Local special cases.
663679
//
664-
AdjustmentRule sourceRule = sourceTimeZone.GetAdjustmentRuleForTime(dateTime);
680+
int? sourceRuleIndex;
681+
AdjustmentRule sourceRule = sourceTimeZone.GetAdjustmentRuleForTime(dateTime, out sourceRuleIndex);
665682
TimeSpan sourceOffset = sourceTimeZone.BaseUtcOffset;
666683

667684
if (sourceRule != null)
@@ -670,7 +687,7 @@ private static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZo
670687
if (sourceRule.HasDaylightSaving)
671688
{
672689
bool sourceIsDaylightSavings = false;
673-
DaylightTimeStruct sourceDaylightTime = sourceTimeZone.GetDaylightTime(dateTime.Year, sourceRule);
690+
DaylightTimeStruct sourceDaylightTime = sourceTimeZone.GetDaylightTime(dateTime.Year, sourceRule, sourceRuleIndex);
674691

675692
// 'dateTime' might be in an invalid time range since it is in an AdjustmentRule
676693
// period that supports DST
@@ -1048,10 +1065,19 @@ private TimeZoneInfo(SerializationInfo info, StreamingContext context)
10481065
_supportsDaylightSavingTime = (bool)info.GetValue("SupportsDaylightSavingTime", typeof(bool));
10491066
}
10501067

1051-
private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTimeisUtc = false)
1068+
private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, out int? ruleIndex)
1069+
{
1070+
AdjustmentRule result = GetAdjustmentRuleForTime(dateTime, dateTimeisUtc: false, ruleIndex: out ruleIndex);
1071+
Debug.Assert(result == null || ruleIndex.HasValue, "If an AdjustmentRule was found, ruleIndex should also be set.");
1072+
1073+
return result;
1074+
}
1075+
1076+
private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTimeisUtc, out int? ruleIndex)
10521077
{
10531078
if (_adjustmentRules == null || _adjustmentRules.Length == 0)
10541079
{
1080+
ruleIndex = null;
10551081
return null;
10561082
}
10571083

@@ -1076,6 +1102,7 @@ private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTime
10761102
int compareResult = CompareAdjustmentRuleToDateTime(rule, previousRule, dateTime, date, dateTimeisUtc);
10771103
if (compareResult == 0)
10781104
{
1105+
ruleIndex = median;
10791106
return rule;
10801107
}
10811108
else if (compareResult < 0)
@@ -1088,6 +1115,7 @@ private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTime
10881115
}
10891116
}
10901117

1118+
ruleIndex = null;
10911119
return null;
10921120
}
10931121

@@ -1199,7 +1227,7 @@ private static DateTime ConvertUtcToTimeZone(long ticks, TimeZoneInfo destinatio
11991227
/// <summary>
12001228
/// Helper function that returns a DaylightTime from a year and AdjustmentRule.
12011229
/// </summary>
1202-
private DaylightTimeStruct GetDaylightTime(int year, AdjustmentRule rule)
1230+
private DaylightTimeStruct GetDaylightTime(int year, AdjustmentRule rule, int? ruleIndex)
12031231
{
12041232
TimeSpan delta = rule.DaylightDelta;
12051233
DateTime startTime;
@@ -1211,7 +1239,7 @@ private DaylightTimeStruct GetDaylightTime(int year, AdjustmentRule rule)
12111239
// Convert the UTC times into adjusted time zone times.
12121240

12131241
// use the previous rule to calculate the startTime, since the DST change happens w.r.t. the previous rule
1214-
AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule);
1242+
AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule, ruleIndex);
12151243
startTime = ConvertFromUtc(rule.DateStart, previousRule.DaylightDelta, previousRule.BaseUtcOffsetDelta);
12161244

12171245
endTime = ConvertFromUtc(rule.DateEnd, rule.DaylightDelta, rule.BaseUtcOffsetDelta);
@@ -1301,12 +1329,12 @@ private static bool GetIsDaylightSavings(DateTime time, AdjustmentRule rule, Day
13011329
/// <summary>
13021330
/// Gets the offset that should be used to calculate DST start times from a UTC time.
13031331
/// </summary>
1304-
private TimeSpan GetDaylightSavingsStartOffsetFromUtc(TimeSpan baseUtcOffset, AdjustmentRule rule)
1332+
private TimeSpan GetDaylightSavingsStartOffsetFromUtc(TimeSpan baseUtcOffset, AdjustmentRule rule, int? ruleIndex)
13051333
{
13061334
if (rule.NoDaylightTransitions)
13071335
{
13081336
// use the previous rule to calculate the startTime, since the DST change happens w.r.t. the previous rule
1309-
AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule);
1337+
AdjustmentRule previousRule = GetPreviousAdjustmentRule(rule, ruleIndex);
13101338
return baseUtcOffset + previousRule.BaseUtcOffsetDelta + previousRule.DaylightDelta;
13111339
}
13121340
else
@@ -1328,7 +1356,7 @@ private TimeSpan GetDaylightSavingsEndOffsetFromUtc(TimeSpan baseUtcOffset, Adju
13281356
/// Helper function that checks if a given dateTime is in Daylight Saving Time (DST).
13291357
/// This function assumes the dateTime is in UTC and AdjustmentRule is in a different time zone.
13301358
/// </summary>
1331-
private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpan utc, AdjustmentRule rule, out bool isAmbiguousLocalDst, TimeZoneInfo zone)
1359+
private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpan utc, AdjustmentRule rule, int? ruleIndex, out bool isAmbiguousLocalDst, TimeZoneInfo zone)
13321360
{
13331361
isAmbiguousLocalDst = false;
13341362

@@ -1338,7 +1366,7 @@ private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpa
13381366
}
13391367

13401368
// Get the daylight changes for the year of the specified time.
1341-
DaylightTimeStruct daylightTime = zone.GetDaylightTime(year, rule);
1369+
DaylightTimeStruct daylightTime = zone.GetDaylightTime(year, rule, ruleIndex);
13421370

13431371
// The start and end times represent the range of universal times that are in DST for that year.
13441372
// Within that there is an ambiguous hour, usually right at the end, but at the beginning in
@@ -1352,14 +1380,20 @@ private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpa
13521380
// Note we handle the similar case when rule year start with daylight saving and previous year end with daylight saving.
13531381

13541382
bool ignoreYearAdjustment = false;
1355-
TimeSpan dstStartOffset = zone.GetDaylightSavingsStartOffsetFromUtc(utc, rule);
1383+
TimeSpan dstStartOffset = zone.GetDaylightSavingsStartOffsetFromUtc(utc, rule, ruleIndex);
13561384
DateTime startTime;
13571385
if (rule.IsStartDateMarkerForBeginningOfYear() && daylightTime.Start.Year > DateTime.MinValue.Year)
13581386
{
1359-
AdjustmentRule previousYearRule = zone.GetAdjustmentRuleForTime(new DateTime(daylightTime.Start.Year - 1, 12, 31));
1387+
int? previousYearRuleIndex;
1388+
AdjustmentRule previousYearRule = zone.GetAdjustmentRuleForTime(
1389+
new DateTime(daylightTime.Start.Year - 1, 12, 31),
1390+
out previousYearRuleIndex);
13601391
if (previousYearRule != null && previousYearRule.IsEndDateMarkerForEndOfYear())
13611392
{
1362-
DaylightTimeStruct previousDaylightTime = zone.GetDaylightTime(daylightTime.Start.Year - 1, previousYearRule);
1393+
DaylightTimeStruct previousDaylightTime = zone.GetDaylightTime(
1394+
daylightTime.Start.Year - 1,
1395+
previousYearRule,
1396+
previousYearRuleIndex);
13631397
startTime = previousDaylightTime.Start - utc - previousYearRule.BaseUtcOffsetDelta;
13641398
ignoreYearAdjustment = true;
13651399
}
@@ -1377,7 +1411,10 @@ private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpa
13771411
DateTime endTime;
13781412
if (rule.IsEndDateMarkerForEndOfYear() && daylightTime.End.Year < DateTime.MaxValue.Year)
13791413
{
1380-
AdjustmentRule nextYearRule = zone.GetAdjustmentRuleForTime(new DateTime(daylightTime.End.Year + 1, 1, 1));
1414+
int? nextYearRuleIndex;
1415+
AdjustmentRule nextYearRule = zone.GetAdjustmentRuleForTime(
1416+
new DateTime(daylightTime.End.Year + 1, 1, 1),
1417+
out nextYearRuleIndex);
13811418
if (nextYearRule != null && nextYearRule.IsStartDateMarkerForBeginningOfYear())
13821419
{
13831420
if (nextYearRule.IsEndDateMarkerForEndOfYear())
@@ -1387,7 +1424,10 @@ private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpa
13871424
}
13881425
else
13891426
{
1390-
DaylightTimeStruct nextdaylightTime = zone.GetDaylightTime(daylightTime.End.Year + 1, nextYearRule);
1427+
DaylightTimeStruct nextdaylightTime = zone.GetDaylightTime(
1428+
daylightTime.End.Year + 1,
1429+
nextYearRule,
1430+
nextYearRuleIndex);
13911431
endTime = nextdaylightTime.End - utc - nextYearRule.BaseUtcOffsetDelta - nextYearRule.DaylightDelta;
13921432
}
13931433
ignoreYearAdjustment = true;
@@ -1644,14 +1684,15 @@ private static bool GetIsInvalidTime(DateTime time, AdjustmentRule rule, Dayligh
16441684
private static TimeSpan GetUtcOffset(DateTime time, TimeZoneInfo zone, TimeZoneInfoOptions flags)
16451685
{
16461686
TimeSpan baseOffset = zone.BaseUtcOffset;
1647-
AdjustmentRule rule = zone.GetAdjustmentRuleForTime(time);
1687+
int? ruleIndex;
1688+
AdjustmentRule rule = zone.GetAdjustmentRuleForTime(time, out ruleIndex);
16481689

16491690
if (rule != null)
16501691
{
16511692
baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
16521693
if (rule.HasDaylightSaving)
16531694
{
1654-
DaylightTimeStruct daylightTime = zone.GetDaylightTime(time.Year, rule);
1695+
DaylightTimeStruct daylightTime = zone.GetDaylightTime(time.Year, rule, ruleIndex);
16551696
bool isDaylightSavings = GetIsDaylightSavings(time, rule, daylightTime, flags);
16561697
baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
16571698
}
@@ -1690,21 +1731,24 @@ internal static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone, o
16901731
isAmbiguousLocalDst = false;
16911732
TimeSpan baseOffset = zone.BaseUtcOffset;
16921733
int year;
1734+
int? ruleIndex;
16931735
AdjustmentRule rule;
16941736

16951737
if (time > s_maxDateOnly)
16961738
{
1697-
rule = zone.GetAdjustmentRuleForTime(DateTime.MaxValue);
1739+
rule = zone.GetAdjustmentRuleForTime(DateTime.MaxValue, out ruleIndex);
16981740
year = 9999;
16991741
}
17001742
else if (time < s_minDateOnly)
17011743
{
1702-
rule = zone.GetAdjustmentRuleForTime(DateTime.MinValue);
1744+
rule = zone.GetAdjustmentRuleForTime(DateTime.MinValue, out ruleIndex);
17031745
year = 1;
17041746
}
17051747
else
17061748
{
1707-
rule = zone.GetAdjustmentRuleForTime(time, dateTimeisUtc: true);
1749+
rule = zone.GetAdjustmentRuleForTime(time, dateTimeisUtc: true, ruleIndex: out ruleIndex);
1750+
Debug.Assert(rule == null || ruleIndex.HasValue,
1751+
"If GetAdjustmentRuleForTime returned an AdjustmentRule, ruleIndex should also be set.");
17081752

17091753
// As we get the associated rule using the adjusted targetTime, we should use the adjusted year (targetTime.Year) too as after adding the baseOffset,
17101754
// sometimes the year value can change if the input datetime was very close to the beginning or the end of the year. Examples of such cases:
@@ -1719,7 +1763,7 @@ internal static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone, o
17191763
baseOffset = baseOffset + rule.BaseUtcOffsetDelta;
17201764
if (rule.HasDaylightSaving)
17211765
{
1722-
isDaylightSavings = GetIsDaylightSavingsFromUtc(time, year, zone._baseUtcOffset, rule, out isAmbiguousLocalDst, zone);
1766+
isDaylightSavings = GetIsDaylightSavingsFromUtc(time, year, zone._baseUtcOffset, rule, ruleIndex, out isAmbiguousLocalDst, zone);
17231767
baseOffset += (isDaylightSavings ? rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
17241768
}
17251769
}

0 commit comments

Comments
 (0)