Skip to content

Commit 8167e99

Browse files
authored
TimeZones improvement (#119662)
* TimeZones improvement * Address the feedback * Address David's feedback * Feedback addressing * Adjust one case calculation * Address more feedback
1 parent 83c947b commit 8167e99

File tree

7 files changed

+1571
-995
lines changed

7 files changed

+1571
-995
lines changed

src/libraries/System.Private.CoreLib/src/System.Private.CoreLib.Shared.projitems

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1341,6 +1341,7 @@
13411341
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZone.cs" />
13421342
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.AdjustmentRule.cs" />
13431343
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.cs" />
1344+
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.Cache.cs" />
13441345
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.FullGlobalizationData.cs" Condition="'$(UseMinimalGlobalizationData)' != 'true'" />
13451346
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.StringSerializer.cs" />
13461347
<Compile Include="$(MSBuildThisFileDirectory)System\TimeZoneInfo.TransitionTime.cs" />

src/libraries/System.Private.CoreLib/src/System/DateTime.cs

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1509,17 +1509,8 @@ public static DateTime Now
15091509
get
15101510
{
15111511
DateTime utc = UtcNow;
1512-
long offset = TimeZoneInfo.GetDateTimeNowUtcOffsetFromUtc(utc, out bool isAmbiguousLocalDst).Ticks;
1513-
long tick = utc.Ticks + offset;
1514-
if ((ulong)tick <= MaxTicks)
1515-
{
1516-
if (!isAmbiguousLocalDst)
1517-
{
1518-
return new DateTime((ulong)tick | KindLocal);
1519-
}
1520-
return new DateTime((ulong)tick | KindLocalAmbiguousDst);
1521-
}
1522-
return new DateTime(tick < 0 ? KindLocal : MaxTicks | KindLocal);
1512+
long localTicks = TimeZoneInfo.GetLocalDateTimeNowTicks(utc, out bool isAmbiguousLocalDst);
1513+
return new DateTime((ulong)localTicks | (isAmbiguousLocalDst ? KindLocalAmbiguousDst : KindLocal));
15231514
}
15241515
}
15251516

src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Cache.cs

Lines changed: 1421 additions & 0 deletions
Large diffs are not rendered by default.

src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs

Lines changed: 0 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -354,13 +354,6 @@ private static TimeZoneInfoResult TryGetTimeZoneFromLocalMachine(string id, out
354354
private static TimeZoneInfoResult TryGetTimeZone(string id, out TimeZoneInfo? timeZone, out Exception? e, CachedData cachedData)
355355
=> TryGetTimeZone(id, false, out timeZone, out e, cachedData, alwaysFallbackToLocalMachine: true);
356356

357-
// DateTime.Now fast path that avoids allocating an historically accurate TimeZoneInfo.Local and just creates a 1-year (current year) accurate time zone
358-
internal static TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out bool isAmbiguousLocalDst)
359-
{
360-
// Use the standard code path for Unix since there isn't a faster way of handling current-year-only time zones
361-
return GetUtcOffsetFromUtc(time, Local, out _, out isAmbiguousLocalDst);
362-
}
363-
364357
// TZFILE(5) BSD File Formats Manual TZFILE(5)
365358
//
366359
// NAME

src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Win32.cs

Lines changed: 1 addition & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -32,47 +32,6 @@ public sealed partial class TimeZoneInfo
3232

3333
private const string InvariantUtcStandardDisplayName = "Coordinated Universal Time";
3434

