From f0fd56aae2c838d5431f4db94bfc54baedaba638 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 26 Jul 2024 22:28:25 +0900 Subject: [PATCH 01/15] test: remove incorrect comments --- tests/system/I18n/TimeTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/system/I18n/TimeTest.php b/tests/system/I18n/TimeTest.php index 34516948ef65..f00b3a055be9 100644 --- a/tests/system/I18n/TimeTest.php +++ b/tests/system/I18n/TimeTest.php @@ -61,7 +61,7 @@ public function testNewTimeNow(): void 'en_US', IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, - 'America/Chicago', // Default for CodeIgniter + 'America/Chicago', IntlDateFormatter::GREGORIAN, 'yyyy-MM-dd HH:mm:ss' ); @@ -76,7 +76,7 @@ public function testTimeWithTimezone(): void 'en_US', IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, - 'Europe/London', // Default for CodeIgniter + 'Europe/London', IntlDateFormatter::GREGORIAN, 'yyyy-MM-dd HH:mm:ss' ); @@ -92,7 +92,7 @@ public function testTimeWithTimezoneAndLocale(): void 'fr_FR', IntlDateFormatter::SHORT, IntlDateFormatter::SHORT, - 'Europe/London', // Default for CodeIgniter + 'Europe/London', IntlDateFormatter::GREGORIAN, 'yyyy-MM-dd HH:mm:ss' ); From 8d401343ea40cbe30f4b36eb83f90f123282bfa8 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Jul 2024 11:28:23 +0900 Subject: [PATCH 02/15] test: fix typo in test method name --- tests/system/I18n/TimeTest.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/system/I18n/TimeTest.php b/tests/system/I18n/TimeTest.php index f00b3a055be9..53c58fa0ef72 100644 --- a/tests/system/I18n/TimeTest.php +++ b/tests/system/I18n/TimeTest.php @@ -875,7 +875,7 @@ public function testEqualWithString(): void $this->assertTrue($time1->equals('January 11, 2017 03:50:00', 'Europe/London')); } - public function testEqualWithStringAndNotimezone(): void + public function testEqualWithStringAndNoTimezone(): void { $time1 = Time::parse('January 10, 2017 21:50:00', 'America/Chicago'); From a37f845418d210a0074c54fc02a0a34f7f06163e Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 26 Jul 2024 22:38:39 +0900 Subject: [PATCH 03/15] test: add test for Time::createFromFormat() with microseconds --- tests/system/I18n/TimeTest.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tests/system/I18n/TimeTest.php b/tests/system/I18n/TimeTest.php index 53c58fa0ef72..fd53c93afd83 100644 --- a/tests/system/I18n/TimeTest.php +++ b/tests/system/I18n/TimeTest.php @@ -234,6 +234,13 @@ public function testCreateFromFormat(): void $this->assertCloseEnoughString(date('2017-01-15 H:i:s', $now->getTimestamp()), $time->toDateTimeString()); } + public function testCreateFromFormatWithMicroseconds(): void + { + $time = Time::createFromFormat('Y-m-d H:i:s.u', '2024-07-09 09:13:34.654321'); + + $this->assertSame('2024-07-09 09:13:34.654321', $time->format('Y-m-d H:i:s.u')); + } + public function testCreateFromFormatWithTimezoneString(): void { $time = Time::createFromFormat('F j, Y', 'January 15, 2017', 'Europe/London'); From a302f835a160cc64f0f4df8f961552de2f6c9d43 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Jul 2024 11:30:39 +0900 Subject: [PATCH 04/15] test: add test for Time::equals() with microseconds --- tests/system/I18n/TimeTest.php | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/tests/system/I18n/TimeTest.php b/tests/system/I18n/TimeTest.php index fd53c93afd83..ab5d62e7c41f 100644 --- a/tests/system/I18n/TimeTest.php +++ b/tests/system/I18n/TimeTest.php @@ -889,6 +889,14 @@ public function testEqualWithStringAndNoTimezone(): void $this->assertTrue($time1->equals('January 10, 2017 21:50:00')); } + public function testEqualWithDifferentMicroseconds(): void + { + $time1 = new Time('2024-01-01 12:00:00.654321'); + $time2 = new Time('2024-01-01 12:00:00'); + + $this->assertFalse($time1->equals($time2)); + } + public function testSameSuccess(): void { $time1 = Time::parse('January 10, 2017 21:50:00', 'America/Chicago'); From 5c3b74c545b2e86bbda600fe8477c48f9e122f03 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Jul 2024 12:53:07 +0900 Subject: [PATCH 05/15] test: add tests for Time::isBefore() and isAfter() --- tests/system/I18n/TimeTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/system/I18n/TimeTest.php b/tests/system/I18n/TimeTest.php index ab5d62e7c41f..eeaa9e019e9c 100644 --- a/tests/system/I18n/TimeTest.php +++ b/tests/system/I18n/TimeTest.php @@ -936,6 +936,15 @@ public function testBefore(): void $this->assertFalse($time2->isBefore($time1)); } + public function testBeforeWithMicroseconds(): void + { + $time1 = new Time('2024-01-01 12:00:00.000000'); + $time2 = new Time('2024-01-01 12:00:00.654321'); + + $this->assertTrue($time1->isBefore($time2)); + $this->assertFalse($time2->isBefore($time1)); + } + public function testAfter(): void { $time1 = Time::parse('January 10, 2017 21:50:00', 'America/Chicago'); @@ -945,6 +954,15 @@ public function testAfter(): void $this->assertTrue($time2->isAfter($time1)); } + public function testAfterWithMicroseconds(): void + { + $time1 = new Time('2024-01-01 12:00:00.654321'); + $time2 = new Time('2024-01-01 12:00:00.000000'); + + $this->assertTrue($time1->isAfter($time2)); + $this->assertFalse($time2->isAfter($time1)); + } + public function testHumanizeYearsSingle(): void { Time::setTestNow('March 10, 2017', 'America/Chicago'); From d4cf9bc20f98667d6e24e4a051a8d98bf621b288 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Jul 2024 13:12:14 +0900 Subject: [PATCH 06/15] test: add tests for Time::isBefore() and isAfter() with same time --- tests/system/I18n/TimeTest.php | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/tests/system/I18n/TimeTest.php b/tests/system/I18n/TimeTest.php index eeaa9e019e9c..0e5bc733407c 100644 --- a/tests/system/I18n/TimeTest.php +++ b/tests/system/I18n/TimeTest.php @@ -936,6 +936,15 @@ public function testBefore(): void $this->assertFalse($time2->isBefore($time1)); } + public function testBeforeSameTime(): void + { + $time1 = new Time('2024-01-01 12:00:00.000000'); + $time2 = new Time('2024-01-01 12:00:00.000000'); + + $this->assertFalse($time1->isBefore($time2)); + $this->assertFalse($time2->isBefore($time1)); + } + public function testBeforeWithMicroseconds(): void { $time1 = new Time('2024-01-01 12:00:00.000000'); @@ -954,6 +963,15 @@ public function testAfter(): void $this->assertTrue($time2->isAfter($time1)); } + public function testAfterSameTime(): void + { + $time1 = new Time('2024-01-01 12:00:00.000000'); + $time2 = new Time('2024-01-01 12:00:00.000000'); + + $this->assertFalse($time1->isAfter($time2)); + $this->assertFalse($time2->isAfter($time1)); + } + public function testAfterWithMicroseconds(): void { $time1 = new Time('2024-01-01 12:00:00.654321'); From 6b32d5838d22e1266978d18ecf02e149e2cc7768 Mon Sep 17 00:00:00 2001 From: kenjis Date: Fri, 26 Jul 2024 22:39:48 +0900 Subject: [PATCH 07/15] fix: Time loses microseconds --- system/I18n/TimeTrait.php | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/system/I18n/TimeTrait.php b/system/I18n/TimeTrait.php index fefb2fc544a3..c73ca5bd7368 100644 --- a/system/I18n/TimeTrait.php +++ b/system/I18n/TimeTrait.php @@ -81,10 +81,10 @@ public function __construct(?string $time = null, $timezone = null, ?string $loc if ($time === '' && static::$testNow instanceof self) { if ($timezone !== null) { $testNow = static::$testNow->setTimezone($timezone); - $time = $testNow->format('Y-m-d H:i:s'); + $time = $testNow->format('Y-m-d H:i:s.u'); } else { $timezone = static::$testNow->getTimezone(); - $time = static::$testNow->format('Y-m-d H:i:s'); + $time = static::$testNow->format('Y-m-d H:i:s.u'); } } @@ -97,7 +97,7 @@ public function __construct(?string $time = null, $timezone = null, ?string $loc if ($time !== '' && static::hasRelativeKeywords($time)) { $instance = new DateTime('now', $this->timezone); $instance->modify($time); - $time = $instance->format('Y-m-d H:i:s'); + $time = $instance->format('Y-m-d H:i:s.u'); } parent::__construct($time, $this->timezone); @@ -253,7 +253,7 @@ public static function createFromFormat($format, $datetime, $timezone = null) throw I18nException::forInvalidFormat($format); } - return new self($date->format('Y-m-d H:i:s'), $timezone); + return new self($date->format('Y-m-d H:i:s.u'), $timezone); } /** @@ -283,7 +283,7 @@ public static function createFromTimestamp(int $timestamp, $timezone = null, ?st */ public static function createFromInstance(DateTimeInterface $dateTime, ?string $locale = null) { - $date = $dateTime->format('Y-m-d H:i:s'); + $date = $dateTime->format('Y-m-d H:i:s.u'); $timezone = $dateTime->getTimezone(); return new self($date, $timezone, $locale); @@ -348,7 +348,7 @@ public static function setTestNow($datetime = null, $timezone = null, ?string $l if (is_string($datetime)) { $datetime = new self($datetime, $timezone, $locale); } elseif ($datetime instanceof DateTimeInterface && ! $datetime instanceof self) { - $datetime = new self($datetime->format('Y-m-d H:i:s'), $timezone); + $datetime = new self($datetime->format('Y-m-d H:i:s.u'), $timezone); } static::$testNow = $datetime; @@ -941,9 +941,9 @@ public function equals($testTime, ?string $timezone = null): bool $ourTime = $this->toDateTime() ->setTimezone(new DateTimeZone('UTC')) - ->format('Y-m-d H:i:s'); + ->format('Y-m-d H:i:s.u'); - return $testTime->format('Y-m-d H:i:s') === $ourTime; + return $testTime->format('Y-m-d H:i:s.u') === $ourTime; } /** @@ -956,15 +956,15 @@ public function equals($testTime, ?string $timezone = null): bool public function sameAs($testTime, ?string $timezone = null): bool { if ($testTime instanceof DateTimeInterface) { - $testTime = $testTime->format('Y-m-d H:i:s'); + $testTime = $testTime->format('Y-m-d H:i:s.u O'); } elseif (is_string($testTime)) { $timezone = $timezone ?: $this->timezone; $timezone = $timezone instanceof DateTimeZone ? $timezone : new DateTimeZone($timezone); $testTime = new DateTime($testTime, $timezone); - $testTime = $testTime->format('Y-m-d H:i:s'); + $testTime = $testTime->format('Y-m-d H:i:s.u O'); } - $ourTime = $this->toDateTimeString(); + $ourTime = $this->format('Y-m-d H:i:s.u O'); return $testTime === $ourTime; } From b193a62e4c8858c7c22536b4eaa0d54c247d9e41 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Jul 2024 11:14:48 +0900 Subject: [PATCH 08/15] fix: Time::toDateTime() loses microseconds --- system/I18n/TimeTrait.php | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/system/I18n/TimeTrait.php b/system/I18n/TimeTrait.php index c73ca5bd7368..07028d661b3d 100644 --- a/system/I18n/TimeTrait.php +++ b/system/I18n/TimeTrait.php @@ -314,10 +314,11 @@ public static function instance(DateTime $dateTime, ?string $locale = null) */ public function toDateTime() { - $dateTime = new DateTime('', $this->getTimezone()); - $dateTime->setTimestamp(parent::getTimestamp()); - - return $dateTime; + return DateTime::createFromFormat( + 'Y-m-d H:i:s.u', + $this->format('Y-m-d H:i:s.u'), + $this->getTimezone() + ); } // -------------------------------------------------------------------- From f3b15c03a61660123305742fd046657c0802af69 Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Jul 2024 12:54:04 +0900 Subject: [PATCH 09/15] fix: Time::isBefore() and isAfter() loses microseconds --- system/I18n/TimeTrait.php | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/system/I18n/TimeTrait.php b/system/I18n/TimeTrait.php index 07028d661b3d..3257ad06189f 100644 --- a/system/I18n/TimeTrait.php +++ b/system/I18n/TimeTrait.php @@ -980,10 +980,16 @@ public function sameAs($testTime, ?string $timezone = null): bool */ public function isBefore($testTime, ?string $timezone = null): bool { - $testTime = $this->getUTCObject($testTime, $timezone)->getTimestamp(); - $ourTime = $this->getTimestamp(); + $testTime = $this->getUTCObject($testTime, $timezone); + + $testTimestamp = $testTime->getTimestamp(); + $ourTimestamp = $this->getTimestamp(); + + if ($ourTimestamp === $testTimestamp) { + return $this->format('u') < $testTime->format('u'); + } - return $ourTime < $testTime; + return $ourTimestamp < $testTimestamp; } /** @@ -996,10 +1002,16 @@ public function isBefore($testTime, ?string $timezone = null): bool */ public function isAfter($testTime, ?string $timezone = null): bool { - $testTime = $this->getUTCObject($testTime, $timezone)->getTimestamp(); - $ourTime = $this->getTimestamp(); + $testTime = $this->getUTCObject($testTime, $timezone); + + $testTimestamp = $testTime->getTimestamp(); + $ourTimestamp = $this->getTimestamp(); + + if ($ourTimestamp === $testTimestamp) { + return $this->format('u') > $testTime->format('u'); + } - return $ourTime > $testTime; + return $ourTimestamp > $testTimestamp; } // -------------------------------------------------------------------- From 4932e44f098a6f90ad01f6f40c7b24bf6cce1bad Mon Sep 17 00:00:00 2001 From: kenjis Date: Sat, 27 Jul 2024 07:21:36 +0900 Subject: [PATCH 10/15] docs: add docs --- user_guide_src/source/changelogs/v4.6.0.rst | 6 ++++ .../source/installation/upgrade_460.rst | 36 +++++++++++++++++++ .../source/installation/upgrade_460/002.php | 8 +++++ .../source/installation/upgrade_460/003.php | 8 +++++ .../source/installation/upgrade_460/004.php | 8 +++++ .../source/installation/upgrade_460/005.php | 9 +++++ .../source/installation/upgrade_460/006.php | 10 ++++++ .../source/installation/upgrade_460/007.php | 17 +++++++++ 8 files changed, 102 insertions(+) create mode 100644 user_guide_src/source/installation/upgrade_460/002.php create mode 100644 user_guide_src/source/installation/upgrade_460/003.php create mode 100644 user_guide_src/source/installation/upgrade_460/004.php create mode 100644 user_guide_src/source/installation/upgrade_460/005.php create mode 100644 user_guide_src/source/installation/upgrade_460/006.php create mode 100644 user_guide_src/source/installation/upgrade_460/007.php diff --git a/user_guide_src/source/changelogs/v4.6.0.rst b/user_guide_src/source/changelogs/v4.6.0.rst index 8872615a3fc6..e001f5c9d4c2 100644 --- a/user_guide_src/source/changelogs/v4.6.0.rst +++ b/user_guide_src/source/changelogs/v4.6.0.rst @@ -59,6 +59,12 @@ executed twice, an exception will be thrown. See .. _v460-interface-changes: +Time with Microseconds +---------------------- + +Fixed bugs that some methods in ``Time`` to lose microseconds have been fixed. +See :ref:`Upgrading Guide ` for details. + Interface Changes ================= diff --git a/user_guide_src/source/installation/upgrade_460.rst b/user_guide_src/source/installation/upgrade_460.rst index 5eccc23abd05..7078110b7068 100644 --- a/user_guide_src/source/installation/upgrade_460.rst +++ b/user_guide_src/source/installation/upgrade_460.rst @@ -29,6 +29,42 @@ See :ref:`ChangeLog ` for details. If you have code that catches these exceptions, change the exception classes. +.. _upgrade-460-time-keeps-microseconds: + +Time keeps Microseconds +======================= + +In previous versions, :doc:`Time <../libraries/time>` lost microseconds in some +cases. But the bugs have been fixed. + +The results of the ``Time`` comparison may differ due to these fixes: + +.. literalinclude:: upgrade_460/006.php + :lines: 2- + +In a such case, you need to remove the microseconds: + +.. literalinclude:: upgrade_460/007.php + :lines: 2- + +The following cases now keeps microseconds: + +.. literalinclude:: upgrade_460/002.php + :lines: 2- + +.. literalinclude:: upgrade_460/003.php + :lines: 2- + +Note that ``Time`` with the current time has been holding microseconds since before. + +.. literalinclude:: upgrade_460/004.php + :lines: 2- + +Also, methods that returns an ``int`` still lose the microseconds. + +.. literalinclude:: upgrade_460/005.php + :lines: 2- + .. _upgrade-460-registrars-with-dirty-hack: Registrars with Dirty Hack diff --git a/user_guide_src/source/installation/upgrade_460/002.php b/user_guide_src/source/installation/upgrade_460/002.php new file mode 100644 index 000000000000..91747d7b5464 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_460/002.php @@ -0,0 +1,8 @@ +format('Y-m-d H:i:s.u'); +// Before: 2024-07-09 09:13:34.000000 +// After: 2024-07-09 09:13:34.654321 diff --git a/user_guide_src/source/installation/upgrade_460/003.php b/user_guide_src/source/installation/upgrade_460/003.php new file mode 100644 index 000000000000..65914ece9c7c --- /dev/null +++ b/user_guide_src/source/installation/upgrade_460/003.php @@ -0,0 +1,8 @@ +format('Y-m-d H:i:s.u'); +// Before: 2024-07-26 21:05:57.000000 +// After: 2024-07-26 21:05:57.857235 diff --git a/user_guide_src/source/installation/upgrade_460/004.php b/user_guide_src/source/installation/upgrade_460/004.php new file mode 100644 index 000000000000..dab6489fd68b --- /dev/null +++ b/user_guide_src/source/installation/upgrade_460/004.php @@ -0,0 +1,8 @@ +format('Y-m-d H:i:s.u'); +// Before: 2024-07-26 21:39:32.249072 +// After: 2024-07-26 21:39:32.249072 diff --git a/user_guide_src/source/installation/upgrade_460/005.php b/user_guide_src/source/installation/upgrade_460/005.php new file mode 100644 index 000000000000..a5e80c09b778 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_460/005.php @@ -0,0 +1,9 @@ +getTimestamp(); // 1704110400 + +$time2 = new Time('2024-01-01 12:00:00.654321'); +echo $time2->getTimestamp(); // 1704110400 diff --git a/user_guide_src/source/installation/upgrade_460/006.php b/user_guide_src/source/installation/upgrade_460/006.php new file mode 100644 index 000000000000..316d802ada48 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_460/006.php @@ -0,0 +1,10 @@ +equals($time2); +// Before: true +// After: false diff --git a/user_guide_src/source/installation/upgrade_460/007.php b/user_guide_src/source/installation/upgrade_460/007.php new file mode 100644 index 000000000000..22cadc8c0773 --- /dev/null +++ b/user_guide_src/source/installation/upgrade_460/007.php @@ -0,0 +1,17 @@ +format('Y-m-d H:i:s'), + $time1->getTimezone() +); + +$time1->equals($time2); +// Before: true +// After: true From 7b0d8c3ec1942f59a42f4c87f9804c2891427c01 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Jul 2024 10:29:55 +0900 Subject: [PATCH 11/15] test: add test for DataConverter to convert datetime to db with microseconds --- .../system/DataConverter/DataConverterTest.php | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/tests/system/DataConverter/DataConverterTest.php b/tests/system/DataConverter/DataConverterTest.php index 6592f4d801c6..f56a18d10e48 100644 --- a/tests/system/DataConverter/DataConverterTest.php +++ b/tests/system/DataConverter/DataConverterTest.php @@ -379,6 +379,23 @@ public function testDateTimeConvertDataToDB(): void $this->assertSame('2023-11-18 14:18:18', $data['date']); } + public function testDateTimeConvertDataToDBWithFormat(): void + { + $types = [ + 'id' => 'int', + 'date' => 'datetime[us]', + ]; + $converter = $this->createDataConverter($types, [], db_connect()); + + $phpData = [ + 'id' => '1', + 'date' => Time::parse('2009-02-15 00:00:01.123456'), + ]; + $data = $converter->toDataSource($phpData); + + $this->assertSame('2009-02-15 00:00:01.123456', $data['date']); + } + public function testTimestampConvertDataFromDB(): void { $types = [ From 3c69a6133586da2a27b66c3334bacf6bc0825e4e Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Jul 2024 10:32:01 +0900 Subject: [PATCH 12/15] fix: DatetimeCast::set() loses microseconds --- system/DataCaster/Cast/DatetimeCast.php | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/system/DataCaster/Cast/DatetimeCast.php b/system/DataCaster/Cast/DatetimeCast.php index b6fa97f9ddda..22fbcc6171db 100644 --- a/system/DataCaster/Cast/DatetimeCast.php +++ b/system/DataCaster/Cast/DatetimeCast.php @@ -62,6 +62,19 @@ public static function set( self::invalidTypeValueError($value); } - return (string) $value; + if (! $helper instanceof BaseConnection) { + $message = 'The parameter $helper must be BaseConnection.'; + + throw new InvalidArgumentException($message); + } + + $format = match ($params[0] ?? '') { + '' => $helper->dateFormat['datetime'], + 'ms' => $helper->dateFormat['datetime-ms'], + 'us' => $helper->dateFormat['datetime-us'], + default => throw new InvalidArgumentException('Invalid parameter: ' . $params[0]), + }; + + return $value->format($format); } } From 94adfc55cfae99e152c7e4d943ca03820551cc66 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Jul 2024 10:45:55 +0900 Subject: [PATCH 13/15] test: update test code --- tests/system/DataConverter/DataConverterTest.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/tests/system/DataConverter/DataConverterTest.php b/tests/system/DataConverter/DataConverterTest.php index f56a18d10e48..53a6361bb0de 100644 --- a/tests/system/DataConverter/DataConverterTest.php +++ b/tests/system/DataConverter/DataConverterTest.php @@ -368,7 +368,7 @@ public function testDateTimeConvertDataToDB(): void 'id' => 'int', 'date' => 'datetime', ]; - $converter = $this->createDataConverter($types); + $converter = $this->createDataConverter($types, [], db_connect()); $phpData = [ 'id' => '1', @@ -620,7 +620,7 @@ public function testExtract(): void 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; - $converter = $this->createDataConverter($types); + $converter = $this->createDataConverter($types, [], db_connect()); $phpData = [ 'id' => 1, @@ -652,7 +652,7 @@ public function testExtractWithExtractMethod(): void 'created_at' => 'datetime', 'updated_at' => 'datetime', ]; - $converter = $this->createDataConverter($types, [], null, 'toRawArray'); + $converter = $this->createDataConverter($types, [], db_connect(), 'toRawArray'); $phpData = [ 'id' => 1, From 9ad1748a4cd71b3d6373b4995e0ae3f060a0cd4e Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Jul 2024 10:46:58 +0900 Subject: [PATCH 14/15] refactor: extract getDateTimeFormat() method --- system/DataCaster/Cast/DatetimeCast.php | 29 +++++++++++++++---------- 1 file changed, 17 insertions(+), 12 deletions(-) diff --git a/system/DataCaster/Cast/DatetimeCast.php b/system/DataCaster/Cast/DatetimeCast.php index 22fbcc6171db..25f1e5fad448 100644 --- a/system/DataCaster/Cast/DatetimeCast.php +++ b/system/DataCaster/Cast/DatetimeCast.php @@ -43,12 +43,7 @@ public static function get( /** * @see https://www.php.net/manual/en/datetimeimmutable.createfromformat.php#datetimeimmutable.createfromformat.parameters */ - $format = match ($params[0] ?? '') { - '' => $helper->dateFormat['datetime'], - 'ms' => $helper->dateFormat['datetime-ms'], - 'us' => $helper->dateFormat['datetime-us'], - default => throw new InvalidArgumentException('Invalid parameter: ' . $params[0]), - }; + $format = self::getDateTimeFormat($params, $helper); return Time::createFromFormat($format, $value); } @@ -68,13 +63,23 @@ public static function set( throw new InvalidArgumentException($message); } - $format = match ($params[0] ?? '') { - '' => $helper->dateFormat['datetime'], - 'ms' => $helper->dateFormat['datetime-ms'], - 'us' => $helper->dateFormat['datetime-us'], - default => throw new InvalidArgumentException('Invalid parameter: ' . $params[0]), - }; + $format = self::getDateTimeFormat($params, $helper); return $value->format($format); } + + /** + * Gets DateTime format from the DB connection. + * + * @param list $params Additional param + */ + protected static function getDateTimeFormat(array $params, BaseConnection $db): string + { + return match ($params[0] ?? '') { + '' => $db->dateFormat['datetime'], + 'ms' => $db->dateFormat['datetime-ms'], + 'us' => $db->dateFormat['datetime-us'], + default => throw new InvalidArgumentException('Invalid parameter: ' . $params[0]), + }; + } } From ed29b50c7a78ed4fe3c37c1a994e2bee6d554d14 Mon Sep 17 00:00:00 2001 From: kenjis Date: Mon, 29 Jul 2024 11:51:54 +0900 Subject: [PATCH 15/15] docs: add notes for datetime ms/us in model and query builder --- user_guide_src/source/models/model.rst | 12 ++++++++++++ user_guide_src/source/models/model/063.php | 13 +++++++++++++ 2 files changed, 25 insertions(+) create mode 100644 user_guide_src/source/models/model/063.php diff --git a/user_guide_src/source/models/model.rst b/user_guide_src/source/models/model.rst index c77e68cb81b8..5d9bb6ebdb4e 100644 --- a/user_guide_src/source/models/model.rst +++ b/user_guide_src/source/models/model.rst @@ -395,6 +395,18 @@ The datetime format is set in the ``dateFormat`` array of the :ref:`database configuration ` in the **app/Config/Database.php** file. +.. note:: + When you set ``ms`` or ``us`` as a parameter, **Model** takes care of second's + fractional part of the Time. But **Query Builder** does not. So you still need + to use the ``format()`` method when you pass the Time to Query Builder's methods + like ``where()``: + + .. literalinclude:: model/063.php + :lines: 2- + +.. note:: Prior to v4.6.0, you cannot use ``ms`` or ``us`` as a parameter. + Because the second's fractional part of Time was lost due to bugs. + Custom Casting ============== diff --git a/user_guide_src/source/models/model/063.php b/user_guide_src/source/models/model/063.php new file mode 100644 index 000000000000..cc75d0648421 --- /dev/null +++ b/user_guide_src/source/models/model/063.php @@ -0,0 +1,13 @@ +where('my_dt_field', $now->format('Y-m-d H:i:s.u'))->findAll(); +// Generates: SELECT * FROM `my_table` WHERE `my_dt_field` = '2024-07-28 18:57:58.900326' + +// But the following code loses the microseconds. +$model->where('my_dt_field', $now)->findAll(); +// Generates: SELECT * FROM `my_table` WHERE `my_dt_field` = '2024-07-28 18:57:58'