diff --git a/src/Illuminate/Support/Carbon.php b/src/Illuminate/Support/Carbon.php index 81f9fce89733..9b2363ca484f 100644 --- a/src/Illuminate/Support/Carbon.php +++ b/src/Illuminate/Support/Carbon.php @@ -4,6 +4,7 @@ use Carbon\Carbon as BaseCarbon; use Carbon\CarbonImmutable as BaseCarbonImmutable; +use DateTimeInterface; use Illuminate\Support\Traits\Conditionable; use Illuminate\Support\Traits\Dumpable; use Ramsey\Uuid\Uuid; @@ -34,6 +35,23 @@ public static function createFromId(Uuid|Ulid|string $id): static return static::createFromInterface($id->getDateTime()); } + /** + * Clamp the date to be within the given range. + * + * @param \DateTimeInterface|string $min + * @param \DateTimeInterface|string $max + * @return static + */ + public function clamp(DateTimeInterface|string $min, DateTimeInterface|string $max): static + { + $min = $this->resolveCarbon($min); + $max = $this->resolveCarbon($max); + + [$min, $max] = $min <= $max ? [$min, $max] : [$max, $min]; + + return $this->min($max)->max($min)->copy(); + } + /** * Get the current date / time plus a given amount of time. */ diff --git a/tests/Support/SupportCarbonTest.php b/tests/Support/SupportCarbonTest.php index 942d783d252e..d5b1ac336a5e 100644 --- a/tests/Support/SupportCarbonTest.php +++ b/tests/Support/SupportCarbonTest.php @@ -5,16 +5,14 @@ use BadMethodCallException; use Carbon\Carbon as BaseCarbon; use Carbon\CarbonImmutable as BaseCarbonImmutable; +use Carbon\CarbonInterface; use DateTimeInterface; use Illuminate\Support\Carbon; use PHPUnit\Framework\TestCase; class SupportCarbonTest extends TestCase { - /** - * @var \Illuminate\Support\Carbon - */ - protected $now; + protected Carbon $now; protected function setUp(): void { @@ -159,4 +157,70 @@ public function testMinus(): void $carbon = Carbon::parse('2026-05-31'); $this->assertSame('2026-04-30', $carbon->minus(months: 1, overflow: false)->toDateString()); } + + public function testClampReturnsSelfWhenWithinRange(): void + { + $result = Carbon::parse('2025-06-15')->clamp( + min: '2020-01-01', + max: '2030-12-31', + ); + + $this->assertCarbonIs($result, '2025-06-15'); + } + + public function testClampReturnsMinWhenBefore(): void + { + $result = Carbon::parse('2020-01-01')->clamp( + min: '2025-06-15', + max: '2030-12-31', + ); + + $this->assertCarbonIs($result, '2025-06-15'); + } + + public function testClampReturnsMaxWhenAfter(): void + { + $result = Carbon::parse('2030-12-31')->clamp( + min: '2020-01-01', + max: '2025-06-15', + ); + + $this->assertCarbonIs($result, '2025-06-15'); + } + + public function testClampDoesNotMutateOriginal(): void + { + $carbon = Carbon::parse('2020-01-01'); + $carbon->clamp( + min: '2025-06-15', + max: '2030-12-31', + ); + + $this->assertCarbonIs($carbon, '2020-01-01'); + } + + public function testClampSwapMinIfGreaterThanMax(): void + { + $result = Carbon::parse('2025-06-15')->clamp( + min: '2030-12-31', + max: '2020-01-01', + ); + + $this->assertCarbonIs($result, '2025-06-15'); + } + + public function testClampWorksWithCarbonInstances(): void + { + $result = Carbon::parse('2020-01-01')->clamp( + min: Carbon::parse('2025-06-15'), + max: Carbon::parse('2030-12-31'), + ); + + $this->assertCarbonIs($result, '2025-06-15'); + } + + protected function assertCarbonIs(CarbonInterface $carbon, string $expected, $format = 'Y-m-d'): void + { + $this->assertTrue($carbon->isSameAs($format, Carbon::createFromFormat($format, $expected))); + } }