From 4753b0bb554156b69117d53284f407a98e1d8e58 Mon Sep 17 00:00:00 2001 From: Krzysztof Wicher Date: Thu, 7 Jun 2018 15:55:54 -0700 Subject: [PATCH] Skip old time zones adjustments with offsets higher than 14h (dotnet/coreclr#18305) * Skip old time zones with offsets higher than 14h * apply offline feedback * fix braces Commit migrated from https://github.com/dotnet/coreclr/commit/ba12331656a1f80ef11acf53c24d18d3e5c66a9d --- .../src/System/TimeZoneInfo.Unix.cs | 28 ++++++- .../src/System/TimeZoneInfo.cs | 89 +++++++++++++++++++--- 2 files changed, 104 insertions(+), 13 deletions(-) diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs index a91e602..3e403fb 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.Unix.cs @@ -595,6 +595,7 @@ namespace System } catch (ArgumentException) { } catch (InvalidTimeZoneException) { } + try { return new TimeZoneInfo(rawData, id, dstDisabled: true); // create a TimeZoneInfo instance from the TZif data w/o DST support @@ -602,7 +603,6 @@ namespace System catch (ArgumentException) { } catch (InvalidTimeZoneException) { } } - return null; } @@ -866,7 +866,7 @@ namespace System index++; } - if (index == 0) + if (rulesList.Count == 0 && index < dts.Length) { TZifType transitionType = TZif_GetEarlyDateTransitionType(transitionTypes); DateTime endTransitionDate = dts[index]; @@ -883,6 +883,12 @@ namespace System default(TransitionTime), baseUtcDelta, noDaylightTransitions: true); + + if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r)) + { + NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r); + } + rulesList.Add(r); } else if (index < dts.Length) @@ -920,6 +926,12 @@ namespace System default(TransitionTime), baseUtcDelta, noDaylightTransitions: true); + + if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r)) + { + NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r); + } + rulesList.Add(r); } else @@ -932,8 +944,14 @@ namespace System if (!string.IsNullOrEmpty(futureTransitionsPosixFormat)) { AdjustmentRule r = TZif_CreateAdjustmentRuleForPosixFormat(futureTransitionsPosixFormat, startTransitionDate, timeZoneBaseUtcOffset); + if (r != null) { + if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r)) + { + NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r); + } + rulesList.Add(r); } } @@ -954,6 +972,12 @@ namespace System default(TransitionTime), baseUtcDelta, noDaylightTransitions: true); + + if (!IsValidAdjustmentRuleOffest(timeZoneBaseUtcOffset, r)) + { + NormalizeAdjustmentRuleOffset(timeZoneBaseUtcOffset, ref r); + } + rulesList.Add(r); } } diff --git a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs index 6e27376..e95321d 100644 --- a/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs +++ b/src/libraries/System.Private.CoreLib/src/System/TimeZoneInfo.cs @@ -1918,12 +1918,6 @@ namespace System } /// - /// Helper function that validates the TimeSpan is within +/- 14.0 hours - /// - internal static bool UtcOffsetOutOfRange(TimeSpan offset) => - offset.TotalHours < -14.0 || offset.TotalHours > 14.0; - - /// /// Helper function that performs all of the validation checks for the /// factory methods and deserialization callback. /// @@ -1972,11 +1966,7 @@ namespace System throw new InvalidTimeZoneException(SR.Argument_AdjustmentRulesNoNulls); } - // FUTURE: check to see if this rule supports Daylight Saving Time - // adjustmentRulesSupportDst = adjustmentRulesSupportDst || current.SupportsDaylightSavingTime; - // FUTURE: test baseUtcOffset + current.StandardDelta - - if (UtcOffsetOutOfRange(baseUtcOffset + current.DaylightDelta)) + if (!IsValidAdjustmentRuleOffest(baseUtcOffset, current)) { throw new InvalidTimeZoneException(SR.ArgumentOutOfRange_UtcOffsetAndDaylightDelta); } @@ -1989,5 +1979,82 @@ namespace System } } } + + private static readonly TimeSpan MaxOffset = TimeSpan.FromHours(14.0); + private static readonly TimeSpan MinOffset = -MaxOffset; + + /// + /// Helper function that validates the TimeSpan is within +/- 14.0 hours + /// + internal static bool UtcOffsetOutOfRange(TimeSpan offset) => + offset < MinOffset || offset > MaxOffset; + + private static TimeSpan GetUtcOffset(TimeSpan baseUtcOffset, AdjustmentRule adjustmentRule) + { + return baseUtcOffset + + adjustmentRule.BaseUtcOffsetDelta + + (adjustmentRule.HasDaylightSaving ? adjustmentRule.DaylightDelta : TimeSpan.Zero); + } + + /// + /// Helper function that performs adjustment rule validation + /// + private static bool IsValidAdjustmentRuleOffest(TimeSpan baseUtcOffset, AdjustmentRule adjustmentRule) + { + TimeSpan utcOffset = GetUtcOffset(baseUtcOffset, adjustmentRule); + return !UtcOffsetOutOfRange(utcOffset); + } + + /// + /// Normalize adjustment rule offset so that it is within valid range + /// This method should not be called at all but is here in case something changes in the future + /// or if really old time zones are present on the OS (no combination is known at the moment) + /// + private static void NormalizeAdjustmentRuleOffset(TimeSpan baseUtcOffset, ref AdjustmentRule adjustmentRule) + { + // Certain time zones such as: + // Time Zone start date end date offset + // ----------------------------------------------------- + // America/Yakutat 0001-01-01 1867-10-18 14:41:00 + // America/Yakutat 1867-10-18 1900-08-20 14:41:00 + // America/Sitka 0001-01-01 1867-10-18 14:58:00 + // America/Sitka 1867-10-18 1900-08-20 14:58:00 + // Asia/Manila 0001-01-01 1844-12-31 -15:56:00 + // Pacific/Guam 0001-01-01 1845-01-01 -14:21:00 + // Pacific/Saipan 0001-01-01 1845-01-01 -14:21:00 + // + // have larger offset than currently supported by framework. + // If for whatever reason we find that time zone exceeding max + // offset of 14h this function will truncate it to the max valid offset. + // Updating max offset may cause problems with interacting with SQL server + // which uses SQL DATETIMEOFFSET field type which was originally designed to be + // bit-for-bit compatible with DateTimeOffset. + + TimeSpan utcOffset = GetUtcOffset(baseUtcOffset, adjustmentRule); + + // utc base offset delta increment + TimeSpan adjustment = TimeSpan.Zero; + + if (utcOffset > MaxOffset) + { + adjustment = MaxOffset - utcOffset; + } + else if (utcOffset < MinOffset) + { + adjustment = MinOffset - utcOffset; + } + + if (adjustment != TimeSpan.Zero) + { + adjustmentRule = AdjustmentRule.CreateAdjustmentRule( + adjustmentRule.DateStart, + adjustmentRule.DateEnd, + adjustmentRule.DaylightDelta, + adjustmentRule.DaylightTransitionStart, + adjustmentRule.DaylightTransitionEnd, + adjustmentRule.BaseUtcOffsetDelta + adjustment, + adjustmentRule.NoDaylightTransitions); + } + } } } -- 2.7.4