sebastianbergmann/comparator · error · ComparisonFailure

Failed asserting that two DateInterval objects are equal.

Error message

Failed asserting that two DateInterval objects are equal.

What it means

ComparisonFailure thrown by DateIntervalComparator::assertEquals when two DateInterval objects differ by more than $delta in total seconds (each interval converted via toSeconds). DateInterval equality is duration-based, not property-based, since ISO 8601 durations can be expressed in many equivalent ways.

Solutions

  1. Increase $delta to the tolerance your use case allows (e.g. 3600 for one hour).
  2. Normalize both intervals to a common unit before comparing (e.g. compare toSeconds yourself).
  3. Fix the source string so the parsed interval is the intended duration.
  4. If DST is involved, construct intervals in UTC or compare fixed seconds rather than calendar days.

Example fix

// before
$c->assertEquals(new DateInterval('P1D'), new DateInterval('PT23H'), 0.0); // throws
// after
$c->assertEquals(new DateInterval('P1D'), new DateInterval('PT23H'), 3600.0); // allow 1h tolerance
Defensive patterns

Strategy: validation

Validate before calling

// compare durations in seconds first
$secs = fn(DateInterval $i) => (new DateTime('@0'))->add($i)->getTimestamp();
if (abs($secs($expected) - $secs($actual)) > $toleranceSeconds) { /* will fail; adjust delta or data */ }

Type guard

function isDateInterval(mixed $v): bool { return $v instanceof DateInterval; }

Try / catch

try {
    $c->assertEquals($expectedInterval, $actualInterval, 60.0);
} catch (ComparisonFailure $e) {
    // inspect $e->getExpectedAsString()/getActualAsString() (formatted intervals)
}

Prevention

When it happens

Trigger: assertEquals on two DateInterval instances where abs(toSeconds(expected) - toSeconds(actual)) > abs(delta) — e.g. comparing 'P1D' with 'PT24H' plus timezone/DST-influenced conversions, or passing too small a delta.

Common situations: Comparing durations parsed from ISO 8601 strings against DateInterval::createFromDateString results; DST transitions making day-based and second-based intervals unequal; asserting expiry/timeout intervals in tests with an overly tight delta.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of sebastianbergmann/comparator@00837a9d22 (2026-09-15). Data as JSON: /api/errors/1fc91bbac23c5cd6. Report an issue: GitHub.

Appendix: source

Thrown at src/DateIntervalComparator.php:52

    /**
     * @param array<mixed> $processed
     *
     * @throws ComparisonFailure
     */
    public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false, array &$processed = []): void
    {
        assert($expected instanceof DateInterval);
        assert($actual instanceof DateInterval);

        if (in_array([$actual, $expected], $processed, true) ||
            in_array([$expected, $actual], $processed, true)) {
            return;
        }

        $processed[] = [$actual, $expected];

        if (abs($this->toSeconds($expected) - $this->toSeconds($actual)) > abs($delta)) {
            throw new ComparisonFailure(
                $expected,
                $actual,
                $this->format($expected),
                $this->format($actual),
                'Failed asserting that two DateInterval objects are equal.',
                $this->contextLines(),
            );
        }
    }

    /**
     * Converts a DateInterval to a duration in seconds by applying it to the
     * Unix epoch. Month and year components are therefore evaluated against a
     * fixed anchor (a month is not always 30 days, a year not always 365), so
     * two intervals whose calendar components differ may still be considered
     * equal within a delta when the resulting durations match.
     */
    private function toSeconds(DateInterval $interval): float

View on GitHub (pinned to 00837a9d22)