Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions system/I18n/Exceptions/I18nException.php
Original file line number Diff line number Diff line change
Expand Up @@ -96,4 +96,14 @@ public static function forInvalidSeconds(string $seconds)
{
return new static(lang('Time.invalidSeconds', [$seconds]));
}

/**
* Thrown when the supplied clamp range is invalid.
*
* @return static
*/
public static function forInvalidClampRange(string $start, string $end)
{
return new static(lang('Time.invalidClampRange', [$start, $end]));
}
}
35 changes: 35 additions & 0 deletions system/I18n/TimeTrait.php
Original file line number Diff line number Diff line change
Expand Up @@ -862,6 +862,41 @@ public function subYears(int $years)
return $time->sub(DateInterval::createFromDateString("{$years} years"));
}

/**
* Returns a new Time instance with the time clamped between the two provided times.
* If the current instance is before the $start time, a new instance will be returned with the same time as $start.
* If the current instance is after the $end time, a new instance will be returned with the same time as $end.
* Otherwise, the current instance will be returned.
*/
public function clamp(DateTimeInterface|self|string $start, DateTimeInterface|self|string $end): static
{
if ($start instanceof DateTimeInterface && ! $start instanceof self) {
$start = static::createFromInstance($start, $this->locale);
} elseif (is_string($start)) {
$start = new static($start, $this->getTimezone(), $this->locale);
}

if ($end instanceof DateTimeInterface && ! $end instanceof self) {
$end = static::createFromInstance($end, $this->locale);
} elseif (is_string($end)) {
$end = new static($end, $this->getTimezone(), $this->locale);
}
Comment on lines +873 to +883
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like this way. But I didn't found any current method that would cleanly return the static instance from either DateTimeInterface, self and string. So, maybe create a method for this?


if ($end->isBefore($start)) {
throw I18nException::forInvalidClampRange($start->toDateTimeString(), $end->toDateTimeString());
}

if ($this->isBefore($start)) {
return static::createFromInstance($start, $this->locale);
}

if ($this->isAfter($end)) {
return static::createFromInstance($end, $this->locale);
}

return $this;
}

// --------------------------------------------------------------------
// Formatters
// --------------------------------------------------------------------
Expand Down
39 changes: 20 additions & 19 deletions system/Language/en/Time.php
Original file line number Diff line number Diff line change
Expand Up @@ -13,23 +13,24 @@

// Time language settings
return [
'invalidFormat' => '"{0}" is not a valid datetime format',
'invalidMonth' => 'Months must be between 1 and 12. Given: {0}',
'invalidDay' => 'Days must be between 1 and 31. Given: {0}',
'invalidOverDay' => 'Days must be between 1 and {0}. Given: {1}',
'invalidHours' => 'Hours must be between 0 and 23. Given: {0}',
'invalidMinutes' => 'Minutes must be between 0 and 59. Given: {0}',
'invalidSeconds' => 'Seconds must be between 0 and 59. Given: {0}',
'years' => '{0, plural, =1{# year} other{# years}}',
'months' => '{0, plural, =1{# month} other{# months}}',
'weeks' => '{0, plural, =1{# week} other{# weeks}}',
'days' => '{0, plural, =1{# day} other{# days}}',
'hours' => '{0, plural, =1{# hour} other{# hours}}',
'minutes' => '{0, plural, =1{# minute} other{# minutes}}',
'seconds' => '{0, plural, =1{# second} other{# seconds}}',
'ago' => '{0} ago',
'inFuture' => 'in {0}',
'yesterday' => 'Yesterday',
'tomorrow' => 'Tomorrow',
'now' => 'Just now',
'invalidFormat' => '"{0}" is not a valid datetime format',
'invalidMonth' => 'Months must be between 1 and 12. Given: {0}',
'invalidDay' => 'Days must be between 1 and 31. Given: {0}',
'invalidOverDay' => 'Days must be between 1 and {0}. Given: {1}',
'invalidHours' => 'Hours must be between 0 and 23. Given: {0}',
'invalidMinutes' => 'Minutes must be between 0 and 59. Given: {0}',
'invalidSeconds' => 'Seconds must be between 0 and 59. Given: {0}',
'invalidClampRange' => 'The start time "{0}" must be earlier than or equal to the end time "{1}".',
'years' => '{0, plural, =1{# year} other{# years}}',
'months' => '{0, plural, =1{# month} other{# months}}',
'weeks' => '{0, plural, =1{# week} other{# weeks}}',
'days' => '{0, plural, =1{# day} other{# days}}',
'hours' => '{0, plural, =1{# hour} other{# hours}}',
'minutes' => '{0, plural, =1{# minute} other{# minutes}}',
'seconds' => '{0, plural, =1{# second} other{# seconds}}',
'ago' => '{0} ago',
'inFuture' => 'in {0}',
'yesterday' => 'Yesterday',
'tomorrow' => 'Tomorrow',
'now' => 'Just now',
];
29 changes: 29 additions & 0 deletions tests/system/I18n/TimeTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -744,6 +744,35 @@ public function testSetTimestampDateTimeImmutable(): void
$this->assertSame('2017-03-31 19:00:00 -05:00', $time2->format('Y-m-d H:i:s P'));
}

