public void MinMaxOffsetsWithOtherTailZone()
 {
     var tailZone = new FixedDateTimeZone("TestFixed", Offset.FromHours(8));
     var testZone = new PrecalculatedDateTimeZone("Test",
         new[] { FirstInterval, SecondInterval, ThirdInterval }, tailZone);
     Assert.AreEqual(Offset.FromHours(-5), testZone.MinOffset);
     Assert.AreEqual(Offset.FromHours(8), testZone.MaxOffset);
 }
 public void MinMaxOffsetsWithNullTailZone()
 {
     var testZone = new PrecalculatedDateTimeZone("Test",
         new[] { FirstInterval, SecondInterval, ThirdInterval,
                 new ZoneInterval("Last", ThirdInterval.End, Instant.AfterMaxValue, Offset.Zero, Offset.Zero) }, null);
     Assert.AreEqual(Offset.FromHours(-5), testZone.MinOffset);
     Assert.AreEqual(Offset.FromHours(4), testZone.MaxOffset);
 }
 public void MapLocal_GapAroundAndInTailZoneTransition()
 {
     // Tail zone is -10 / +5, with the transition occurring just after
     // the transition *to* the tail zone from the precalculated zone.
     // A local time of one hour after the transition from the precalculated zone (which is -5)
     // will therefore be in the gap.
     var tailZone = new SingleTransitionDateTimeZone(ThirdInterval.End + Duration.FromHours(1), -10, +5);
     var gapZone = new PrecalculatedDateTimeZone("Test",
         new[] { FirstInterval, SecondInterval, ThirdInterval }, tailZone);
     var mapping = gapZone.MapLocal(ThirdInterval.IsoLocalEnd.PlusHours(1));
     Assert.AreEqual(ThirdInterval, mapping.EarlyInterval);
     Assert.AreEqual(new ZoneInterval("Single-Early", ThirdInterval.End, tailZone.Transition, Offset.FromHours(-10), Offset.Zero),
                     mapping.LateInterval);
     Assert.AreEqual(0, mapping.Count);
 }
 public void GetZoneIntervals_NullTailZone_Eot()
 {
     ZoneInterval[] intervals =
     {
         new ZoneInterval("foo", Instant.BeforeMinValue, Instant.FromUnixTimeTicks(20), Offset.Zero, Offset.Zero),
         new ZoneInterval("foo", Instant.FromUnixTimeTicks(20), Instant.AfterMaxValue, Offset.Zero, Offset.Zero)
     };
     var zone = new PrecalculatedDateTimeZone("Test", intervals, null);
     Assert.AreEqual(intervals[1], zone.GetZoneInterval(Instant.MaxValue));
 }
 public void MapLocal_SingleIntervalAroundTailZoneTransition()
 {
     // Tail zone is fixed at +5. A local instant of one hour before the transition
     // from the precalculated zone (which is -5) will therefore give an instant from
     // the tail zone which occurs before the precalculated-to-tail transition,
     // and can therefore be ignored, resulting in an overall unambiguous time.
     var tailZone = new FixedDateTimeZone(Offset.FromHours(5));
     var gapZone = new PrecalculatedDateTimeZone("Test",
         new[] { FirstInterval, SecondInterval, ThirdInterval }, tailZone);
     var mapping = gapZone.MapLocal(ThirdInterval.IsoLocalEnd.PlusHours(-1));
     Assert.AreEqual(ThirdInterval, mapping.EarlyInterval);
     Assert.AreEqual(ThirdInterval, mapping.LateInterval);
     Assert.AreEqual(1, mapping.Count);
 }
 public void MapLocal_GapAroundTailZoneTransition()
 {
     // Tail zone is fixed at +5. A local time at the transition
     // from the precalculated zone (which is -5) will therefore give an instant from
     // the tail zone which occurs before the precalculated-to-tail transition,
     // and can therefore be ignored, resulting in an overall gap.
     var tailZone = new FixedDateTimeZone(Offset.FromHours(5));
     var gapZone = new PrecalculatedDateTimeZone("Test", 
         new[] { FirstInterval, SecondInterval, ThirdInterval }, tailZone);
     var mapping = gapZone.MapLocal(ThirdInterval.IsoLocalEnd);
     Assert.AreEqual(ThirdInterval, mapping.EarlyInterval);
     Assert.AreEqual(new ZoneInterval("UTC+05", ThirdInterval.End, Instant.AfterMaxValue, Offset.FromHours(5), Offset.Zero),
                     mapping.LateInterval);
     Assert.AreEqual(0, mapping.Count);
 }
 public void MapLocal_AmbiguousButTooEarlyInTailZoneTransition()
 {
     // Tail zone is +10 / +8, with the transition occurring just after
     // the transition *to* the tail zone from the precalculated zone.
     // A local instant of one hour before after the transition from the precalculated zone (which is -5)
     // will therefore be ambiguous, but the resulting instants from the ambiguity occur
     // before our transition into the tail zone, so are ignored.
     var tailZone = new SingleTransitionDateTimeZone(ThirdInterval.End + Duration.FromHours(1), 10, 8);
     var gapZone = new PrecalculatedDateTimeZone("Test",
         new[] { FirstInterval, SecondInterval, ThirdInterval }, tailZone);
     var mapping = gapZone.MapLocal(ThirdInterval.IsoLocalEnd.PlusHours(-1));
     Assert.AreEqual(ThirdInterval, mapping.EarlyInterval);
     Assert.AreEqual(ThirdInterval, mapping.LateInterval);
     Assert.AreEqual(1, mapping.Count);
 }
        /// <summary>
        /// Processes all the rules and builds a DateTimeZone.
        /// </summary>
        /// <param name="zoneId">Time zone ID to assign</param>
        public DateTimeZone ToDateTimeZone(String zoneId)
        {
            if (zoneId == null)
            {
                throw new ArgumentNullException("zoneId");
            }

            var transitions = new List<ZoneTransition>();
            DateTimeZone tailZone = null;
            Instant instant = Instant.MinValue;

            ZoneTransition nextTransition = null;
            int ruleSetCount = ruleSets.Count;
            for (int i = 0; i < ruleSetCount; i++)
            {
                var ruleSet = ruleSets[i];
                var transitionIterator = ruleSet.Iterator(instant);
                nextTransition = transitionIterator.First();
                if (nextTransition == null)
                {
                    continue;
                }
                AddTransition(transitions, nextTransition);

                while ((nextTransition = transitionIterator.Next()) != null)
                {
                    if (AddTransition(transitions, nextTransition))
                    {
                        if (tailZone != null)
                        {
                            // Got the extra transition before DaylightSavingsTimeZone.
                            nextTransition = transitionIterator.Next();
                            break;
                        }
                    }
                    if (tailZone == null && i == ruleSetCount - 1)
                    {
                        tailZone = transitionIterator.BuildTailZone(zoneId);
                        // If tailZone is not null, don't break out of main loop until at least one
                        // more transition is calculated. This ensures a correct 'seam' to the
                        // DaylightSavingsTimeZone.
                    }
                }

                instant = ruleSet.GetUpperLimit(transitionIterator.Savings);
            }

            // Check if a simpler zone implementation can be returned.
            if (transitions.Count == 0)
            {
                return tailZone ?? new FixedDateTimeZone(zoneId, Offset.Zero);
            }
            if (transitions.Count == 1 && tailZone == null)
            {
                var transition = transitions[0];
                return new FixedDateTimeZone(zoneId, transition.WallOffset);
            }
            var precalcedEnd = nextTransition != null ? nextTransition.Instant : Instant.MaxValue;
            var zone = new PrecalculatedDateTimeZone(zoneId, transitions, precalcedEnd, tailZone);
            return zone.IsCachable() ? CachedDateTimeZone.ForZone(zone) : zone;
        }
 public void GetZoneIntervals_GapAroundAndInTailZoneTransition()
 {
     // Tail zone is -10 / +5, with the transition occurring just after
     // the transition *to* the tail zone from the precalculated zone.
     // A local instant of one hour after the transition from the precalculated zone (which is -5)
     // will therefore be in the gap. No zone interval matches, so the result is
     // an empty pair.
     var tailZone = new SingleTransitionZone(ThirdInterval.End + Duration.FromHours(1), -10, +5);
     var gapZone = new PrecalculatedDateTimeZone("Test",
         new[] { FirstInterval, SecondInterval, ThirdInterval }, tailZone);
     var pair = gapZone.GetZoneIntervals(ThirdInterval.LocalEnd + Duration.FromHours(1));
     Assert.IsNull(pair.EarlyInterval);
     Assert.IsNull(pair.LateInterval);
 }
 public void GetZoneIntervals_GapAroundTailZoneTransition()
 {
     // Tail zone is fixed at +5. A local instant of one hour after the transition
     // from the precalculated zone (which is -5) will therefore give an instant from
     // the tail zone which occurs before the precalculated-to-tail transition,
     // and can therefore be ignored, resulting in an overall gap.
     var tailZone = new FixedDateTimeZone(Offset.FromHours(5));
     var gapZone = new PrecalculatedDateTimeZone("Test", 
         new[] { FirstInterval, SecondInterval, ThirdInterval }, tailZone);
     var actual = gapZone.GetZoneIntervals(ThirdInterval.LocalEnd);
     var expected = ZoneIntervalPair.NoMatch;
     Assert.AreEqual(expected, actual);
 }
 public void GetZoneIntervals_SingleIntervalAroundTailZoneTransition()
 {
     // Tail zone is fixed at +5. A local instant of one hour before the transition
     // from the precalculated zone (which is -5) will therefore give an instant from
     // the tail zone which occurs before the precalculated-to-tail transition,
     // and can therefore be ignored, resulting in an overall unambiguous time.
     var tailZone = new FixedDateTimeZone(Offset.FromHours(5));
     var gapZone = new PrecalculatedDateTimeZone("Test",
         new[] { FirstInterval, SecondInterval, ThirdInterval }, tailZone);
     var pair = gapZone.GetZoneIntervals(ThirdInterval.LocalEnd - Duration.FromHours(1));
     Assert.AreEqual(ThirdInterval, pair.EarlyInterval);
     Assert.IsNull(pair.LateInterval);
 }