public void CreateResolver_Skipped() { ZonedDateTime zoned = new ZonedDateTime(TimeInTransition.PlusDays(1).WithOffset(GapZone.EarlyInterval.WallOffset), GapZone); AmbiguousTimeResolver ambiguityResolver = (earlier, later) => { Assert.Fail("Shouldn't be called"); return(default(ZonedDateTime)); }; SkippedTimeResolver skippedTimeResolver = (local, zone, before, after) => zoned; var resolver = Resolvers.CreateMappingResolver(ambiguityResolver, skippedTimeResolver); var resolved = resolver(GapZone.MapLocal(TimeInTransition)); Assert.AreEqual(zoned, resolved); }
public void CreateResolver_Unambiguous() { AmbiguousTimeResolver ambiguityResolver = (earlier, later) => { Assert.Fail("Shouldn't be called"); return(default(ZonedDateTime)); }; SkippedTimeResolver skippedTimeResolver = (local, zone, before, after) => { Assert.Fail("Shouldn't be called"); return(default(ZonedDateTime)); }; var resolver = Resolvers.CreateMappingResolver(ambiguityResolver, skippedTimeResolver); LocalDateTime localTime = new LocalDateTime(1900, 1, 1, 0, 0); var resolved = resolver(GapZone.MapLocal(localTime)); Assert.AreEqual(new ZonedDateTime(localTime.WithOffset(GapZone.EarlyInterval.WallOffset), GapZone), resolved); }
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> /// Combines an <see cref="AmbiguousTimeResolver"/> and a <see cref="SkippedTimeResolver"/> to create a /// <see cref="ZoneLocalMappingResolver"/>. /// </summary> /// <remarks> /// The <c>ZoneLocalMappingResolver</c> created by this method operates in the obvious way: unambiguous mappings /// are returned directly, ambiguous mappings are delegated to the given <c>AmbiguousTimeResolver</c>, and /// "skipped" mappings are delegated to the given <c>SkippedTimeResolver</c>. /// </remarks> /// <param name="ambiguousTimeResolver">Resolver to use for ambiguous mappings.</param> /// <param name="skippedTimeResolver">Resolver to use for "skipped" mappings.</param> /// <returns>The logical combination of the two resolvers.</returns> public static ZoneLocalMappingResolver CreateMappingResolver(AmbiguousTimeResolver ambiguousTimeResolver, SkippedTimeResolver skippedTimeResolver) { Preconditions.CheckNotNull(ambiguousTimeResolver, nameof(ambiguousTimeResolver)); Preconditions.CheckNotNull(skippedTimeResolver, nameof(skippedTimeResolver)); return(mapping => Preconditions.CheckNotNull(mapping, nameof(mapping)).Count switch { 0 => skippedTimeResolver(mapping.LocalDateTime, mapping.Zone, mapping.EarlyInterval, mapping.LateInterval), 1 => mapping.First(), 2 => ambiguousTimeResolver(mapping.First(), mapping.Last()), _ => throw new InvalidOperationException("Mapping has count outside range 0-2; should not happen.") });
/// <summary> /// Combines an <see cref="AmbiguousTimeResolver"/> and a <see cref="SkippedTimeResolver"/> to create a /// <see cref="ZoneLocalMappingResolver"/>. /// </summary> /// <remarks> /// The <c>ZoneLocalMappingResolver</c> created by this method operates in the obvious way: unambiguous mappings /// are returned directly, ambiguous mappings are delegated to the given <c>AmbiguousTimeResolver</c>, and /// "skipped" mappings are delegated to the given <c>SkippedTimeResolver</c>. /// </remarks> /// <param name="ambiguousTimeResolver">Resolver to use for ambiguous mappings.</param> /// <param name="skippedTimeResolver">Resolver to use for "skipped" mappings.</param> /// <returns>The logical combination of the two resolvers.</returns> public static ZoneLocalMappingResolver CreateMappingResolver([NotNull] AmbiguousTimeResolver ambiguousTimeResolver, [NotNull] SkippedTimeResolver skippedTimeResolver) { Preconditions.CheckNotNull(ambiguousTimeResolver, nameof(ambiguousTimeResolver)); Preconditions.CheckNotNull(skippedTimeResolver, nameof(skippedTimeResolver)); return(mapping => { Preconditions.CheckNotNull(mapping, nameof(mapping)); switch (mapping.Count) { case 0: return skippedTimeResolver(mapping.LocalDateTime, mapping.Zone, mapping.EarlyInterval, mapping.LateInterval); case 1: return mapping.First(); case 2: return ambiguousTimeResolver(mapping.First(), mapping.Last()); default: throw new InvalidOperationException("Mapping has count outside range 0-2; should not happen."); } }); }