{"record":{"id":"1fc91bbac23c5cd6","repo":"sebastianbergmann/comparator","slug":"failed-asserting-that-two-dateinterval-objects-are-equal","errorCode":null,"errorMessage":"Failed asserting that two DateInterval objects are equal.","messagePattern":"Failed asserting that two DateInterval objects are equal\\.","errorType":"exception","errorClass":"ComparisonFailure","httpStatus":null,"severity":"error","filePath":"src/DateIntervalComparator.php","lineNumber":52,"sourceCode":"    /**\n     * @param array<mixed> $processed\n     *\n     * @throws ComparisonFailure\n     */\n    public function assertEquals(mixed $expected, mixed $actual, float $delta = 0.0, bool $canonicalize = false, bool $ignoreCase = false, array &$processed = []): void\n    {\n        assert($expected instanceof DateInterval);\n        assert($actual instanceof DateInterval);\n\n        if (in_array([$actual, $expected], $processed, true) ||\n            in_array([$expected, $actual], $processed, true)) {\n            return;\n        }\n\n        $processed[] = [$actual, $expected];\n\n        if (abs($this->toSeconds($expected) - $this->toSeconds($actual)) > abs($delta)) {\n            throw new ComparisonFailure(\n                $expected,\n                $actual,\n                $this->format($expected),\n                $this->format($actual),\n                'Failed asserting that two DateInterval objects are equal.',\n                $this->contextLines(),\n            );\n        }\n    }\n\n    /**\n     * Converts a DateInterval to a duration in seconds by applying it to the\n     * Unix epoch. Month and year components are therefore evaluated against a\n     * fixed anchor (a month is not always 30 days, a year not always 365), so\n     * two intervals whose calendar components differ may still be considered\n     * equal within a delta when the resulting durations match.\n     */\n    private function toSeconds(DateInterval $interval): float","sourceCodeStart":34,"sourceCodeEnd":70,"githubUrl":"https://github.com/sebastianbergmann/comparator/blob/00837a9d227b4e123b0b42a91736960f6b0b3583/src/DateIntervalComparator.php#L34-L70","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Increase $delta to the tolerance your use case allows (e.g. 3600 for one hour).","Normalize both intervals to a common unit before comparing (e.g. compare toSeconds yourself).","Fix the source string so the parsed interval is the intended duration.","If DST is involved, construct intervals in UTC or compare fixed seconds rather than calendar days."],"exampleFix":"// before\n$c->assertEquals(new DateInterval('P1D'), new DateInterval('PT23H'), 0.0); // throws\n// after\n$c->assertEquals(new DateInterval('P1D'), new DateInterval('PT23H'), 3600.0); // allow 1h tolerance","handlingStrategy":"validation","validationCode":"// compare durations in seconds first\n$secs = fn(DateInterval $i) => (new DateTime('@0'))->add($i)->getTimestamp();\nif (abs($secs($expected) - $secs($actual)) > $toleranceSeconds) { /* will fail; adjust delta or data */ }","typeGuard":"function isDateInterval(mixed $v): bool { return $v instanceof DateInterval; }","tryCatchPattern":"try {\n    $c->assertEquals($expectedInterval, $actualInterval, 60.0);\n} catch (ComparisonFailure $e) {\n    // inspect $e->getExpectedAsString()/getActualAsString() (formatted intervals)\n}","preventionTips":["Always pass an explicit delta for interval comparisons.","Convert both intervals to seconds yourself when you need precise semantics.","Be aware DST/day-based intervals may not equal the same number of seconds."],"tags":["phpunit","datetime","dateinterval","comparison"],"backgroundTag":"value-out-of-range","analyzedSha":"00837a9d227b4e123b0b42a91736960f6b0b3583","analyzedAt":"2026-09-15T03:36:55.154Z","contentChangedAt":"2026-09-15T03:36:55.154Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}