コード例 #1
0
        /// <summary>
        /// Returns the earliest valid <see cref="ZonedDateTime"/> with the given local date.
        /// </summary>
        /// <remarks>
        /// If midnight exists unambiguously on the given date, it is returned.
        /// If the given date has an ambiguous start time (e.g. the clocks go back from 1am to midnight)
        /// then the earlier ZonedDateTime is returned. If the given date has no midnight (e.g. the clocks
        /// go forward from midnight to 1am) then the earliest valid value is returned; this will be the instant
        /// of the transition.
        /// </remarks>
        /// <param name="date">The local date to map in this time zone.</param>
        /// <exception cref="SkippedTimeException">The entire day was skipped due to a very large time zone transition.
        /// (This is extremely rare.)</exception>
        /// <returns>The <see cref="ZonedDateTime"/> representing the earliest time in the given date, in this time zone.</returns>
        public ZonedDateTime AtStartOfDay(LocalDate date)
        {
            LocalDateTime midnight = date.AtMidnight();
            var           mapping  = MapLocal(midnight);

            switch (mapping.Count)
            {
            // Midnight doesn't exist. Maybe we just skip to 1am (or whatever), or maybe the whole day is missed.
            case 0:
                var interval = mapping.LateInterval;
                // Safe to use Start, as it can't extend to the start of time.
                var offsetDateTime = new OffsetDateTime(interval.Start, interval.WallOffset, date.Calendar);
                // It's possible that the entire day is skipped. For example, Samoa skipped December 30th 2011.
                // We know the two values are in the same calendar here, so we just need to check the YearMonthDay.
                if (offsetDateTime.YearMonthDay != date.YearMonthDay)
                {
                    throw new SkippedTimeException(midnight, this);
                }
                return(new ZonedDateTime(offsetDateTime, this));

            // Unambiguous or occurs twice, we can just use the offset from the earlier interval.
            case 1:
            case 2:
                return(new ZonedDateTime(midnight.WithOffset(mapping.EarlyInterval.WallOffset), this));

            default:
                throw new InvalidOperationException("This won't happen.");
            }
        }
コード例 #2
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)));
            }
        }