35-
private sealed partial class CachedData
36-
{
37-
private static TimeZoneInfo GetCurrentOneYearLocal()
38-
{
39-
// load the data from the OS
40-
uint result = Interop.Kernel32.GetTimeZoneInformation(out TIME_ZONE_INFORMATION timeZoneInformation);
41-
return result == Interop.Kernel32.TIME_ZONE_ID_INVALID ?
42-
CreateCustomTimeZone(LocalId, TimeSpan.Zero, LocalId, LocalId) :
43-
GetLocalTimeZoneFromWin32Data(timeZoneInformation, dstDisabled: false);
44-
}
45-
46-
private volatile OffsetAndRule? _oneYearLocalFromUtc;
47-
48-
public OffsetAndRule GetOneYearLocalFromUtc(int year)
49-
{
50-
OffsetAndRule? oneYearLocFromUtc = _oneYearLocalFromUtc;
51-
if (oneYearLocFromUtc == null || oneYearLocFromUtc.Year != year)
52-
{
53-
TimeZoneInfo currentYear = GetCurrentOneYearLocal();
54-
AdjustmentRule? rule = currentYear._adjustmentRules?[0];
55-
oneYearLocFromUtc = new OffsetAndRule(year, currentYear.BaseUtcOffset, rule);
56-
_oneYearLocalFromUtc = oneYearLocFromUtc;
57-
}
58-
return oneYearLocFromUtc;
59-
}
60-
}
61-
62-
private sealed class OffsetAndRule
63-
{
64-
public readonly int Year;
65-
public readonly TimeSpan Offset;
66-
public readonly AdjustmentRule? Rule;
67-
68-
public OffsetAndRule(int year, TimeSpan offset, AdjustmentRule? rule)
69-
{
70-
Year = year;
71-
Offset = offset;
72-
Rule = rule;
73-
}
74-
}
75-
7635
/// <summary>
7736
/// Returns a cloned array of AdjustmentRule objects
7837
/// </summary>
@@ -360,38 +319,10 @@ private static TimeZoneInfo GetLocalTimeZoneFromWin32Data(in TIME_ZONE_INFORMATI
360319
private static TimeZoneInfoResult TryGetTimeZone(string id, out TimeZoneInfo? timeZone, out Exception? e, CachedData cachedData)
361320
=> TryGetTimeZone(id, false, out timeZone, out e, cachedData);
362321

363-
// DateTime.Now fast path that avoids allocating an historically accurate TimeZoneInfo.Local and just creates a 1-year (current year) accurate time zone
364-
internal static TimeSpan GetDateTimeNowUtcOffsetFromUtc(DateTime time, out bool isAmbiguousLocalDst)
365-
{
366-
isAmbiguousLocalDst = false;
367-
368-
if (Invariant)
369-
{
370-
return TimeSpan.Zero;
371-
}
372-
373-
int timeYear = time.Year;
374-
375-
OffsetAndRule match = s_cachedData.GetOneYearLocalFromUtc(timeYear);
376-
TimeSpan baseOffset = match.Offset;
377-
378-
if (match.Rule != null)
379-
{
380-
baseOffset += match.Rule.BaseUtcOffsetDelta;
381-
if (match.Rule.HasDaylightSaving)
382-
{
383-
bool isDaylightSavings = GetIsDaylightSavingsFromUtc(time, timeYear, match.Offset, match.Rule, null, out isAmbiguousLocalDst, Local);
384-
baseOffset += (isDaylightSavings ? match.Rule.DaylightDelta : TimeSpan.Zero /* FUTURE: rule.StandardDelta */);
385-
}
386-
}
387-
388-
return baseOffset;
389-
}
390-
391322
/// <summary>
392323
/// Converts a REG_TZI_FORMAT struct to a TransitionTime
393324
/// - When the argument 'readStart' is true the corresponding daylightTransitionTimeStart field is read
394-
/// - When the argument 'readStart' is false the corresponding dayightTransitionTimeEnd field is read
325+
/// - When the argument 'readStart' is false the corresponding daylightTransitionTimeEnd field is read
395326
/// </summary>
396327
private static bool TransitionTimeFromTimeZoneInformation(in REG_TZI_FORMAT timeZoneInformation, out TransitionTime transitionTime, bool readStartDate)
397328
{

0 commit comments

Comments
 (0)