/// <summary> /// Creates a new <see cref="BclDateTimeZone" /> from a <see cref="TimeZoneInfo"/> from the Base Class Library. /// </summary> /// <param name="bclZone">The original time zone to take information from.</param> /// <returns>A <see cref="BclDateTimeZone"/> wrapping the given <c>TimeZoneInfo</c>.</returns> public static BclDateTimeZone FromTimeZoneInfo(TimeZoneInfo bclZone) { Preconditions.CheckNotNull(bclZone, nameof(bclZone)); Offset standardOffset = bclZone.BaseUtcOffset.ToOffset(); var rules = bclZone.GetAdjustmentRules(); if (!bclZone.SupportsDaylightSavingTime || rules.Length == 0) { var fixedInterval = new ZoneInterval(bclZone.StandardName, Instant.BeforeMinValue, Instant.AfterMaxValue, standardOffset, Offset.Zero); return(new BclDateTimeZone(bclZone, standardOffset, standardOffset, new SingleZoneIntervalMap(fixedInterval))); } int windowsRules = rules.Count(IsWindowsRule); var ruleConverter = AreWindowsStyleRules(rules) ? rule => BclAdjustmentRule.FromWindowsAdjustmentRule(bclZone, rule) : (Converter <TimeZoneInfo.AdjustmentRule, BclAdjustmentRule>)(rule => BclAdjustmentRule.FromUnixAdjustmentRule(bclZone, rule)); BclAdjustmentRule[] convertedRules = Array.ConvertAll(rules, ruleConverter); Offset minRuleOffset = convertedRules.Aggregate(Offset.MaxValue, (min, rule) => Offset.Min(min, rule.Savings + rule.StandardOffset)); Offset maxRuleOffset = convertedRules.Aggregate(Offset.MinValue, (min, rule) => Offset.Max(min, rule.Savings + rule.StandardOffset)); IZoneIntervalMap uncachedMap = BuildMap(convertedRules, standardOffset, bclZone.StandardName); IZoneIntervalMap cachedMap = CachingZoneIntervalMap.CacheMap(uncachedMap); return(new BclDateTimeZone(bclZone, Offset.Min(standardOffset, minRuleOffset), Offset.Max(standardOffset, maxRuleOffset), cachedMap)); }
/// <summary> /// Creates a new <see cref="BclDateTimeZone" /> from a <see cref="TimeZoneInfo"/> from the Base Class Library. /// </summary> /// <param name="bclZone">The original time zone to take information from.</param> /// <returns>A <see cref="BclDateTimeZone"/> wrapping the given <c>TimeZoneInfo</c>.</returns> public static BclDateTimeZone FromTimeZoneInfo(TimeZoneInfo bclZone) { Preconditions.CheckNotNull(bclZone, nameof(bclZone)); Offset standardOffset = bclZone.BaseUtcOffset.ToOffset(); var rules = bclZone.GetAdjustmentRules(); if (!bclZone.SupportsDaylightSavingTime || rules.Length == 0) { var fixedInterval = new ZoneInterval(bclZone.StandardName, Instant.BeforeMinValue, Instant.AfterMaxValue, standardOffset, Offset.Zero); return(new BclDateTimeZone(bclZone, new SingleZoneIntervalMap(fixedInterval))); } BclAdjustmentRule[] convertedRules; if (AreWindowsStyleRules(rules)) { convertedRules = Array.ConvertAll(rules, rule => BclAdjustmentRule.FromWindowsAdjustmentRule(bclZone, rule)); } else { convertedRules = Array.ConvertAll(rules, rule => BclAdjustmentRule.FromUnixAdjustmentRule(bclZone, rule)); FixUnixTransitions(convertedRules); } IZoneIntervalMap uncachedMap = BuildMap(convertedRules, standardOffset, bclZone.StandardName); IZoneIntervalMap cachedMap = CachingZoneIntervalMap.CacheMap(uncachedMap); return(new BclDateTimeZone(bclZone, cachedMap)); }
private static IZoneIntervalMap BuildMap(BclAdjustmentRule[] rules, Offset standardOffset, [NotNull] string standardName) { Preconditions.CheckNotNull(standardName, nameof(standardName)); // First work out a naive list of partial maps. These will give the right offset at every instant, but not necessarily // correct intervals - we may we need to stitch intervals together. List<PartialZoneIntervalMap> maps = new List<PartialZoneIntervalMap>(); // Handle the start of time until the start of the first rule, if necessary. if (rules[0].Start.IsValid) { maps.Add(PartialZoneIntervalMap.ForZoneInterval(standardName, Instant.BeforeMinValue, rules[0].Start, standardOffset, Offset.Zero)); } for (int i = 0; i < rules.Length - 1; i++) { var beforeRule = rules[i]; var afterRule = rules[i + 1]; maps.Add(beforeRule.PartialMap); // If there's a gap between this rule and the next one, fill it with a fixed interval. if (beforeRule.End < afterRule.Start) { maps.Add(PartialZoneIntervalMap.ForZoneInterval(standardName, beforeRule.End, afterRule.Start, standardOffset, Offset.Zero)); } } var lastRule = rules[rules.Length - 1]; maps.Add(lastRule.PartialMap); // Handle the end of the last rule until the end of time, if necessary. if (lastRule.End.IsValid) { maps.Add(PartialZoneIntervalMap.ForZoneInterval(standardName, lastRule.End, Instant.AfterMaxValue, standardOffset, Offset.Zero)); } return PartialZoneIntervalMap.ConvertToFullMap(maps); }