Пример #1
0
 public void Contains_Instant_Normal()
 {
     Assert.IsTrue(SampleInterval.Contains(SampleStart));
     Assert.IsFalse(SampleInterval.Contains(SampleEnd));
     Assert.IsFalse(SampleInterval.Contains(Instant.MinValue));
     Assert.IsFalse(SampleInterval.Contains(Instant.MaxValue));
 }
Пример #2
0
 public void Contains_Instant_WholeOfTime_ViaSpecialInstants()
 {
     ZoneInterval interval = new ZoneInterval("All Time", Instant.BeforeMinValue, Instant.AfterMaxValue,
         Offset.FromHours(9), Offset.FromHours(1));
     Assert.IsTrue(interval.Contains(SampleStart));
     Assert.IsTrue(interval.Contains(Instant.MinValue));
     Assert.IsTrue(interval.Contains(Instant.MaxValue));
 }
Пример #3
0
 public void Contains_LocalInstant_WholeOfTime()
 {
     ZoneInterval interval = new ZoneInterval("All Time", Instant.BeforeMinValue, Instant.AfterMaxValue,
         Offset.FromHours(9), Offset.FromHours(1));
     Assert.IsTrue(interval.Contains(SampleStart.Plus(Offset.Zero)));
     Assert.IsTrue(interval.Contains(Instant.MinValue.Plus(Offset.Zero)));
     Assert.IsTrue(interval.Contains(Instant.MaxValue.Plus(Offset.Zero)));
 }
Пример #4
0
 public void Contains_Instant_WholeOfTime_ViaNullity()
 {
     ZoneInterval interval = new ZoneInterval("All Time", null, null,
         Offset.FromHours(9), Offset.FromHours(1));
     Assert.IsTrue(interval.Contains(SampleStart));
     Assert.IsTrue(interval.Contains(Instant.MinValue));
     Assert.IsTrue(interval.Contains(Instant.MaxValue));
 }
Пример #5
0
        public void Contains_Instant_WholeOfTime_ViaSpecialInstants()
        {
            ZoneInterval interval = new ZoneInterval("All Time", Instant.BeforeMinValue, Instant.AfterMaxValue,
                                                     Offset.FromHours(9), Offset.FromHours(1));

            Assert.IsTrue(interval.Contains(SampleStart));
            Assert.IsTrue(interval.Contains(Instant.MinValue));
            Assert.IsTrue(interval.Contains(Instant.MaxValue));
        }
Пример #6
0
        public void Contains_Instant_WholeOfTime_ViaNullity()
        {
            ZoneInterval interval = new ZoneInterval("All Time", null, null,
                                                     Offset.FromHours(9), Offset.FromHours(1));

            Assert.IsTrue(interval.Contains(SampleStart));
            Assert.IsTrue(interval.Contains(Instant.MinValue));
            Assert.IsTrue(interval.Contains(Instant.MaxValue));
        }
Пример #7
0
        public void Contains_LocalInstant_WholeOfTime()
        {
            ZoneInterval interval = new ZoneInterval("All Time", Instant.MinValue, Instant.MaxValue,
                                                     Offset.FromHours(9), Offset.FromHours(1));

            Assert.IsTrue(interval.Contains(SampleStart.Plus(Offset.Zero)));
            Assert.IsTrue(interval.Contains(LocalInstant.MinValue));
            Assert.IsTrue(interval.Contains(LocalInstant.MaxValue));
        }
Пример #8
0
        public void Contains_OutsideLocalInstantange()
        {
            ZoneInterval veryEarly = new ZoneInterval("Very early", Instant.BeforeMinValue, Instant.MinValue + Duration.FromHours(8), Offset.FromHours(-9), Offset.Zero);
            ZoneInterval veryLate  = new ZoneInterval("Very late", Instant.MaxValue - Duration.FromHours(8), Instant.AfterMaxValue, Offset.FromHours(9), Offset.Zero);

            // The instants are contained...
            Assert.IsTrue(veryEarly.Contains(Instant.MinValue + Duration.FromHours(4)));
            Assert.IsTrue(veryLate.Contains(Instant.MaxValue - Duration.FromHours(4)));
            // But there are no valid local instants
            Assert.IsFalse(veryEarly.Contains(Instant.MinValue.Plus(Offset.Zero)));
            Assert.IsFalse(veryLate.Contains(Instant.MaxValue.Plus(Offset.Zero)));
        }