public function testClamp(): void
{
$time = Time::parse('May 10, 2017', 'America/Chicago');
$time2 = $time->clamp('2017-05-01', '2017-05-31');

$this->assertInstanceOf(Time::class, $time2);
$this->assertSame($time, $time2);

$time3 = $time->clamp('2017-05-11', '2017-05-31');

$this->assertInstanceOf(Time::class, $time3);
$this->assertNotSame($time, $time3);
$this->assertSame('2017-05-11 00:00:00', $time3->toDateTimeString());

$time4 = $time->clamp('2017-05-01', '2017-05-09');

$this->assertInstanceOf(Time::class, $time4);
$this->assertNotSame($time, $time4);
$this->assertSame('2017-05-09 00:00:00', $time4->toDateTimeString());
}

public function testClampException(): void
{
$this->expectException(I18nException::class);

$time = Time::parse('May 10, 2017', 'America/Chicago');
$time->clamp('2017-05-31', '2017-05-01');
}

public function testToDateString(): void
{
$time = Time::parse('May 10, 2017', 'America/Chicago');
Expand Down
1 change: 1 addition & 0 deletions user_guide_src/source/changelogs/v4.8.0.rst
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,7 @@ Others

- **Float and Double Casting:** Added support for precision and rounding mode when casting to float or double in entities.
- Float and Double casting now throws ``CastException::forInvalidFloatRoundingMode()`` if an rounding mode other than up, down, even or odd is provided.
- Added new ``TimeTrait::clamp($start, $end)`` method which returns clamped time between two times. See :ref:`time-setters-clamp` for details.

***************
Message Changes
Expand Down
14 changes: 14 additions & 0 deletions user_guide_src/source/libraries/time.rst
Original file line number Diff line number Diff line change
Expand Up @@ -317,6 +317,20 @@ Returns a new instance with the date set to the new timestamp:
.. note:: Prior to v4.6.0, due to a bug, this method might return incorrect
date/time. See :ref:`Upgrading Guide <upgrade-460-time-set-timestamp>` for details.

.. _time-setters-clamp:

clamp()
-------

.. versionadded:: 4.8.0

Returns a new Time instance with the time clamped between the two times passed in.
If the current instance is before the $min time, new instance with $min time will be returned.
If the current instance is after the $max time, new instance with $max time will be returned.
Otherwise, the current instance will be returned.

.. literalinclude:: time/045.php

Modifying the Value
===================

Expand Down
14 changes: 14 additions & 0 deletions user_guide_src/source/libraries/time/045.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
<?php

use CodeIgniter\I18n\Time;

$time = Time::parse('April 25, 2026 5:46:00pm UTC');

// If the time is between the two times, it will return the time itself.
echo $time->clamp('April 25, 2026 5:00:00pm UTC', 'April 25, 2026 7:00:00pm UTC')->toDateTimeString(); // 2026-04-25 17:46:00

// If the time is before the start time, it will return new instance of start time.
echo $time->clamp('April 25, 2026 6:05:00pm UTC', 'April 25, 2026 9:20:00pm UTC')->toDateTimeString(); // 2026-04-25 18:05:00

// If the time is after the end time, it will return new instance of end time.
echo $time->clamp('April 25, 2026 3:40:00pm UTC', 'April 25, 2026 5:00:00pm UTC')->toDateTimeString(); // 2026-04-25 17:00:00
Loading