@@ -172,10 +172,11 @@ public TimeSpan[] GetAmbiguousTimeOffsets(DateTimeOffset dateTimeOffset)
172
172
DateTime adjustedTime = ConvertTime ( dateTimeOffset , this ) . DateTime ;
173
173
174
174
bool isAmbiguous = false ;
175
- AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets ( adjustedTime ) ;
175
+ int ? ruleIndex ;
176
+ AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets ( adjustedTime , out ruleIndex ) ;
176
177
if ( rule != null && rule . HasDaylightSaving )
177
178
{
178
- DaylightTimeStruct daylightTime = GetDaylightTime ( adjustedTime . Year , rule ) ;
179
+ DaylightTimeStruct daylightTime = GetDaylightTime ( adjustedTime . Year , rule , ruleIndex ) ;
179
180
isAmbiguous = GetIsAmbiguousTime ( adjustedTime , rule , daylightTime ) ;
180
181
}
181
182
@@ -232,10 +233,11 @@ public TimeSpan[] GetAmbiguousTimeOffsets(DateTime dateTime)
232
233
}
233
234
234
235
bool isAmbiguous = false ;
235
- AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets ( adjustedTime ) ;
236
+ int ? ruleIndex ;
237
+ AdjustmentRule rule = GetAdjustmentRuleForAmbiguousOffsets ( adjustedTime , out ruleIndex ) ;
236
238
if ( rule != null && rule . HasDaylightSaving )
237
239
{
238
- DaylightTimeStruct daylightTime = GetDaylightTime ( adjustedTime . Year , rule ) ;
240
+ DaylightTimeStruct daylightTime = GetDaylightTime ( adjustedTime . Year , rule , ruleIndex ) ;
239
241
isAmbiguous = GetIsAmbiguousTime ( adjustedTime , rule , daylightTime ) ;
240
242
}
241
243
@@ -263,15 +265,15 @@ public TimeSpan[] GetAmbiguousTimeOffsets(DateTime dateTime)
263
265
}
264
266
265
267
// note the time is already adjusted
266
- private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets ( DateTime adjustedTime )
268
+ private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets ( DateTime adjustedTime , out int ? ruleIndex )
267
269
{
268
- AdjustmentRule rule = GetAdjustmentRuleForTime ( adjustedTime ) ;
270
+ AdjustmentRule rule = GetAdjustmentRuleForTime ( adjustedTime , out ruleIndex ) ;
269
271
if ( rule != null && rule . NoDaylightTransitions && ! rule . HasDaylightSaving )
270
272
{
271
273
// When using NoDaylightTransitions rules, each rule is only for one offset.
272
274
// When looking for the Daylight savings rules, and we found the non-DST rule,
273
275
// then we get the rule right before this rule.
274
- return GetPreviousAdjustmentRule ( rule ) ;
276
+ return GetPreviousAdjustmentRule ( rule , ruleIndex ) ;
275
277
}
276
278
277
279
return rule ;
@@ -282,12 +284,23 @@ private AdjustmentRule GetAdjustmentRuleForAmbiguousOffsets(DateTime adjustedTim
282
284
/// If the specified rule is the first AdjustmentRule, or it isn't in _adjustmentRules,
283
285
/// then the specified rule is returned.
284
286
/// </summary>
285
- private AdjustmentRule GetPreviousAdjustmentRule ( AdjustmentRule rule )
287
+ private AdjustmentRule GetPreviousAdjustmentRule ( AdjustmentRule rule , int ? ruleIndex )
286
288
{
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
+
287
296
AdjustmentRule result = rule ;
288
297
for ( int i = 1 ; i < _adjustmentRules . Length ; i ++ )
289
298
{
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 ] ) )
291
304
{
292
305
result = _adjustmentRules [ i - 1 ] ;
293
306
break ;
@@ -407,10 +420,11 @@ internal bool IsAmbiguousTime(DateTime dateTime, TimeZoneInfoOptions flags)
407
420
dateTime . Kind == DateTimeKind . Utc ? ConvertTime ( dateTime , s_utcTimeZone , this , flags , cachedData ) :
408
421
dateTime ;
409
422
410
- AdjustmentRule rule = GetAdjustmentRuleForTime ( adjustedTime ) ;
423
+ int ? ruleIndex ;
424
+ AdjustmentRule rule = GetAdjustmentRuleForTime ( adjustedTime , out ruleIndex ) ;
411
425
if ( rule != null && rule . HasDaylightSaving )
412
426
{
413
- DaylightTimeStruct daylightTime = GetDaylightTime ( adjustedTime . Year , rule ) ;
427
+ DaylightTimeStruct daylightTime = GetDaylightTime ( adjustedTime . Year , rule , ruleIndex ) ;
414
428
return GetIsAmbiguousTime ( adjustedTime , rule , daylightTime ) ;
415
429
}
416
430
return false ;
@@ -492,10 +506,11 @@ private bool IsDaylightSavingTime(DateTime dateTime, TimeZoneInfoOptions flags,
492
506
//
493
507
// handle the normal cases...
494
508
//
495
- AdjustmentRule rule = GetAdjustmentRuleForTime ( adjustedTime ) ;
509
+ int ? ruleIndex ;
510
+ AdjustmentRule rule = GetAdjustmentRuleForTime ( adjustedTime , out ruleIndex ) ;
496
511
if ( rule != null && rule . HasDaylightSaving )
497
512
{
498
- DaylightTimeStruct daylightTime = GetDaylightTime ( adjustedTime . Year , rule ) ;
513
+ DaylightTimeStruct daylightTime = GetDaylightTime ( adjustedTime . Year , rule , ruleIndex ) ;
499
514
return GetIsDaylightSavings ( adjustedTime , rule , daylightTime , flags ) ;
500
515
}
501
516
else
@@ -515,11 +530,12 @@ public bool IsInvalidTime(DateTime dateTime)
515
530
( dateTime . Kind == DateTimeKind . Local && s_cachedData . GetCorrespondingKind ( this ) == DateTimeKind . Local ) )
516
531
{
517
532
// 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 ) ;
519
535
520
536
if ( rule != null && rule . HasDaylightSaving )
521
537
{
522
- DaylightTimeStruct daylightTime = GetDaylightTime ( dateTime . Year , rule ) ;
538
+ DaylightTimeStruct daylightTime = GetDaylightTime ( dateTime . Year , rule , ruleIndex ) ;
523
539
isInvalid = GetIsInvalidTime ( dateTime , rule , daylightTime ) ;
524
540
}
525
541
else
@@ -661,7 +677,8 @@ private static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZo
661
677
// performance for the normal case at the expense of the 'ArgumentException'
662
678
// case and Loss-less Local special cases.
663
679
//
664
- AdjustmentRule sourceRule = sourceTimeZone . GetAdjustmentRuleForTime ( dateTime ) ;
680
+ int ? sourceRuleIndex ;
681
+ AdjustmentRule sourceRule = sourceTimeZone . GetAdjustmentRuleForTime ( dateTime , out sourceRuleIndex ) ;
665
682
TimeSpan sourceOffset = sourceTimeZone . BaseUtcOffset ;
666
683
667
684
if ( sourceRule != null )
@@ -670,7 +687,7 @@ private static DateTime ConvertTime(DateTime dateTime, TimeZoneInfo sourceTimeZo
670
687
if ( sourceRule . HasDaylightSaving )
671
688
{
672
689
bool sourceIsDaylightSavings = false ;
673
- DaylightTimeStruct sourceDaylightTime = sourceTimeZone . GetDaylightTime ( dateTime . Year , sourceRule ) ;
690
+ DaylightTimeStruct sourceDaylightTime = sourceTimeZone . GetDaylightTime ( dateTime . Year , sourceRule , sourceRuleIndex ) ;
674
691
675
692
// 'dateTime' might be in an invalid time range since it is in an AdjustmentRule
676
693
// period that supports DST
@@ -1048,10 +1065,19 @@ private TimeZoneInfo(SerializationInfo info, StreamingContext context)
1048
1065
_supportsDaylightSavingTime = ( bool ) info . GetValue ( "SupportsDaylightSavingTime" , typeof ( bool ) ) ;
1049
1066
}
1050
1067
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 )
1052
1077
{
1053
1078
if ( _adjustmentRules == null || _adjustmentRules . Length == 0 )
1054
1079
{
1080
+ ruleIndex = null ;
1055
1081
return null ;
1056
1082
}
1057
1083
@@ -1076,6 +1102,7 @@ private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTime
1076
1102
int compareResult = CompareAdjustmentRuleToDateTime ( rule , previousRule , dateTime , date , dateTimeisUtc ) ;
1077
1103
if ( compareResult == 0 )
1078
1104
{
1105
+ ruleIndex = median ;
1079
1106
return rule ;
1080
1107
}
1081
1108
else if ( compareResult < 0 )
@@ -1088,6 +1115,7 @@ private AdjustmentRule GetAdjustmentRuleForTime(DateTime dateTime, bool dateTime
1088
1115
}
1089
1116
}
1090
1117
1118
+ ruleIndex = null ;
1091
1119
return null ;
1092
1120
}
1093
1121
@@ -1199,7 +1227,7 @@ private static DateTime ConvertUtcToTimeZone(long ticks, TimeZoneInfo destinatio
1199
1227
/// <summary>
1200
1228
/// Helper function that returns a DaylightTime from a year and AdjustmentRule.
1201
1229
/// </summary>
1202
- private DaylightTimeStruct GetDaylightTime ( int year , AdjustmentRule rule )
1230
+ private DaylightTimeStruct GetDaylightTime ( int year , AdjustmentRule rule , int ? ruleIndex )
1203
1231
{
1204
1232
TimeSpan delta = rule . DaylightDelta ;
1205
1233
DateTime startTime ;
@@ -1211,7 +1239,7 @@ private DaylightTimeStruct GetDaylightTime(int year, AdjustmentRule rule)
1211
1239
// Convert the UTC times into adjusted time zone times.
1212
1240
1213
1241
// 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 ) ;
1215
1243
startTime = ConvertFromUtc ( rule . DateStart , previousRule . DaylightDelta , previousRule . BaseUtcOffsetDelta ) ;
1216
1244
1217
1245
endTime = ConvertFromUtc ( rule . DateEnd , rule . DaylightDelta , rule . BaseUtcOffsetDelta ) ;
@@ -1301,12 +1329,12 @@ private static bool GetIsDaylightSavings(DateTime time, AdjustmentRule rule, Day
1301
1329
/// <summary>
1302
1330
/// Gets the offset that should be used to calculate DST start times from a UTC time.
1303
1331
/// </summary>
1304
- private TimeSpan GetDaylightSavingsStartOffsetFromUtc ( TimeSpan baseUtcOffset , AdjustmentRule rule )
1332
+ private TimeSpan GetDaylightSavingsStartOffsetFromUtc ( TimeSpan baseUtcOffset , AdjustmentRule rule , int ? ruleIndex )
1305
1333
{
1306
1334
if ( rule . NoDaylightTransitions )
1307
1335
{
1308
1336
// 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 ) ;
1310
1338
return baseUtcOffset + previousRule . BaseUtcOffsetDelta + previousRule . DaylightDelta ;
1311
1339
}
1312
1340
else
@@ -1328,7 +1356,7 @@ private TimeSpan GetDaylightSavingsEndOffsetFromUtc(TimeSpan baseUtcOffset, Adju
1328
1356
/// Helper function that checks if a given dateTime is in Daylight Saving Time (DST).
1329
1357
/// This function assumes the dateTime is in UTC and AdjustmentRule is in a different time zone.
1330
1358
/// </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 )
1332
1360
{
1333
1361
isAmbiguousLocalDst = false ;
1334
1362
@@ -1338,7 +1366,7 @@ private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpa
1338
1366
}
1339
1367
1340
1368
// 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 ) ;
1342
1370
1343
1371
// The start and end times represent the range of universal times that are in DST for that year.
1344
1372
// 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
1352
1380
// Note we handle the similar case when rule year start with daylight saving and previous year end with daylight saving.
1353
1381
1354
1382
bool ignoreYearAdjustment = false ;
1355
- TimeSpan dstStartOffset = zone . GetDaylightSavingsStartOffsetFromUtc ( utc , rule ) ;
1383
+ TimeSpan dstStartOffset = zone . GetDaylightSavingsStartOffsetFromUtc ( utc , rule , ruleIndex ) ;
1356
1384
DateTime startTime ;
1357
1385
if ( rule . IsStartDateMarkerForBeginningOfYear ( ) && daylightTime . Start . Year > DateTime . MinValue . Year )
1358
1386
{
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 ) ;
1360
1391
if ( previousYearRule != null && previousYearRule . IsEndDateMarkerForEndOfYear ( ) )
1361
1392
{
1362
- DaylightTimeStruct previousDaylightTime = zone . GetDaylightTime ( daylightTime . Start . Year - 1 , previousYearRule ) ;
1393
+ DaylightTimeStruct previousDaylightTime = zone . GetDaylightTime (
1394
+ daylightTime . Start . Year - 1 ,
1395
+ previousYearRule ,
1396
+ previousYearRuleIndex ) ;
1363
1397
startTime = previousDaylightTime . Start - utc - previousYearRule . BaseUtcOffsetDelta ;
1364
1398
ignoreYearAdjustment = true ;
1365
1399
}
@@ -1377,7 +1411,10 @@ private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpa
1377
1411
DateTime endTime ;
1378
1412
if ( rule . IsEndDateMarkerForEndOfYear ( ) && daylightTime . End . Year < DateTime . MaxValue . Year )
1379
1413
{
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 ) ;
1381
1418
if ( nextYearRule != null && nextYearRule . IsStartDateMarkerForBeginningOfYear ( ) )
1382
1419
{
1383
1420
if ( nextYearRule . IsEndDateMarkerForEndOfYear ( ) )
@@ -1387,7 +1424,10 @@ private static bool GetIsDaylightSavingsFromUtc(DateTime time, int year, TimeSpa
1387
1424
}
1388
1425
else
1389
1426
{
1390
- DaylightTimeStruct nextdaylightTime = zone . GetDaylightTime ( daylightTime . End . Year + 1 , nextYearRule ) ;
1427
+ DaylightTimeStruct nextdaylightTime = zone . GetDaylightTime (
1428
+ daylightTime . End . Year + 1 ,
1429
+ nextYearRule ,
1430
+ nextYearRuleIndex ) ;
1391
1431
endTime = nextdaylightTime . End - utc - nextYearRule . BaseUtcOffsetDelta - nextYearRule . DaylightDelta ;
1392
1432
}
1393
1433
ignoreYearAdjustment = true ;
@@ -1644,14 +1684,15 @@ private static bool GetIsInvalidTime(DateTime time, AdjustmentRule rule, Dayligh
1644
1684
private static TimeSpan GetUtcOffset ( DateTime time , TimeZoneInfo zone , TimeZoneInfoOptions flags )
1645
1685
{
1646
1686
TimeSpan baseOffset = zone . BaseUtcOffset ;
1647
- AdjustmentRule rule = zone . GetAdjustmentRuleForTime ( time ) ;
1687
+ int ? ruleIndex ;
1688
+ AdjustmentRule rule = zone . GetAdjustmentRuleForTime ( time , out ruleIndex ) ;
1648
1689
1649
1690
if ( rule != null )
1650
1691
{
1651
1692
baseOffset = baseOffset + rule . BaseUtcOffsetDelta ;
1652
1693
if ( rule . HasDaylightSaving )
1653
1694
{
1654
- DaylightTimeStruct daylightTime = zone . GetDaylightTime ( time . Year , rule ) ;
1695
+ DaylightTimeStruct daylightTime = zone . GetDaylightTime ( time . Year , rule , ruleIndex ) ;
1655
1696
bool isDaylightSavings = GetIsDaylightSavings ( time , rule , daylightTime , flags ) ;
1656
1697
baseOffset += ( isDaylightSavings ? rule . DaylightDelta : TimeSpan . Zero /* FUTURE: rule.StandardDelta */ ) ;
1657
1698
}
@@ -1690,21 +1731,24 @@ internal static TimeSpan GetUtcOffsetFromUtc(DateTime time, TimeZoneInfo zone, o
1690
1731
isAmbiguousLocalDst = false ;
1691
1732
TimeSpan baseOffset = zone . BaseUtcOffset ;
1692
1733
int year ;
1734
+ int ? ruleIndex ;
1693
1735
AdjustmentRule rule ;
1694
1736
1695
1737
if ( time > s_maxDateOnly )
1696
1738
{
1697
- rule = zone . GetAdjustmentRuleForTime ( DateTime . MaxValue ) ;
1739
+ rule = zone . GetAdjustmentRuleForTime ( DateTime . MaxValue , out ruleIndex ) ;
1698
1740
year = 9999 ;
1699
1741
}
1700
1742
else if ( time < s_minDateOnly )
1701
1743
{
1702
- rule = zone . GetAdjustmentRuleForTime ( DateTime . MinValue ) ;
1744
+ rule = zone . GetAdjustmentRuleForTime ( DateTime . MinValue , out ruleIndex ) ;
1703
1745
year = 1 ;
1704
1746
}
1705
1747
else
1706
1748
{
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." ) ;
1708
1752
1709
1753
// As we get the associated rule using the adjusted targetTime, we should use the adjusted year (targetTime.Year) too as after adding the baseOffset,
1710
1754
// 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
1719
1763
baseOffset = baseOffset + rule . BaseUtcOffsetDelta ;
1720
1764
if ( rule . HasDaylightSaving )
1721
1765
{
1722
- isDaylightSavings = GetIsDaylightSavingsFromUtc ( time , year , zone . _baseUtcOffset , rule , out isAmbiguousLocalDst , zone ) ;
1766
+ isDaylightSavings = GetIsDaylightSavingsFromUtc ( time , year , zone . _baseUtcOffset , rule , ruleIndex , out isAmbiguousLocalDst , zone ) ;
1723
1767
baseOffset += ( isDaylightSavings ? rule . DaylightDelta : TimeSpan . Zero /* FUTURE: rule.StandardDelta */ ) ;
1724
1768
}
1725
1769
}
0 commit comments