@@ -18,48 +18,58 @@ public sealed partial class TimeZoneInfo
1818 {
1919 private const string DefaultTimeZoneDirectory = "/usr/share/zoneinfo/" ;
2020
21- // UTC aliases per https://github.com/unicode-org/cldr/blob/master/common/bcp47/timezone.xml
21+ // Set fallback values using abbreviations, base offset, and id
22+ // These are expected in environments without time zone globalization data
23+ private string ? _standardAbbrevName ;
24+ private string ? _daylightAbbrevName ;
25+
26+ // Handle UTC and its aliases per https://github.com/unicode-org/cldr/blob/master/common/bcp47/timezone.xml
2227 // Hard-coded because we need to treat all aliases of UTC the same even when globalization data is not available.
2328 // (This list is not likely to change.)
24- private static readonly string [ ] s_UtcAliases = new [ ] {
25- "Etc/UTC" ,
26- "Etc/UCT" ,
27- "Etc/Universal" ,
28- "Etc/Zulu" ,
29- "UCT" ,
30- "UTC" ,
31- "Universal" ,
32- "Zulu"
33- } ;
29+ private static bool IsUtcAlias ( string id )
30+ {
31+ switch ( ( ushort ) id [ 0 ] )
32+ {
33+ case 69 : // e
34+ case 101 : // E
35+ return string . Equals ( id , "Etc/UTC" , StringComparison . OrdinalIgnoreCase ) ||
36+ string . Equals ( id , "Etc/Universal" , StringComparison . OrdinalIgnoreCase ) ||
37+ string . Equals ( id , "Etc/UTC" , StringComparison . OrdinalIgnoreCase ) ||
38+ string . Equals ( id , "Etc/Zulu" , StringComparison . OrdinalIgnoreCase ) ;
39+ case 85 : // u
40+ case 117 : // U
41+ return string . Equals ( id , "UCT" , StringComparison . OrdinalIgnoreCase ) ||
42+ string . Equals ( id , "UTC" , StringComparison . OrdinalIgnoreCase ) ||
43+ string . Equals ( id , "Universal" , StringComparison . OrdinalIgnoreCase ) ;
44+ case 90 : // z
45+ case 122 : // Z
46+ return string . Equals ( id , "Zulu" , StringComparison . OrdinalIgnoreCase ) ;
47+ }
48+
49+ return false ;
50+ }
3451
3552 private TimeZoneInfo ( byte [ ] data , string id , bool dstDisabled )
3653 {
3754 _id = id ;
3855
3956 HasIanaId = true ;
4057
41- // Handle UTC and its aliases
42- if ( StringArrayContains ( _id , s_UtcAliases , StringComparison . OrdinalIgnoreCase ) )
58+ if ( IsUtcAlias ( id ) )
4359 {
44- _standardDisplayName = GetUtcStandardDisplayName ( ) ;
45- _daylightDisplayName = _standardDisplayName ;
46- _displayName = GetUtcFullDisplayName ( _id , _standardDisplayName ) ;
4760 _baseUtcOffset = TimeSpan . Zero ;
4861 _adjustmentRules = Array . Empty < AdjustmentRule > ( ) ;
4962 return ;
5063 }
5164
52- TZifHead t ;
5365 DateTime [ ] dts ;
5466 byte [ ] typeOfLocalTime ;
5567 TZifType [ ] transitionType ;
5668 string zoneAbbreviations ;
5769 string ? futureTransitionsPosixFormat ;
58- string ? standardAbbrevName = null ;
59- string ? daylightAbbrevName = null ;
6070
6171 // parse the raw TZif bytes; this method can throw ArgumentException when the data is malformed.
62- TZif_ParseRaw ( data , out t , out dts , out typeOfLocalTime , out transitionType , out zoneAbbreviations , out futureTransitionsPosixFormat ) ;
72+ TZif_ParseRaw ( data , out dts , out typeOfLocalTime , out transitionType , out zoneAbbreviations , out futureTransitionsPosixFormat ) ;
6373
6474 // find the best matching baseUtcOffset and display strings based on the current utcNow value.
6575 // NOTE: read the Standard and Daylight display strings from the tzfile now in case they can't be loaded later
@@ -71,11 +81,11 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
7181 if ( ! transitionType [ type ] . IsDst )
7282 {
7383 _baseUtcOffset = transitionType [ type ] . UtcOffset ;
74- standardAbbrevName = TZif_GetZoneAbbreviation ( zoneAbbreviations , transitionType [ type ] . AbbreviationIndex ) ;
84+ _standardAbbrevName = TZif_GetZoneAbbreviation ( zoneAbbreviations , transitionType [ type ] . AbbreviationIndex ) ;
7585 }
7686 else
7787 {
78- daylightAbbrevName = TZif_GetZoneAbbreviation ( zoneAbbreviations , transitionType [ type ] . AbbreviationIndex ) ;
88+ _daylightAbbrevName = TZif_GetZoneAbbreviation ( zoneAbbreviations , transitionType [ type ] . AbbreviationIndex ) ;
7989 }
8090 }
8191
@@ -88,24 +98,15 @@ private TimeZoneInfo(byte[] data, string id, bool dstDisabled)
8898 if ( ! transitionType [ i ] . IsDst )
8999 {
90100 _baseUtcOffset = transitionType [ i ] . UtcOffset ;
91- standardAbbrevName = TZif_GetZoneAbbreviation ( zoneAbbreviations , transitionType [ i ] . AbbreviationIndex ) ;
101+ _standardAbbrevName = TZif_GetZoneAbbreviation ( zoneAbbreviations , transitionType [ i ] . AbbreviationIndex ) ;
92102 }
93103 else
94104 {
95- daylightAbbrevName = TZif_GetZoneAbbreviation ( zoneAbbreviations , transitionType [ i ] . AbbreviationIndex ) ;
105+ _daylightAbbrevName = TZif_GetZoneAbbreviation ( zoneAbbreviations , transitionType [ i ] . AbbreviationIndex ) ;
96106 }
97107 }
98108 }
99109
100- // Set fallback values using abbreviations, base offset, and id
101- // These are expected in environments without time zone globalization data
102- _standardDisplayName = standardAbbrevName ;
103- _daylightDisplayName = daylightAbbrevName ?? standardAbbrevName ;
104- _displayName = string . Create ( null , stackalloc char [ 256 ] , $ "(UTC{ ( _baseUtcOffset >= TimeSpan . Zero ? '+' : '-' ) } { _baseUtcOffset : hh\\:mm} ) { _id } ") ;
105-
106- // Try to populate the display names from the globalization data
107- TryPopulateTimeZoneDisplayNamesFromGlobalizationData ( _id , _baseUtcOffset , ref _standardDisplayName , ref _daylightDisplayName , ref _displayName ) ;
108-
109110 // TZif supports seconds-level granularity with offsets but TimeZoneInfo only supports minutes since it aligns
110111 // with DateTimeOffset, SQL Server, and the W3C XML Specification
111112 if ( _baseUtcOffset . Ticks % TimeSpan . TicksPerMinute != 0 )
@@ -219,6 +220,50 @@ public AdjustmentRule[] GetAdjustmentRules()
219220 return rulesList . ToArray ( ) ;
220221 }
221222
223+ private string ? PopulateDisplayName ( )
224+ {
225+ if ( IsUtcAlias ( Id ) )
226+ return GetUtcFullDisplayName ( Id , StandardName ) ;
227+
228+ // Set fallback value using abbreviations, base offset, and id
229+ // These are expected in environments without time zone globalization data
230+ string ? displayName = string . Create ( null , stackalloc char [ 256 ] , $ "(UTC{ ( _baseUtcOffset >= TimeSpan . Zero ? '+' : '-' ) } { _baseUtcOffset : hh\\:mm} ) { _id } ") ;
231+ if ( GlobalizationMode . Invariant )
232+ return displayName ;
233+
234+ GetFullValueForDisplayNameField ( Id , BaseUtcOffset , ref displayName ) ;
235+
236+ return displayName ;
237+ }
238+
239+ private string ? PopulateStandardDisplayName ( )
240+ {
241+ if ( IsUtcAlias ( Id ) )
242+ return GetUtcStandardDisplayName ( ) ;
243+
244+ string ? standardDisplayName = _standardAbbrevName ;
245+ if ( GlobalizationMode . Invariant )
246+ return standardDisplayName ;
247+
248+ GetStandardDisplayName ( Id , ref standardDisplayName ) ;
249+
250+ return standardDisplayName ;
251+ }
252+
253+ private string ? PopulateDaylightDisplayName ( )
254+ {
255+ if ( IsUtcAlias ( Id ) )
256+ return StandardName ;
257+
258+ string ? daylightDisplayName = _daylightAbbrevName ?? _standardAbbrevName ;
259+ if ( GlobalizationMode . Invariant )
260+ return daylightDisplayName ;
261+
262+ GetDaylightDisplayName ( Id , ref daylightDisplayName ) ;
263+
264+ return daylightDisplayName ;
265+ }
266+
222267 private static void PopulateAllSystemTimeZones ( CachedData cachedData )
223268 {
224269 Debug . Assert ( Monitor . IsEntered ( cachedData ) ) ;
@@ -1065,15 +1110,15 @@ private static DateTime TZif_UnixTimeToDateTime(long unixTime) =>
10651110 unixTime > DateTimeOffset . UnixMaxSeconds ? DateTime . MaxValue :
10661111 DateTimeOffset . FromUnixTimeSeconds ( unixTime ) . UtcDateTime ;
10671112
1068- private static void TZif_ParseRaw ( byte [ ] data , out TZifHead t , out DateTime [ ] dts , out byte [ ] typeOfLocalTime , out TZifType [ ] transitionType ,
1113+ private static void TZif_ParseRaw ( byte [ ] data , out DateTime [ ] dts , out byte [ ] typeOfLocalTime , out TZifType [ ] transitionType ,
10691114 out string zoneAbbreviations , out string ? futureTransitionsPosixFormat )
10701115 {
10711116 futureTransitionsPosixFormat = null ;
10721117
10731118 // read in the 44-byte TZ header containing the count/length fields
10741119 //
10751120 int index = 0 ;
1076- t = new TZifHead ( data , index ) ;
1121+ TZifHead t = new TZifHead ( data , index ) ;
10771122 index += TZifHead . Length ;
10781123
10791124 int timeValuesLength = 4 ; // the first version uses 4-bytes to specify times
0 commit comments