nightIntervalMinutes(null, null)); self::assertSame(0, $calc->nightIntervalMinutes('08:00', null)); self::assertSame(0, $calc->nightIntervalMinutes(null, '17:00')); } public function testPureDayRangeHasNoNight(): void { $calc = new NightHoursCalculator(); // 08:00 -> 17:00 : entierement hors fenetres nuit (00:00-06:00, 21:00-24:00). self::assertSame(0, $calc->nightIntervalMinutes('08:00', '17:00')); } public function testWindowBoundariesAreRightExclusive(): void { $calc = new NightHoursCalculator(); // 06:00 -> 21:00 : pile entre les deux fenetres de nuit, 0 min. self::assertSame(0, $calc->nightIntervalMinutes('06:00', '21:00')); // 22:00 -> 06:00 : 22-24 (120) + 00-06 (360) = 480, borne 06:00 exclue. self::assertSame(480, $calc->nightIntervalMinutes('22:00', '06:00')); } public function testEveningWindowCounts(): void { $calc = new NightHoursCalculator(); // 21:00 -> 24:00 = 180 min de nuit. self::assertSame(180, $calc->nightIntervalMinutes('21:00', '00:00')); } public function testShiftCrossingMidnightCountsBothWindows(): void { $calc = new NightHoursCalculator(); // 21:00 -> 05:00 : 21-24 (180) + 00-05 (300) = 480 min. self::assertSame(480, $calc->nightIntervalMinutes('21:00', '05:00')); } public function testNightMinutesForWorkHourDriverUsesManualField(): void { $calc = new NightHoursCalculator(); $wh = new WorkHour(); $wh->setWorkDate(new DateTimeImmutable('2026-01-15')) ->setDayHoursMinutes(300) ->setNightHoursMinutes(250) ->setMorningFrom('08:00')->setMorningTo('12:00') ; // Driver -> champ manuel nightHoursMinutes, plages ignorees. self::assertSame(250, $calc->nightMinutesForWorkHour($wh, true)); } public function testNightMinutesForWorkHourNonDriverSumsRanges(): void { $calc = new NightHoursCalculator(); $wh = new WorkHour(); $wh->setWorkDate(new DateTimeImmutable('2026-01-15')) ->setMorningFrom('22:00')->setMorningTo('00:00') // 120 min nuit ->setEveningFrom('04:00')->setEveningTo('06:00') // 120 min nuit ; self::assertSame(240, $calc->nightMinutesForWorkHour($wh, false)); } }