Пример #9
0
        internal ZonedDateTime ResolveLocal(LocalDateTime localDateTime,
                                            [Trusted][NotNull] AmbiguousTimeResolver ambiguousResolver,
                                            [Trusted][NotNull] SkippedTimeResolver skippedResolver)
        {
            Preconditions.DebugCheckNotNull(ambiguousResolver, nameof(ambiguousResolver));
            Preconditions.DebugCheckNotNull(skippedResolver, nameof(skippedResolver));
            LocalInstant localInstant = localDateTime.ToLocalInstant();
            Instant      firstGuess   = localInstant.MinusZeroOffset();
            ZoneInterval interval     = GetZoneInterval(firstGuess);

            // Most of the time we'll go into here... the local instant and the instant
            // are close enough that we've found the right instant.
            if (interval.Contains(localInstant))
            {
                ZonedDateTime guessZoned = new ZonedDateTime(localDateTime.WithOffset(interval.WallOffset), this);
                ZoneInterval  earlier    = GetEarlierMatchingInterval(interval, localInstant);
                if (earlier != null)
                {
                    ZonedDateTime earlierZoned = new ZonedDateTime(localDateTime.WithOffset(earlier.WallOffset), this);
                    return(ambiguousResolver(earlierZoned, guessZoned));
                }
                ZoneInterval later = GetLaterMatchingInterval(interval, localInstant);
                if (later != null)
                {
                    ZonedDateTime laterZoned = new ZonedDateTime(localDateTime.WithOffset(later.WallOffset), this);
                    return(ambiguousResolver(guessZoned, laterZoned));
                }
                return(guessZoned);
            }
            else
            {
                // Our first guess was wrong. Either we need to change interval by one (either direction)
                // or we're in a gap.
                ZoneInterval earlier = GetEarlierMatchingInterval(interval, localInstant);
                if (earlier != null)
                {
                    return(new ZonedDateTime(localDateTime.WithOffset(earlier.WallOffset), this));
                }
                ZoneInterval later = GetLaterMatchingInterval(interval, localInstant);
                if (later != null)
                {
                    return(new ZonedDateTime(localDateTime.WithOffset(later.WallOffset), this));
                }
                return(skippedResolver(localDateTime, this, GetIntervalBeforeGap(localInstant), GetIntervalAfterGap(localInstant)));
            }
        }
Пример #10
0
        /// <summary>
        /// Returns the next interval after this one, if it contains the given local instant, or null otherwise.
        /// </summary>
        private ZoneInterval GetLaterMatchingInterval(ZoneInterval interval, LocalInstant localInstant)
        {
            // Micro-optimization to avoid fetching interval.End multiple times. Seems
            // to give a performance improvement on x86 at least...
            Instant intervalEnd = interval.End;

            if (intervalEnd == Instant.MaxValue)
            {
                return(null);
            }
            if (intervalEnd.Ticks + minOffsetTicks <= localInstant.Ticks)
            {
                ZoneInterval candidate = GetZoneInterval(intervalEnd);
                if (candidate.Contains(localInstant))
                {
                    return(candidate);
                }
            }
            return(null);
        }
Пример #11
0
        /// <summary>
        /// Returns the interval before this one, if it contains the given local instant, or null otherwise.
        /// </summary>
        private ZoneInterval?GetEarlierMatchingInterval(ZoneInterval interval, LocalInstant localInstant)
        {
            // Micro-optimization to avoid fetching interval.Start multiple times. Seems
            // to give a performance improvement on x86 at least...
            // If the zone interval extends to the start of time, the next check will definitely evaluate to false.
            Instant intervalStart = interval.RawStart;

            // This allows for a maxOffset of up to +1 day, and the "truncate towards beginning of time"
            // nature of the Days property.
            if (localInstant.DaysSinceEpoch <= intervalStart.DaysSinceEpoch + 1)
            {
                // We *could* do a more accurate check here based on the actual maxOffset, but it's probably
                // not worth it.
                ZoneInterval candidate = GetZoneInterval(intervalStart - Duration.Epsilon);
                if (candidate.Contains(localInstant))
                {
                    return(candidate);
                }
            }
            return(null);
        }
