///|
// datetime compares/subtracts in wall time only for the same tzinfo object.
// Fixed-offset values with equal offsets have the same result either way.
fn relative_date_difference(first : DateTime, second : DateTime) -> Int64 {
  let same_zone = match (first.timezone, second.timezone) {
    (Some(a), Some(b)) => physical_equal(a, b)
    (None, None) => first.offset_seconds == second.offset_seconds
    _ => false
  }
  if same_zone {
    locator_microseconds(first) - locator_microseconds(second)
  } else {
    rruleset_instant(first) - rruleset_instant(second)
  }
}

///|
/// Construct first - second using calendar years/months and the remaining wall
/// or instant duration. Matching rule-zone handles follow datetime's same-tzinfo
/// semantics. Construction does not read the date epoch or display timezone rc.
pub fn RelativeDelta::between(
  first : DateTime,
  second : DateTime,
) -> RelativeDelta raise DateError {
  let mut months = Int64::from_int(
    (first.year - second.year) * 12 + first.month - second.month,
  )
  let mut delta = RelativeDelta::new(months~)
  let mut adjusted = second.add_relative_delta(delta)
  let backwards = relative_date_difference(first, second) < 0L
  while (if backwards {
          relative_date_difference(first, adjusted) > 0L
        } else {
          relative_date_difference(first, adjusted) < 0L
        }) {
    months += if backwards { 1L } else { -1L }
    delta = RelativeDelta::new(months~)
    adjusted = second.add_relative_delta(delta)
  }
  let (seconds, microseconds) = delta_split(
    relative_date_difference(first, adjusted),
    1000000L,
  )
  RelativeDelta::new(
    years=delta.years,
    months=Int64::from_int(delta.months),
    seconds=seconds.to_double(),
    microseconds=microseconds.to_double(),
  )
}