ChronosTime::parseString() passed the fractional-seconds digits straight to (int), so a short fraction such as .5 was read as 5 microseconds instead of 500000 (half a second), diverging from DateTime. Right-pad the fractional digits to microseconds before converting.
393 lines
14 KiB
PHP
393 lines
14 KiB
PHP
<?php
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
|
*
|
|
* Licensed under The MIT License
|
|
* Redistributions of files must retain the above copyright notice.
|
|
*
|
|
* @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
|
|
* @link https://cakephp.org CakePHP(tm) Project
|
|
* @license https://www.opensource.org/licenses/mit-license.php MIT License
|
|
*/
|
|
|
|
namespace Cake\Chronos\Test\TestCase;
|
|
|
|
use Cake\Chronos\Chronos;
|
|
use Cake\Chronos\ChronosTime;
|
|
use DateTimeImmutable;
|
|
use InvalidArgumentException;
|
|
|
|
class ChronosTimeTest extends TestCase
|
|
{
|
|
public function testConstructNow(): void
|
|
{
|
|
Chronos::setTestNow('2001-01-01 12:13:14.123456');
|
|
|
|
$t = new ChronosTime();
|
|
$this->assertSame('12:13:14.123456', $t->format('H:i:s.u'));
|
|
|
|
$t = new ChronosTime();
|
|
$this->assertSame('12:13:14.123456', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testConstructFromString(): void
|
|
{
|
|
$t = new ChronosTime('12:13');
|
|
$this->assertSame('12:13:00.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = new ChronosTime('0.0.0.0');
|
|
$this->assertSame('00:00:00.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = new ChronosTime('1:01:1.000001');
|
|
$this->assertSame('01:01:01.000001', $t->format('H:i:s.u'));
|
|
|
|
$t = new ChronosTime('23:59.59.999999');
|
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
|
|
|
$t = new ChronosTime('23:59.59.9999991');
|
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
|
|
|
$t = new ChronosTime('24:59.59.9999991');
|
|
$this->assertSame('00:59:59.999999', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testConstructFromStringWithFractionalSeconds(): void
|
|
{
|
|
// The fractional part is a fraction of a second, matching DateTime.
|
|
$t = new ChronosTime('12:00:00.5');
|
|
$this->assertSame('12:00:00.500000', $t->format('H:i:s.u'));
|
|
|
|
$t = new ChronosTime('12:00:00.05');
|
|
$this->assertSame('12:00:00.050000', $t->format('H:i:s.u'));
|
|
|
|
$t = new ChronosTime('12:00:00.000005');
|
|
$this->assertSame('12:00:00.000005', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testConstructFromInstance(): void
|
|
{
|
|
$t = new ChronosTime(new DateTimeImmutable('23:59:59.999999'));
|
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
|
|
|
$t = new ChronosTime(new Chronos('23:59:59.999999'));
|
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
|
|
|
$t = new ChronosTime(new ChronosTime(new Chronos('23:59:59.999999')));
|
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testConstructInvalid(): void
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
new ChronosTime('tomorrow');
|
|
}
|
|
|
|
public function testConstructIncomplete(): void
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
new ChronosTime('23');
|
|
}
|
|
|
|
public function testConstructInvalidHours(): void
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
new ChronosTime('25:00:00');
|
|
}
|
|
|
|
public function testConstructInvalidMinutes(): void
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
new ChronosTime('23:60:00');
|
|
}
|
|
|
|
public function testConstructInvalidSeconds(): void
|
|
{
|
|
$this->expectException(InvalidArgumentException::class);
|
|
new ChronosTime('23:59:60');
|
|
}
|
|
|
|
public function testParse(): void
|
|
{
|
|
$t = ChronosTime::parse('23:59:59.999999');
|
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
|
|
|
Chronos::setTestNow(new Chronos('2001-01-01 12:13:14.123456', 'America/Chicago'));
|
|
$t = ChronosTime::parse(null, 'America/New_York');
|
|
$this->assertSame('13:13:14.123456', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testNow(): void
|
|
{
|
|
Chronos::setTestNow(new Chronos('2001-01-01 12:13:14.123456', 'America/Chicago'));
|
|
|
|
$t = ChronosTime::now();
|
|
$this->assertSame('12:13:14.123456', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::now('America/New_York');
|
|
$this->assertSame('13:13:14.123456', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testMidnight(): void
|
|
{
|
|
$t = ChronosTime::midnight();
|
|
$this->assertSame('00:00:00.000000', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testNoon(): void
|
|
{
|
|
$t = ChronosTime::noon();
|
|
$this->assertSame('12:00:00.000000', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testEndOfDay(): void
|
|
{
|
|
$t = ChronosTime::endOfDay();
|
|
$this->assertSame('23:59:59.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::endOfDay(microseconds: true);
|
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testSetters(): void
|
|
{
|
|
$t = ChronosTime::midnight()->setHours(24);
|
|
$this->assertSame('00:00:00.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setHours(-1);
|
|
$this->assertSame('23:00:00.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setMinutes(60);
|
|
$this->assertSame('01:00:00.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setMinutes(-1);
|
|
$this->assertSame('23:59:00.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setSeconds(60);
|
|
$this->assertSame('00:01:00.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setSeconds(-1);
|
|
$this->assertSame('23:59:59.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setMicroseconds(1_000_000);
|
|
$this->assertSame('00:00:01.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setMicroseconds(-1);
|
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setHours(25)->setMinutes(120)->setSeconds(120)->setMicroseconds(2_000_001);
|
|
$this->assertSame('03:02:02.000001', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testGetters(): void
|
|
{
|
|
$t = ChronosTime::midnight()->setTime(-1, -1, -1, -1);
|
|
$this->assertSame(22, $t->getHours());
|
|
$this->assertSame(58, $t->getMinutes());
|
|
$this->assertSame(58, $t->getSeconds());
|
|
$this->assertSame(999_999, $t->getMicroseconds());
|
|
}
|
|
|
|
public function testSetTime(): void
|
|
{
|
|
$t = new ChronosTime();
|
|
$new = $t->setTime();
|
|
$this->assertNotSame($new, $t);
|
|
$this->assertSame('00:00:00.000000', $new->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setTime(24, 120, 120, 1_000_000);
|
|
$this->assertSame('02:02:01.000000', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setTime(-1, -1, -1, -1);
|
|
$this->assertSame('22:58:58.999999', $t->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::midnight()->setTime(-1, 120, -1, 1_000_001);
|
|
$this->assertSame('01:00:00.000001', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testStartOfHour(): void
|
|
{
|
|
$t = ChronosTime::parse('12:30:45.123456');
|
|
$start = $t->startOfHour();
|
|
|
|
$this->assertNotSame($t, $start);
|
|
$this->assertSame('12:00:00.000000', $start->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::parse('00:59:59.999999');
|
|
$this->assertSame('00:00:00.000000', $t->startOfHour()->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::parse('23:01:01');
|
|
$this->assertSame('23:00:00.000000', $t->startOfHour()->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testEndOfHour(): void
|
|
{
|
|
$t = ChronosTime::parse('12:30:45.123456');
|
|
$end = $t->endOfHour();
|
|
|
|
$this->assertNotSame($t, $end);
|
|
$this->assertSame('12:59:59.999999', $end->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::parse('00:00:00');
|
|
$this->assertSame('00:59:59.999999', $t->endOfHour()->format('H:i:s.u'));
|
|
|
|
$t = ChronosTime::parse('23:30:00');
|
|
$this->assertSame('23:59:59.999999', $t->endOfHour()->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testFormat(): void
|
|
{
|
|
$t = new ChronosTime('23:59:59.999999');
|
|
$this->assertSame('23:59:59.999999', $t->format('H:i:s.u'));
|
|
}
|
|
|
|
public function testComparisons(): void
|
|
{
|
|
$t1 = ChronosTime::parse('00:00:00');
|
|
$t2 = ChronosTime::parse('00:00:00');
|
|
$this->assertTrue($t1->equals($t2));
|
|
$this->assertTrue($t1->greaterThanOrEquals($t2));
|
|
$this->assertTrue($t1->lessThanOrEquals($t2));
|
|
$this->assertFalse($t1->greaterThan($t2));
|
|
$this->assertFalse($t1->lessThan($t2));
|
|
|
|
$t1 = ChronosTime::parse('00:00:00');
|
|
$t2 = ChronosTime::parse('00:00:00')->setHours(24);
|
|
$this->assertTrue($t1->equals($t2));
|
|
$this->assertTrue($t1->greaterThanOrEquals($t2));
|
|
$this->assertTrue($t1->lessThanOrEquals($t2));
|
|
$this->assertFalse($t1->greaterThan($t2));
|
|
$this->assertFalse($t1->lessThan($t2));
|
|
|
|
$t1 = ChronosTime::parse('00:00:00');
|
|
$t2 = ChronosTime::parse('00:00:00.000001');
|
|
$this->assertTrue($t1->lessThan($t2));
|
|
$this->assertTrue($t1->lessThanOrEquals($t2));
|
|
$this->assertFalse($t1->equals($t2));
|
|
$this->assertFalse($t1->greaterThan($t2));
|
|
$this->assertFalse($t1->greaterThanOrEquals($t2));
|
|
|
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
|
$t2 = ChronosTime::parse('00:00:00');
|
|
$this->assertTrue($t1->greaterThan($t2));
|
|
$this->assertTrue($t1->greaterThanOrEquals($t2));
|
|
$this->assertFalse($t1->equals($t2));
|
|
$this->assertFalse($t1->lessThan($t2));
|
|
$this->assertFalse($t1->lessThanOrEquals($t2));
|
|
|
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
|
$this->assertTrue($t2->between($t1, $t3));
|
|
|
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
|
$this->assertTrue($t2->between($t1, $t3, false));
|
|
|
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
|
$this->assertTrue($t1->between($t1, $t3, true));
|
|
|
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
|
$this->assertFalse($t1->between($t1, $t3, false));
|
|
|
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
|
$this->assertTrue($t2->between($t3, $t1, false));
|
|
|
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
|
$this->assertFalse($t1->between($t2, $t3));
|
|
|
|
$t1 = ChronosTime::parse('00:00:00.000001');
|
|
$t2 = ChronosTime::parse('00:00:00.000002');
|
|
$t3 = ChronosTime::parse('00:00:00.000003');
|
|
$this->assertFalse($t3->between($t1, $t2));
|
|
}
|
|
|
|
public function testIsStartOfDay(): void
|
|
{
|
|
$this->assertTrue(ChronosTime::parse('00:00:00')->isStartOfDay());
|
|
$this->assertTrue(ChronosTime::midnight()->isStartOfDay());
|
|
$this->assertFalse(ChronosTime::parse('00:00:00.000001')->isStartOfDay());
|
|
$this->assertFalse(ChronosTime::parse('00:00:01')->isStartOfDay());
|
|
$this->assertFalse(ChronosTime::noon()->isStartOfDay());
|
|
}
|
|
|
|
public function testIsEndOfDay(): void
|
|
{
|
|
$this->assertTrue(ChronosTime::parse('23:59:59')->isEndOfDay());
|
|
$this->assertTrue(ChronosTime::endOfDay()->isEndOfDay());
|
|
$this->assertTrue(ChronosTime::parse('23:59:59.999999')->isEndOfDay());
|
|
$this->assertFalse(ChronosTime::parse('23:59:58')->isEndOfDay());
|
|
$this->assertFalse(ChronosTime::midnight()->isEndOfDay());
|
|
$this->assertFalse(ChronosTime::noon()->isEndOfDay());
|
|
}
|
|
|
|
public function testIsMidnight(): void
|
|
{
|
|
$this->assertTrue(ChronosTime::midnight()->isMidnight());
|
|
$this->assertTrue(ChronosTime::parse('00:00:00')->isMidnight());
|
|
$this->assertFalse(ChronosTime::parse('00:00:00.000001')->isMidnight());
|
|
$this->assertFalse(ChronosTime::noon()->isMidnight());
|
|
}
|
|
|
|
public function testIsMidday(): void
|
|
{
|
|
$this->assertTrue(ChronosTime::noon()->isMidday());
|
|
$this->assertTrue(ChronosTime::parse('12:00:00')->isMidday());
|
|
$this->assertFalse(ChronosTime::parse('12:00:00.000001')->isMidday());
|
|
$this->assertFalse(ChronosTime::parse('12:00:01')->isMidday());
|
|
$this->assertFalse(ChronosTime::midnight()->isMidday());
|
|
}
|
|
|
|
public function testToDateTimeImmutable(): void
|
|
{
|
|
$time = ChronosTime::parse('23:59:59.999999');
|
|
$native = $time->toDateTimeImmutable();
|
|
$this->assertSame('23:59:59.999999', $native->format('H:i:s.u'));
|
|
|
|
$native = $time->toNative();
|
|
$this->assertSame('23:59:59.999999', $native->format('H:i:s.u'));
|
|
|
|
$native = $time->toDateTimeImmutable('Asia/Tokyo');
|
|
$this->assertSame('23:59:59.999999', $native->format('H:i:s.u'));
|
|
$this->assertSame('Asia/Tokyo', $native->getTimezone()->getName());
|
|
|
|
$native = $time->toNative('Asia/Tokyo');
|
|
$this->assertSame('23:59:59.999999', $native->format('H:i:s.u'));
|
|
$this->assertSame('Asia/Tokyo', $native->getTimezone()->getName());
|
|
}
|
|
|
|
public function testToString(): void
|
|
{
|
|
$t = new ChronosTime('12:13:14.123456');
|
|
$this->assertSame('12:13:14', (string)$t);
|
|
|
|
ChronosTime::setToStringFormat('H:i:s.u');
|
|
$this->assertSame('12:13:14.123456', (string)$t);
|
|
|
|
ChronosTime::resetToStringFormat();
|
|
$this->assertSame('12:13:14', (string)$t);
|
|
}
|
|
|
|
public function testToArray(): void
|
|
{
|
|
$t = new ChronosTime('12:30:45.123456');
|
|
$array = $t->toArray();
|
|
|
|
$this->assertSame(12, $array['hour']);
|
|
$this->assertSame(30, $array['minute']);
|
|
$this->assertSame(45, $array['second']);
|
|
$this->assertSame(123456, $array['microsecond']);
|
|
$this->assertCount(4, $array);
|
|
}
|
|
}
|