Пример #12
0
        /// <summary>
        /// Returns complete information about how the given <see cref="LocalDateTime" /> is mapped in this time zone.
        /// </summary>
        /// <remarks>
        /// <para>
        /// Mapping a local date/time to a time zone can give an unambiguous, ambiguous or impossible result, depending on
        /// time zone transitions. Use the return value of this method to handle these cases in an appropriate way for
        /// your use case.
        /// </para>
        /// <para>
        /// As an alternative, consider <see cref="ResolveLocal(LocalDateTime, ZoneLocalMappingResolver)"/>, which uses a caller-provided strategy to
        /// convert the <see cref="ZoneLocalMapping"/> returned here to a <see cref="ZonedDateTime"/>.
        /// </para>
        /// </remarks>
        /// <param name="localDateTime">The local date and time to map in this time zone.</param>
        /// <returns>A mapping of the given local date and time to zero, one or two zoned date/time values.</returns>
        public virtual ZoneLocalMapping MapLocal(LocalDateTime localDateTime)
        {
            LocalInstant localInstant = localDateTime.ToLocalInstant();
            Instant      firstGuess   = localInstant.MinusZeroOffset();
            ZoneInterval interval     = GetZoneInterval(firstGuess);

            // Most of the time we'll go into here... the local instant and the instant
            // are close enough that we've found the right instant.
            if (interval.Contains(localInstant))
            {
                ZoneInterval?earlier = GetEarlierMatchingInterval(interval, localInstant);
                if (earlier != null)
                {
                    return(new ZoneLocalMapping(this, localDateTime, earlier, interval, 2));
                }
                ZoneInterval?later = GetLaterMatchingInterval(interval, localInstant);
                if (later != null)
                {
                    return(new ZoneLocalMapping(this, localDateTime, interval, later, 2));
                }
                return(new ZoneLocalMapping(this, localDateTime, interval, interval, 1));
            }
            else
            {
                // Our first guess was wrong. Either we need to change interval by one (either direction)
                // or we're in a gap.
                ZoneInterval?earlier = GetEarlierMatchingInterval(interval, localInstant);
                if (earlier != null)
                {
                    return(new ZoneLocalMapping(this, localDateTime, earlier, earlier, 1));
                }
                ZoneInterval?later = GetLaterMatchingInterval(interval, localInstant);
                if (later != null)
                {
                    return(new ZoneLocalMapping(this, localDateTime, later, later, 1));
                }
                return(new ZoneLocalMapping(this, localDateTime, GetIntervalBeforeGap(localInstant), GetIntervalAfterGap(localInstant), 0));
            }
        }
Пример #13
0
        /// <summary>
        /// Finds all zone intervals for the given local instant. Usually there's one (i.e. only a single
        /// instant is mapped to the given local instant within the time zone) but during DST transitions
        /// there can be either 0 (the given local instant doesn't exist, e.g. local time skipped from 1am to
        /// 2am, but you gave us 1.30am) or 2 (the given local instant is ambiguous, e.g. local time skipped
        /// from 2am to 1am, but you gave us 1.30am).
        /// </summary>
        /// <remarks>
        /// This method is implemented in terms of GetZoneInterval(Instant) within DateTimeZone,
        /// and should work for any zone. However, internal derived classes may override this method
        /// for optimization purposes, e.g. if the zone interval is always ambiguous with
        /// a fixed value.
        /// </remarks>
        /// <param name="localInstant">The local instant to find matching zone intervals for</param>
        /// <returns>The struct containing up to two ZoneInterval references.</returns>
        internal virtual ZoneIntervalPair GetZoneIntervalPair(LocalInstant localInstant)
        {
            Instant      firstGuess = new Instant(localInstant.Ticks);
            ZoneInterval interval   = GetZoneInterval(firstGuess);

            // Most of the time we'll go into here... the local instant and the instant
            // are close enough that we've found the right instant.
            if (interval.Contains(localInstant))
            {
                ZoneInterval earlier = GetEarlierMatchingInterval(interval, localInstant);
                if (earlier != null)
                {
                    return(ZoneIntervalPair.Ambiguous(earlier, interval));
                }
                ZoneInterval later = GetLaterMatchingInterval(interval, localInstant);
                if (later != null)
                {
                    return(ZoneIntervalPair.Ambiguous(interval, later));
                }
                return(ZoneIntervalPair.Unambiguous(interval));
            }
            else
            {
                // Our first guess was wrong. Either we need to change interval by one (either direction)
                // or we're in a gap.
                ZoneInterval earlier = GetEarlierMatchingInterval(interval, localInstant);
                if (earlier != null)
                {
                    return(ZoneIntervalPair.Unambiguous(earlier));
                }
                ZoneInterval later = GetLaterMatchingInterval(interval, localInstant);
                if (later != null)
                {
                    return(ZoneIntervalPair.Unambiguous(later));
                }
                return(ZoneIntervalPair.NoMatch);
            }
        }
