differenceBetweenDateTime static method
Period
differenceBetweenDateTime(
- LocalDateTime start,
- LocalDateTime end, [
- PeriodUnits units = PeriodUnits.dateAndTime
Returns the exact difference between two date/times or returns the period between a start and an end date/time, using only the given units.
If end
is before 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 start
will give a value between start
and end
. In other words,
any rounding is 'towards start'; this is true whether the resulting period is negative or positive.
start
: Start date/timeend
: End date/timeunits
: Units to use for calculations
Returns: The period between the given date/times, using the given units.
- ArgumentError:
units
is empty or contained unknown values. - ArgumentError:
start
andend
use different calendars.
Implementation
static Period differenceBetweenDateTime(LocalDateTime start, LocalDateTime end, [PeriodUnits units = PeriodUnits.dateAndTime]) {
Preconditions.checkArgument(units != PeriodUnits.none, 'units', "Units must not be empty");
Preconditions.checkArgument((units.value & ~PeriodUnits.allUnits.value) == 0, 'units', "Units contains an unknown value: $units");
CalendarSystem calendar = start.calendar;
Preconditions.checkArgument(calendar == end.calendar, '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.calendarDate;
if (start < end) {
if (start.clockTime > end.clockTime) {
endDate = endDate.addDays(-1);
}
}
else if (start > end && start.clockTime < end.clockTime) {
endDate = endDate.addDays(1);
}
// Optimization for single field
// todo: optimize me?
Map _betweenFunctionMap = {
PeriodUnits.years: () => Period(years: DatePeriodFields.yearsField.unitsBetween(start.calendarDate, endDate)),
PeriodUnits.months: () => Period(months: DatePeriodFields.monthsField.unitsBetween(start.calendarDate, endDate)),
PeriodUnits.weeks: () => Period(weeks: DatePeriodFields.weeksField.unitsBetween(start.calendarDate, endDate)),
PeriodUnits.days: () => Period(days: _daysBetween(start.calendarDate, endDate)),
PeriodUnits.hours: () => Period(hours: TimePeriodField.hours.unitsBetween(start, end)),
PeriodUnits.minutes: () => Period(minutes: TimePeriodField.minutes.unitsBetween(start, end)),
PeriodUnits.seconds: () => Period(seconds: TimePeriodField.seconds.unitsBetween(start, end)),
PeriodUnits.milliseconds: () => Period(milliseconds: TimePeriodField.milliseconds.unitsBetween(start, end)),
PeriodUnits.microseconds: () => Period(microseconds: TimePeriodField.microseconds.unitsBetween(start, end)),
PeriodUnits.nanoseconds: () => Period(nanoseconds: TimePeriodField.nanoseconds.unitsBetween(start, end))
};
if (_betweenFunctionMap.containsKey(units)) return _betweenFunctionMap[units]();
// switch (units) {
// case PeriodUnits.years:
// return new Period.fromYears(DatePeriodFields.YearsField.UnitsBetween(start.Date, endDate));
// case PeriodUnits.months:
// return new Period.fromMonths(DatePeriodFields.MonthsField.UnitsBetween(start.Date, endDate));
// case PeriodUnits.weeks:
// return new Period.fromWeeks(DatePeriodFields.WeeksField.UnitsBetween(start.Date, endDate));
// case PeriodUnits.days:
// return new Period.fromDays(DaysBetween(start.Date, endDate));
// case PeriodUnits.hours:
// return new Period.fromHours(TimePeriodField.Hours.UnitsBetween(start, end));
// case PeriodUnits.minutes:
// return new Period.fromMinutes(TimePeriodField.Minutes.UnitsBetween(start, end));
// case PeriodUnits.seconds:
// return new Period.fromSeconds(TimePeriodField.Seconds.UnitsBetween(start, end));
// case PeriodUnits.milliseconds:
// return new Period.fromMilliseconds(TimePeriodField.Milliseconds.UnitsBetween(start, end));
// case PeriodUnits.ticks:
// return new Period.fromTicks(TimePeriodField.Ticks.UnitsBetween(start, end));
// case PeriodUnits.nanoseconds:
// return new Period.fromNanoseconds(TimePeriodField.Nanoseconds.UnitsBetween(start, end));
// }
// Multiple fields
LocalDateTime remaining = start;
int years = 0,
months = 0,
weeks = 0,
days = 0;
if ((units.value & PeriodUnits.allDateUnits.value) != 0) {
// LocalDate remainingDate = DateComponentsBetween(
// start.Date, endDate, units, out years, out months, out weeks, out days);
var result = _dateComponentsBetween(start.calendarDate, endDate, units);
years = result.years;
months = result.months;
weeks = result.weeks;
days = result.days;
var remainingDate = result.date;
remaining = LocalDateTime.localDateAtTime(remainingDate, start.clockTime);
}
if ((units.value & PeriodUnits.allTimeUnits.value) == 0) {
return Period(years: years, months: months, weeks: weeks, days: 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 int arithmetic (as we do for Between(LocalTime, LocalTime)).
// Otherwise (rare case), use duration arithmetic.
int hours, minutes, seconds, milliseconds, microseconds, nanoseconds;
var span = ILocalDateTime.toLocalInstant(end).timeSinceLocalEpoch
- ILocalDateTime.toLocalInstant(remaining).timeSinceLocalEpoch;
if (span.canNanosecondsBeInteger) {
var result = _timeComponentsBetween(span.inNanoseconds, units);
hours = result.hours;
minutes = result.minutes;
seconds = result.seconds;
milliseconds = result.milliseconds;
microseconds = result.microseconds;
nanoseconds = result.nanoseconds;
// TimeComponentsBetween(duration.ToInt64Nanoseconds(), units, out hours, out minutes, out seconds, out milliseconds, out ticks, out nanoseconds);
}
else {
int unitsBetween(PeriodUnits mask, TimePeriodField timeField) {
if ((mask.value & units.value) == 0) {
return 0;
}
int value = timeField.getUnitsInDuration(span);
span -= timeField.toSpan(value);
return value;
}
hours = unitsBetween(PeriodUnits.hours, TimePeriodField.hours);
minutes = unitsBetween(PeriodUnits.minutes, TimePeriodField.minutes);
seconds = unitsBetween(PeriodUnits.seconds, TimePeriodField.seconds);
milliseconds = unitsBetween(PeriodUnits.milliseconds, TimePeriodField.milliseconds);
microseconds = unitsBetween(PeriodUnits.microseconds, TimePeriodField.microseconds);
nanoseconds = unitsBetween(PeriodUnits.microseconds, TimePeriodField.nanoseconds);
}
return Period(years: years,
months: months,
weeks: weeks,
days: days,
hours: hours,
minutes: minutes,
seconds: seconds,
milliseconds: milliseconds,
microseconds: microseconds,
nanoseconds: nanoseconds);
}