Exemple #1
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)));
            }
        }
Exemple #2
0
        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));
            }
        }
Exemple #3
0
        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);
            }
        }
Exemple #4
0
        /// <summary>
        /// Returns the period between a start and an end date/time, using only the given units.
        /// </summary>
        /// <remarks>
        /// If <paramref name="end"/> is before <paramref name="start" />, each property in the returned period
        /// will be negative. If the given set of units cannot exactly reach the end point (e.g. finding
        /// the difference between 1am and 3:15am in hours) the result will be such that adding it to <paramref name="start"/>
        /// will give a value between <paramref name="start"/> and <paramref name="end"/>. In other words,
        /// any rounding is "towards start"; this is true whether the resulting period is negative or positive.
        /// </remarks>
        /// <param name="start">Start date/time</param>
        /// <param name="end">End date/time</param>
        /// <param name="units">Units to use for calculations</param>
        /// <exception cref="ArgumentException"><paramref name="units"/> is empty or contained unknown values.</exception>
        /// <exception cref="ArgumentException"><paramref name="start"/> and <paramref name="end"/> use different calendars.</exception>
        /// <returns>The period between the given date/times, using the given units.</returns>
        public static Period Between(LocalDateTime start, LocalDateTime end, PeriodUnits units)
        {
            Preconditions.CheckArgument(units != 0, "units", "Units must not be empty");
            Preconditions.CheckArgument((units & ~PeriodUnits.AllUnits) == 0, "units", "Units contains an unknown value: {0}", units);
            CalendarSystem calendar = start.Calendar;

            Preconditions.CheckArgument(calendar.Equals(end.Calendar), "end", "start and end must use the same calendar system");

            LocalInstant startLocalInstant = start.LocalInstant;
            LocalInstant endLocalInstant   = end.LocalInstant;

            if (startLocalInstant == endLocalInstant)
            {
                return(Zero);
            }

            PeriodFieldSet fields = calendar.PeriodFields;

            // Optimization for single field
            var singleField = GetSingleField(fields, units);

            if (singleField != null)
            {
                long value = singleField.Subtract(end.LocalInstant, start.LocalInstant);
                return(new Period(units, value));
            }

            // Multiple fields
            long[] values = new long[ValuesArraySize];

            LocalInstant remaining     = startLocalInstant;
            int          numericFields = (int)units;

            for (int i = 0; i < ValuesArraySize; i++)
            {
                if ((numericFields & (1 << i)) != 0)
                {
                    var field = GetFieldForIndex(fields, i);
                    values[i] = field.Subtract(endLocalInstant, remaining);
                    remaining = field.Add(remaining, values[i]);
                }
            }
            return(new Period(values));
        }
        /// <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);
        }
Exemple #6
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);
        }
Exemple #7
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));
            }
        }
        /// <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);
            }
        }
Exemple #9
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);
        }
Exemple #10
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);
        }
 internal int GetMinuteOfDay(LocalInstant localInstant)
 {
     return(TimeOfDayCalculator.GetMinuteOfDay(localInstant));
 }
 /// <summary>
 /// Constructs a <see cref="DateTime"/> from this <see cref="ZonedDateTime"/> which has a
 /// <see cref="DateTime.Kind"/> of <see cref="DateTimeKind.Unspecified"/> and represents the same local time as
 /// this value rather than the same instant in time.
 /// </summary>
 /// <remarks>
 /// <see cref="DateTimeKind.Unspecified"/> is slightly odd - it can be treated as UTC if you use <see cref="DateTime.ToLocalTime"/>
 /// or as system local time if you use <see cref="DateTime.ToUniversalTime"/>, but it's the only kind which allows
 /// you to construct a <see cref="DateTimeOffset"/> with an arbitrary offset.
 /// </remarks>
 /// <returns>A <see cref="DateTime"/> representation of this value with an "unspecified" kind, with the same
 /// local date and time as this value.</returns>
 public DateTime ToDateTimeUnspecified()
 {
     return(LocalInstant.ToDateTimeUnspecified());
 }
Exemple #13
0
        /// <summary>
        /// Returns the local date corresponding to the given "week year", "week of week year", and "day of week"
        /// in the ISO calendar system.
        /// </summary>
        /// <param name="weekYear">ISO-8601 week year of value to return</param>
        /// <param name="weekOfWeekYear">ISO-8601 week of week year of value to return</param>
        /// <param name="dayOfWeek">ISO-8601 day of week to return</param>
        /// <returns>The date corresponding to the given week year / week of week year / day of week.</returns>
        public static LocalDate FromWeekYearWeekAndDay(int weekYear, int weekOfWeekYear, IsoDayOfWeek dayOfWeek)
        {
            LocalInstant localInstant = CalendarSystem.Iso.GetLocalInstantFromWeekYearWeekAndDayOfWeek(weekYear, weekOfWeekYear, dayOfWeek);

            return(new LocalDate(new LocalDateTime(localInstant)));
        }
 internal int GetYearOfCentury(LocalInstant localInstant)
 {
     return(yearMonthDayCalculator.GetYearOfCentury(localInstant));
 }
 internal int GetEra(LocalInstant localInstant)
 {
     return(yearMonthDayCalculator.GetEra(localInstant));
 }
 internal int GetWeekYear(LocalInstant localInstant)
 {
     return(weekYearCalculator.GetWeekYear(localInstant));
 }
 internal int GetMonthOfYear(LocalInstant localInstant)
 {
     return(yearMonthDayCalculator.GetMonthOfYear(localInstant));
 }
 internal int GetDayOfWeek(LocalInstant localInstant)
 {
     return(WeekYearCalculator.GetDayOfWeek(localInstant));
 }
Exemple #19
0
 /// <summary>
 /// Constructor only called from other parts of Noda Time - trusted to be within January 1st 1970 UTC.
 /// </summary>
 internal LocalTime(LocalInstant localInstant)
 {
     this.localInstant = localInstant;
 }
 internal long GetTickOfDay(LocalInstant localInstant)
 {
     return(TimeOfDayCalculator.GetTickOfDay(localInstant));
 }
 internal int GetSecondOfMinute(LocalInstant localInstant)
 {
     return(TimeOfDayCalculator.GetSecondOfMinute(localInstant));
 }
 internal int GetMillisecondOfSecond(LocalInstant localInstant)
 {
     return(TimeOfDayCalculator.GetMillisecondOfSecond(localInstant));
 }
 internal int GetClockHourOfHalfDay(LocalInstant localInstant)
 {
     return(TimeOfDayCalculator.GetClockHourOfHalfDay(localInstant));
 }
Exemple #24
0
 /// <summary>
 /// Initializes a new instance of the <see cref="LocalDateTime"/> struct using the ISO
 /// calendar system.
 /// </summary>
 /// <param name="localInstant">The local instant.</param>
 /// <returns>The resulting date/time.</returns>
 internal LocalDateTime([Trusted] LocalInstant localInstant)
 {
     date = new LocalDate(localInstant.DaysSinceEpoch);
     time = new LocalTime(localInstant.NanosecondOfDay);
 }