Пример #14
0
        /// <summary>
        /// Returns the next interval after this one, if it contains the given local instant, or null otherwise.
        /// </summary>
        private ZoneInterval?GetLaterMatchingInterval(ZoneInterval interval, LocalInstant localInstant)
        {
            // Micro-optimization to avoid fetching interval.End multiple times. Seems
            // to give a performance improvement on x86 at least...
            // If the zone interval extends to the end of time, the next check will
            // definitely evaluate to false.
            Instant intervalEnd = interval.RawEnd;

            // Crude but cheap first check to see whether there *might* be a later interval.
            // This allows for a minOffset of up to -1 day, and the "truncate towards beginning of time"
            // nature of the Days property.
            if (localInstant.DaysSinceEpoch >= intervalEnd.DaysSinceEpoch - 1)
            {
                // We *could* do a more accurate check here based on the actual maxOffset, but it's probably
                // not worth it.
                ZoneInterval candidate = GetZoneInterval(intervalEnd);
                if (candidate.Contains(localInstant))
                {
                    return(candidate);
                }
            }
            return(null);
        }
Пример #15
0
        /// <summary>
        /// Returns the interval before this one, if it contains the given local instant, or null otherwise.
        /// </summary>
        private ZoneInterval GetEarlierMatchingInterval(ZoneInterval interval, LocalInstant localInstant)
        {
            // Micro-optimization to avoid fetching interval.Start multiple times. Seems
            // to give a performance improvement on x86 at least...
            Instant intervalStart = interval.Start;

            if (intervalStart == Instant.MinValue)
            {
                return(null);
            }
            // If the tick before this interval started *could* map to a later local instant, let's
            // get the interval and check whether it actually includes the one we want.
            Instant endOfPrevious = intervalStart;

            if (endOfPrevious.Ticks + maxOffsetTicks > localInstant.Ticks)
            {
                ZoneInterval candidate = GetZoneInterval(endOfPrevious - Duration.Epsilon);
                if (candidate.Contains(localInstant))
                {
                    return(candidate);
                }
            }
            return(null);
        }
 /// <inheritdoc />
 /// <remarks>
 /// This returns either the zone interval before or after the transition, based on the instant provided.
 /// </remarks>
 public override ZoneInterval GetZoneInterval(Instant instant)
 {
     return(earlyInterval.Contains(instant) ? earlyInterval : lateInterval);
 }
Пример #17
0
 public void Contains_OutsideLocalInstantange()
 {
     ZoneInterval veryEarly = new ZoneInterval("Very early", Instant.BeforeMinValue, Instant.MinValue + Duration.FromHours(8), Offset.FromHours(-9), Offset.Zero);
     ZoneInterval veryLate = new ZoneInterval("Very late", Instant.MaxValue - Duration.FromHours(8), Instant.AfterMaxValue, Offset.FromHours(9), Offset.Zero);
     // The instants are contained...
     Assert.IsTrue(veryEarly.Contains(Instant.MinValue + Duration.FromHours(4)));
     Assert.IsTrue(veryLate.Contains(Instant.MaxValue - Duration.FromHours(4)));
     // But there are no valid local instants
     Assert.IsFalse(veryEarly.Contains(Instant.MinValue.Plus(Offset.Zero)));
     Assert.IsFalse(veryLate.Contains(Instant.MaxValue.Plus(Offset.Zero)));
 }