diff --git a/src/Carbon/CarbonInterval.php b/src/Carbon/CarbonInterval.php index 7ff8cad0..114525e5 100644 --- a/src/Carbon/CarbonInterval.php +++ b/src/Carbon/CarbonInterval.php @@ -2158,6 +2158,14 @@ public function toPeriod(...$params): CarbonPeriod $class = ($params[0] ?? null) instanceof DateTime ? CarbonPeriod::class : CarbonPeriodImmutable::class; + if ($this->step) { + $dates = array_filter($params, static fn (mixed $param) => $param instanceof DateTimeInterface); + + if (\count($dates) >= 2 && $dates[0] > $dates[1]) { + $this->invert(); + } + } + return $class::create($this, ...$params); } diff --git a/src/Carbon/CarbonPeriod.php b/src/Carbon/CarbonPeriod.php index 7ae64ebf..c4cc9007 100644 --- a/src/Carbon/CarbonPeriod.php +++ b/src/Carbon/CarbonPeriod.php @@ -694,20 +694,6 @@ public function __construct(...$arguments) } } - if ($raw === null && isset($sortedArguments['start'])) { - $end = $sortedArguments['end'] ?? max(1, $sortedArguments['recurrences'] ?? 1); - - if (\is_float($end)) { - $end = $end === INF ? PHP_INT_MAX : (int) round($end); - } - - $raw = [ - $sortedArguments['start'], - $sortedArguments['interval'] ?? CarbonInterval::day(), - $end, - ]; - } - $this->setFromAssociativeArray($sortedArguments); if ($this->startDate === null) { @@ -2408,7 +2394,7 @@ protected function filterEndDate(CarbonInterface $current): bool|callable return true; } - if ($this->dateInterval->invert ? $current > $this->endDate : $current < $this->endDate) { + if ($this->dateInterval->invert ? ($current > $this->endDate) : ($current < $this->endDate)) { return true; } diff --git a/src/Carbon/Traits/Units.php b/src/Carbon/Traits/Units.php index 59537214..01b828af 100644 --- a/src/Carbon/Traits/Units.php +++ b/src/Carbon/Traits/Units.php @@ -258,12 +258,18 @@ public function add($unit, $value = 1, ?bool $overflow = null): static $unit = CarbonInterval::make($unit, [], true); } + $negated = false; + if ($unit instanceof CarbonConverterInterface) { + if ($unit instanceof DateInterval) { + $negated = (bool) $unit->invert; + } + $unit = Closure::fromCallable([$unit, 'convertDate']); } if ($unit instanceof Closure) { - $result = $this->resolveCarbon($unit($this, false)); + $result = $this->resolveCarbon($unit($this, $negated)); if ($this !== $result && $this->isMutable()) { return $this->modify($result->rawFormat('Y-m-d H:i:s.u e O')); diff --git a/tests/CarbonPeriod/IteratorTest.php b/tests/CarbonPeriod/IteratorTest.php index 1442c699..87423678 100644 --- a/tests/CarbonPeriod/IteratorTest.php +++ b/tests/CarbonPeriod/IteratorTest.php @@ -14,6 +14,7 @@ namespace Tests\CarbonPeriod; use Carbon\Carbon; +use Carbon\CarbonInterface; use Carbon\CarbonInterval; use Carbon\CarbonPeriod; use Generator; @@ -560,4 +561,58 @@ public function testTimezone() $this->assertSame('00 America/Toronto', $str); } + + public function testDynamicStep() + { + $startDate = Carbon::create('2024-09-5')->startOfDay(); + $endDate = Carbon::create('2024-09-12')->startOfDay(); + + $twoWeekdaysInterval = new CarbonInterval( + fn (CarbonInterface $date, bool $negated): CarbonInterface => $negated + ? $date->subWeekdays(2) + : $date->addWeekdays(2), + ); + $twoWeekdaysPeriod = $twoWeekdaysInterval->toPeriod($startDate, $endDate); + + $this->assertSame(3, $twoWeekdaysPeriod->count()); + + $dates = []; + + foreach ($twoWeekdaysPeriod as $date) { + $dates[] = $date->toDateString(); + } + + $this->assertSame([ + '2024-09-05', + '2024-09-09', + '2024-09-11', + ], $dates); + } + + public function testReverseOrderWithDynamicStep() + { + $startDate = Carbon::create('2024-09-12')->startOfDay(); + $endDate = Carbon::create('2024-09-5')->startOfDay(); + + $twoWeekdaysInterval = new CarbonInterval( + fn (CarbonInterface $date, bool $negated): CarbonInterface => $negated + ? $date->subWeekdays(2) + : $date->addWeekdays(2), + ); + $twoWeekdaysPeriod = $twoWeekdaysInterval->toPeriod($startDate, $endDate); + + $this->assertSame(3, $twoWeekdaysPeriod->count()); + + $dates = []; + + foreach ($twoWeekdaysPeriod as $date) { + $dates[] = $date->toDateString(); + } + + $this->assertSame([ + '2024-09-12', + '2024-09-10', + '2024-09-06', + ], $dates); + } }