From 8424509f82a0258ba162f0f1e69b75da9b8b461c Mon Sep 17 00:00:00 2001 From: Dorian Villet Date: Fri, 29 Sep 2023 14:10:47 +0200 Subject: [PATCH] Improve performances for LocalDate, LocalTime, MonthDay, YearMonth and YearWeek's __toString() methods. And slight improvement to Duration's __toString() method. --- src/Duration.php | 4 ++-- src/LocalDate.php | 20 ++++++++++++++++---- src/LocalTime.php | 20 ++++++++------------ src/MonthDay.php | 8 +++++--- src/YearMonth.php | 18 ++++++++++++++---- src/YearWeek.php | 18 ++++++++++++++---- tests/LocalDateTest.php | 16 +++++++++++++++- tests/LocalTimeTest.php | 4 ++++ tests/YearMonthTest.php | 4 ++-- 9 files changed, 80 insertions(+), 32 deletions(-) diff --git a/src/Duration.php b/src/Duration.php index 6f6d8d8..9ca2afc 100644 --- a/src/Duration.php +++ b/src/Duration.php @@ -13,9 +13,9 @@ use function is_int; use function preg_match; use function rtrim; -use function sprintf; use function str_pad; +use const STR_PAD_LEFT; use const STR_PAD_RIGHT; /** @@ -808,7 +808,7 @@ public function toISOString(): string $string .= (($seconds === 0 && $negative) ? '-0' : $seconds); if ($nanos !== 0) { - $string .= '.' . rtrim(sprintf('%09d', $nanos), '0'); + $string .= '.' . rtrim(str_pad((string) $nanos, 9, '0', STR_PAD_LEFT), '0'); } return $string . 'S'; diff --git a/src/LocalDate.php b/src/LocalDate.php index 335fed5..96ff664 100644 --- a/src/LocalDate.php +++ b/src/LocalDate.php @@ -16,7 +16,9 @@ use function intdiv; use function min; -use function sprintf; +use function str_pad; + +use const STR_PAD_LEFT; /** * A date without a time-zone in the ISO-8601 calendar system, such as `2007-12-03`. @@ -759,9 +761,19 @@ public function jsonSerialize(): string */ public function toISOString(): string { - $pattern = ($this->year < 0 ? '%05d' : '%04d') . '-%02d-%02d'; - - return sprintf($pattern, $this->year, $this->month, $this->day); + // This code is optimized for high performance + return ($this->year < 1000 && $this->year > -1000 + ? ( + $this->year < 0 + ? '-' . str_pad((string) -$this->year, 4, '0', STR_PAD_LEFT) + : str_pad((string) $this->year, 4, '0', STR_PAD_LEFT) + ) + : $this->year + ) + . '-' + . ($this->month < 10 ? '0' . $this->month : $this->month) + . '-' + . ($this->day < 10 ? '0' . $this->day : $this->day); } /** diff --git a/src/LocalTime.php b/src/LocalTime.php index 663c248..a033401 100644 --- a/src/LocalTime.php +++ b/src/LocalTime.php @@ -19,9 +19,10 @@ use function intdiv; use function rtrim; -use function sprintf; use function str_pad; +use const STR_PAD_LEFT; + /** * A time without a time-zone in the ISO-8601 calendar system, such as 10:15:30. * @@ -662,17 +663,12 @@ public function jsonSerialize(): string */ public function toISOString(): string { - if ($this->nano === 0) { - if ($this->second === 0) { - return sprintf('%02u:%02u', $this->hour, $this->minute); - } else { - return sprintf('%02u:%02u:%02u', $this->hour, $this->minute, $this->second); - } - } - - $nanos = rtrim(sprintf('%09u', $this->nano), '0'); - - return sprintf('%02u:%02u:%02u.%s', $this->hour, $this->minute, $this->second, $nanos); + // This code is optimized for high performance + return ($this->hour < 10 ? '0' . $this->hour : $this->hour) + . ':' + . ($this->minute < 10 ? '0' . $this->minute : $this->minute) + . ($this->second !== 0 || $this->nano !== 0 ? ':' . ($this->second < 10 ? '0' . $this->second : $this->second) : '') + . ($this->nano !== 0 ? '.' . rtrim(str_pad((string) $this->nano, 9, '0', STR_PAD_LEFT), '0') : ''); } /** diff --git a/src/MonthDay.php b/src/MonthDay.php index 52e5102..cfb4d45 100644 --- a/src/MonthDay.php +++ b/src/MonthDay.php @@ -10,8 +10,6 @@ use Brick\DateTime\Parser\IsoParsers; use JsonSerializable; -use function sprintf; - /** * A month-day in the ISO-8601 calendar system, such as `--12-03`. */ @@ -238,7 +236,11 @@ public function jsonSerialize(): string */ public function toISOString(): string { - return sprintf('--%02d-%02d', $this->month, $this->day); + // This code is optimized for high performance + return '--' + . ($this->month < 10 ? '0' . $this->month : $this->month) + . '-' + . ($this->day < 10 ? '0' . $this->day : $this->day); } /** diff --git a/src/YearMonth.php b/src/YearMonth.php index c3d10d3..15d25ca 100644 --- a/src/YearMonth.php +++ b/src/YearMonth.php @@ -11,7 +11,9 @@ use Brick\DateTime\Utility\Math; use JsonSerializable; -use function sprintf; +use function str_pad; + +use const STR_PAD_LEFT; /** * Represents the combination of a year and a month. @@ -300,9 +302,17 @@ public function jsonSerialize(): string */ public function toISOString(): string { - $pattern = ($this->year < 0 ? '%05d' : '%04d') . '-%02d'; - - return sprintf($pattern, $this->year, $this->month); + // This code is optimized for high performance + return ($this->year < 1000 && $this->year > -1000 + ? ( + $this->year < 0 + ? '-' . str_pad((string) -$this->year, 4, '0', STR_PAD_LEFT) + : str_pad((string) $this->year, 4, '0', STR_PAD_LEFT) + ) + : $this->year + ) + . '-' + . ($this->month < 10 ? '0' . $this->month : $this->month); } /** diff --git a/src/YearWeek.php b/src/YearWeek.php index e9c93ed..fd0838c 100644 --- a/src/YearWeek.php +++ b/src/YearWeek.php @@ -10,7 +10,9 @@ use Brick\DateTime\Parser\IsoParsers; use JsonSerializable; -use function sprintf; +use function str_pad; + +use const STR_PAD_LEFT; /** * Represents the combination of a year and a week. @@ -308,9 +310,17 @@ public function jsonSerialize(): string */ public function toISOString(): string { - $pattern = ($this->year < 0 ? '%05d' : '%04d') . '-W%02d'; - - return sprintf($pattern, $this->year, $this->week); + // This code is optimized for high performance + return ($this->year < 1000 && $this->year > -1000 + ? ( + $this->year < 0 + ? '-' . str_pad((string) -$this->year, 4, '0', STR_PAD_LEFT) + : str_pad((string) $this->year, 4, '0', STR_PAD_LEFT) + ) + : $this->year + ) + . '-W' + . ($this->week < 10 ? '0' . $this->week : $this->week); } /** diff --git a/tests/LocalDateTest.php b/tests/LocalDateTest.php index 190c9fc..ed65c9f 100644 --- a/tests/LocalDateTest.php +++ b/tests/LocalDateTest.php @@ -1402,8 +1402,22 @@ public function testToString(int $year, int $month, int $day, string $expected): public function providerToString(): array { return [ - [999, 1, 2, '0999-01-02'], + [-999999, 12, 31, '-999999-12-31'], + [-185321, 11, 2, '-185321-11-02'], + [-18532, 11, 2, '-18532-11-02'], + [-2023, 11, 28, '-2023-11-28'], + [-2023, 11, 2, '-2023-11-02'], + [-2023, 1, 2, '-2023-01-02'], + [-999, 1, 2, '-0999-01-02'], [-2, 1, 1, '-0002-01-01'], + [2, 1, 1, '0002-01-01'], + [999, 1, 2, '0999-01-02'], + [2023, 1, 2, '2023-01-02'], + [2023, 11, 2, '2023-11-02'], + [2023, 11, 28, '2023-11-28'], + [18532, 11, 2, '18532-11-02'], + [185321, 11, 2, '185321-11-02'], + [999999, 12, 31, '999999-12-31'], ]; } diff --git a/tests/LocalTimeTest.php b/tests/LocalTimeTest.php index c0ad9e7..5506175 100644 --- a/tests/LocalTimeTest.php +++ b/tests/LocalTimeTest.php @@ -1076,6 +1076,10 @@ public function testToString(int $h, int $m, int $s, int $n, string $r): void public function providerToString(): array { return [ + [0, 0, 0, 0, '00:00'], + [0, 1, 0, 0, '00:01'], + [0, 0, 1, 0, '00:00:01'], + [0, 0, 0, 1, '00:00:00.000000001'], [1, 2, 0, 0, '01:02'], [1, 2, 3, 0, '01:02:03'], [1, 2, 3, 4, '01:02:03.000000004'], diff --git a/tests/YearMonthTest.php b/tests/YearMonthTest.php index 1464211..981dcec 100644 --- a/tests/YearMonthTest.php +++ b/tests/YearMonthTest.php @@ -462,9 +462,9 @@ public function providerToLocalDateRange(): array /** * @dataProvider providerToString */ - public function testJsonSerialize(int $year, int $week, string $expectedString): void + public function testJsonSerialize(int $year, int $month, string $expectedString): void { - self::assertSame(json_encode($expectedString), json_encode(YearMonth::of($year, $week))); + self::assertSame(json_encode($expectedString), json_encode(YearMonth::of($year, $month))); } /**