diff --git a/src/Symfony/Component/Yaml/Inline.php b/src/Symfony/Component/Yaml/Inline.php index 2f33429910065..54da91ef48d90 100644 --- a/src/Symfony/Component/Yaml/Inline.php +++ b/src/Symfony/Component/Yaml/Inline.php @@ -110,7 +110,16 @@ public static function dump(mixed $value, int $flags = 0): string return self::dumpNull($flags); case $value instanceof \DateTimeInterface: - return $value->format('c'); + $length = \strlen(rtrim($value->format('u'), '0')); + if (0 === $length) { + $format = 'c'; + } elseif ($length < 4) { + $format = 'Y-m-d\TH:i:s.vP'; + } else { + $format = 'Y-m-d\TH:i:s.uP'; + } + + return $value->format($format); case $value instanceof \UnitEnum: return sprintf('!php/const %s::%s', $value::class, $value->name); case \is_object($value): @@ -712,15 +721,20 @@ private static function evaluateScalar(string $scalar, int $flags, array &$refer return $time; } - try { - if (false !== $scalar = $time->getTimestamp()) { - return $scalar; + $length = \strlen(rtrim($time->format('u'), '0')); + if (0 === $length) { + try { + if (false !== $scalar = $time->getTimestamp()) { + return $scalar; + } + } catch (\ValueError) { + // no-op } - } catch (\ValueError) { - // no-op + + return (int) $time->format('U'); } - return $time->format('U'); + return (float) $time->format('U.u'); } } diff --git a/src/Symfony/Component/Yaml/Tests/DumperTest.php b/src/Symfony/Component/Yaml/Tests/DumperTest.php index 47c0aa3578b29..54ffa37e77180 100644 --- a/src/Symfony/Component/Yaml/Tests/DumperTest.php +++ b/src/Symfony/Component/Yaml/Tests/DumperTest.php @@ -922,6 +922,52 @@ public function testDumpIdeographicSpaces() ], 2)); } + /** + * @dataProvider getDateTimeData + */ + public function testDumpDateTime(array $input, string $expected) + { + $this->assertSame($expected, rtrim($this->dumper->dump($input, 1))); + } + + public function getDateTimeData() + { + yield 'Date without subsecond precision' => [ + ['date' => new \DateTimeImmutable('2023-01-24T01:02:03Z')], + 'date: 2023-01-24T01:02:03+00:00', + ]; + + yield 'Date with one digit for milliseconds' => [ + ['date' => new \DateTimeImmutable('2023-01-24T01:02:03.4Z')], + 'date: 2023-01-24T01:02:03.400+00:00', + ]; + + yield 'Date with two digits for milliseconds' => [ + ['date' => new \DateTimeImmutable('2023-01-24T01:02:03.45Z')], + 'date: 2023-01-24T01:02:03.450+00:00', + ]; + + yield 'Date with full milliseconds' => [ + ['date' => new \DateTimeImmutable('2023-01-24T01:02:03.456Z')], + 'date: 2023-01-24T01:02:03.456+00:00', + ]; + + yield 'Date with four digits for microseconds' => [ + ['date' => new \DateTimeImmutable('2023-01-24T01:02:03.4567Z')], + 'date: 2023-01-24T01:02:03.456700+00:00', + ]; + + yield 'Date with five digits for microseconds' => [ + ['date' => new \DateTimeImmutable('2023-01-24T01:02:03.45678Z')], + 'date: 2023-01-24T01:02:03.456780+00:00', + ]; + + yield 'Date with full microseconds' => [ + ['date' => new \DateTimeImmutable('2023-01-24T01:02:03.456789Z')], + 'date: 2023-01-24T01:02:03.456789+00:00', + ]; + } + private function assertSameData($expected, $actual) { $this->assertEquals($expected, $actual); diff --git a/src/Symfony/Component/Yaml/Tests/InlineTest.php b/src/Symfony/Component/Yaml/Tests/InlineTest.php index 46d30f12044e5..dc21c4dde70dd 100644 --- a/src/Symfony/Component/Yaml/Tests/InlineTest.php +++ b/src/Symfony/Component/Yaml/Tests/InlineTest.php @@ -563,9 +563,10 @@ public function getTestsForDump() /** * @dataProvider getTimestampTests */ - public function testParseTimestampAsUnixTimestampByDefault(string $yaml, int $year, int $month, int $day, int $hour, int $minute, int $second) + public function testParseTimestampAsUnixTimestampByDefault(string $yaml, int $year, int $month, int $day, int $hour, int $minute, int $second, int $microsecond) { - $this->assertSame(gmmktime($hour, $minute, $second, $month, $day, $year), Inline::parse($yaml)); + $expectedDate = (new \DateTimeImmutable($yaml))->format('U'); + $this->assertSame($microsecond ? (float) "$expectedDate.$microsecond" : (int) $expectedDate, Inline::parse($yaml)); } /** diff --git a/src/Symfony/Component/Yaml/Tests/ParserTest.php b/src/Symfony/Component/Yaml/Tests/ParserTest.php index 148a139be1206..4cddeca1c733e 100644 --- a/src/Symfony/Component/Yaml/Tests/ParserTest.php +++ b/src/Symfony/Component/Yaml/Tests/ParserTest.php @@ -1541,6 +1541,15 @@ public function getInvalidBinaryData() ]; } + public function testParseDateWithSubseconds() + { + $yaml = <<<'EOT' +date: 2002-12-14T01:23:45.670000Z +EOT; + + $this->assertSameData(['date' => 1039829025.67], $this->parser->parse($yaml)); + } + public function testParseDateAsMappingValue() { $yaml = <<<'EOT'