private ZoneInterval GetIntervalAfterGap(LocalInstant localInstant) { Instant guess = localInstant.MinusZeroOffset(); ZoneInterval guessInterval = GetZoneInterval(guess); // If the local interval occurs before the zone interval we're looking at starts, // it's the one we're looking for. Otherwise, we need to find the next interval. if (localInstant.Minus(guessInterval.WallOffset) < guessInterval.RawStart) { return(guessInterval); } else { // Will definitely be valid - there can't be a gap after an infinite interval. return(GetZoneInterval(guessInterval.End)); } }
private ZoneInterval GetIntervalBeforeGap(LocalInstant localInstant) { Instant guess = localInstant.MinusZeroOffset(); ZoneInterval guessInterval = GetZoneInterval(guess); // If the local interval occurs before the zone interval we're looking at starts, // we need to find the earlier one; otherwise this interval must come after the gap, and // it's therefore the one we want. if (localInstant.Minus(guessInterval.WallOffset) < guessInterval.RawStart) { return(GetZoneInterval(guessInterval.Start - Duration.Epsilon)); } else { return(guessInterval); } }
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))); } }
/// <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)); } }