예제 #1
0
 /// <summary>
 /// Adds the contents of this period to the given date and time, with the given scale (either 1 or -1, usually).
 /// </summary>
 internal LocalDateTime AddTo(LocalDate date, LocalTime time, int scalar)
 {
     date = AddTo(date, scalar);
     int extraDays = 0;
     time = TimePeriodField.Hours.Add(time, Hours * scalar, ref extraDays);
     time = TimePeriodField.Minutes.Add(time, Minutes * scalar, ref extraDays);
     time = TimePeriodField.Seconds.Add(time, Seconds * scalar, ref extraDays);
     time = TimePeriodField.Milliseconds.Add(time, Milliseconds * scalar, ref extraDays);
     time = TimePeriodField.Ticks.Add(time, Ticks * scalar, ref extraDays);
     time = TimePeriodField.Nanoseconds.Add(time, Nanoseconds * scalar, ref extraDays);
     // TODO(optimization): Investigate the performance impact of us calling PlusDays twice.
     // Could optimize by including that in a single call...
     return new LocalDateTime(date.PlusDays(extraDays), time);
 }
예제 #2
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, nameof(units), "Units must not be empty");
            Preconditions.CheckArgument((units & ~PeriodUnits.AllUnits) == 0, nameof(units), "Units contains an unknown value: {0}", units);
            CalendarSystem calendar = start.Calendar;
            Preconditions.CheckArgument(calendar.Equals(end.Calendar), nameof(end), "start and end must use the same calendar system");

            if (start == end)
            {
                return Zero;
            }

            // Adjust for situations like "days between 5th January 10am and 7th Janary 5am" which should be one
            // day, because if we actually reach 7th January with date fields, we've overshot.
            // The date adjustment will always be valid, because it's just moving it towards start.
            // We need this for all date-based period fields. We could potentially optimize by not doing this
            // in cases where we've only got time fields...
            LocalDate endDate = end.Date;
            if (start < end)
            {
                if (start.TimeOfDay > end.TimeOfDay)
                {
                    endDate = endDate.PlusDays(-1);
                }
            }
            else if (start > end && start.TimeOfDay < end.TimeOfDay)
            {
                endDate = endDate.PlusDays(1);
            }

            // Optimization for single field
            switch (units)
            {
                case PeriodUnits.Years: return FromYears(DatePeriodFields.YearsField.UnitsBetween(start.Date, endDate));
                case PeriodUnits.Months: return FromMonths(DatePeriodFields.MonthsField.UnitsBetween(start.Date, endDate));
                case PeriodUnits.Weeks: return FromWeeks(DatePeriodFields.WeeksField.UnitsBetween(start.Date, endDate));
                case PeriodUnits.Days: return FromDays(DaysBetween(start.Date, endDate));
                case PeriodUnits.Hours: return FromHours(TimePeriodField.Hours.UnitsBetween(start, end));
                case PeriodUnits.Minutes: return FromMinutes(TimePeriodField.Minutes.UnitsBetween(start, end));
                case PeriodUnits.Seconds: return FromSeconds(TimePeriodField.Seconds.UnitsBetween(start, end));
                case PeriodUnits.Milliseconds: return FromMilliseconds(TimePeriodField.Milliseconds.UnitsBetween(start, end));
                case PeriodUnits.Ticks: return FromTicks(TimePeriodField.Ticks.UnitsBetween(start, end));
                case PeriodUnits.Nanoseconds: return FromNanoseconds(TimePeriodField.Nanoseconds.UnitsBetween(start, end));
            }

            // Multiple fields
            LocalDateTime remaining = start;
            int years = 0, months = 0, weeks = 0, days = 0;
            if ((units & PeriodUnits.AllDateUnits) != 0)
            {
                LocalDate remainingDate = DateComponentsBetween(
                    start.Date, endDate, units, out years, out months, out weeks, out days);
                remaining = new LocalDateTime(remainingDate, start.TimeOfDay);
            }
            if ((units & PeriodUnits.AllTimeUnits) == 0)
            {
                return new Period(years, months, weeks, days);
            }

            // The remainder of the computation is with fixed-length units, so we can do it all with
            // Duration instead of Local* values. We don't know for sure that this is small though - we *could*
            // be trying to find the difference between 9998 BC and 9999 CE in nanoseconds...
            // Where we can optimize, do everything with long arithmetic (as we do for Between(LocalTime, LocalTime)).
            // Otherwise (rare case), use duration arithmetic.
            long hours, minutes, seconds, milliseconds, ticks, nanoseconds;
            var duration = end.ToLocalInstant().TimeSinceLocalEpoch - remaining.ToLocalInstant().TimeSinceLocalEpoch;
            if (duration.IsInt64Representable)
            {
                TimeComponentsBetween(duration.ToInt64Nanoseconds(), units, out hours, out minutes, out seconds, out milliseconds, out ticks, out nanoseconds);
            }
            else
            {
                hours = UnitsBetween(PeriodUnits.Hours, TimePeriodField.Hours);
                minutes = UnitsBetween(PeriodUnits.Minutes, TimePeriodField.Minutes);
                seconds = UnitsBetween(PeriodUnits.Seconds, TimePeriodField.Seconds);
                milliseconds = UnitsBetween(PeriodUnits.Milliseconds, TimePeriodField.Milliseconds);
                ticks = UnitsBetween(PeriodUnits.Ticks, TimePeriodField.Ticks);
                nanoseconds = UnitsBetween(PeriodUnits.Ticks, TimePeriodField.Nanoseconds);
            }
            return new Period(years, months, weeks, days, hours, minutes, seconds, milliseconds, ticks, nanoseconds);

            long UnitsBetween(PeriodUnits mask, TimePeriodField timeField)
            {
                if ((mask & units) == 0)
                {
                    return 0;
                }
                long value = timeField.GetUnitsInDuration(duration);
                duration -= timeField.ToDuration(value);
                return value;
            }
        }
예제 #3
0
 /// <summary>
 /// Add days.<br />
 /// 添加天数。
 /// </summary>
 /// <param name="ld"></param>
 /// <param name="days"></param>
 /// <returns></returns>
 public static LocalDate AddDays(this LocalDate ld, int days) => ld.PlusDays(days);