diff --git a/src/Format.php b/src/Format.php index b1bda62..688f74d 100644 --- a/src/Format.php +++ b/src/Format.php @@ -19,7 +19,6 @@ use DateTime; use DateTimeInterface; -use Horde\Date\Formatter\DateTimeFormatter; use Horde\Date\Formatter\IcuFormatter; use IntlDateFormatter; use InvalidArgumentException; @@ -310,11 +309,14 @@ public static function isStrftimeFormat(string $format): bool /** * Parse a formatted date string to a DateInterface object * - * Detects the pattern type (strftime, ICU, or PHP date()) and dispatches - * to the appropriate formatter's parse method. + * Auto-detects strftime patterns (containing %) and converts them to ICU. + * All other patterns are treated as ICU (including shortcuts like short, + * medium, long, full). + * + * For PHP date() patterns, use DateTimeFormatter::parse() directly. * * @param string $formattedString The date string to parse - * @param string $pattern Format pattern (strftime, ICU, or PHP date() syntax) + * @param string $pattern Format pattern (strftime or ICU syntax) * @param string|Stringable $locale Locale for parsing (default: 'en_US') * @param string|null $timezone Timezone identifier (null = UTC) * @@ -336,12 +338,6 @@ public static function parse( return $formatter->parse($formattedString, $icuPattern, $locale, $timezone); } - if (self::isPhpDateFormat($pattern)) { - $formatter = new DateTimeFormatter(); - return $formatter->parse($formattedString, $pattern, $locale, $timezone); - } - - // Default: treat as ICU pattern $formatter = new IcuFormatter(); return $formatter->parse($formattedString, $pattern, $locale, $timezone); } @@ -353,9 +349,13 @@ public static function parse( * the string atomically. This avoids regex-based string splitting which * breaks with AM/PM markers and other multi-word tokens. * + * PHP date() patterns are not auto-detected due to ambiguity with ICU + * single-letter patterns. Use DateTimeFormatter::parse() directly for + * PHP date() syntax. + * * @param string $formattedString The date+time string to parse - * @param string $datePattern Date format pattern (strftime, ICU, or PHP date()) - * @param string $timePattern Time format pattern (strftime, ICU, or PHP date()) + * @param string $datePattern Date format pattern (strftime or ICU) + * @param string $timePattern Time format pattern (strftime or ICU) * @param string|Stringable $locale Locale for parsing (default: 'en_US') * @param string|null $timezone Timezone identifier (null = UTC) * @@ -388,38 +388,6 @@ public static function parseDateTime( return $formatter->parse($formattedString, $combinedPattern, $locale, $timezone); } - /** - * Detect if a pattern uses PHP date() syntax (single letters like Y, m, d, H, i, s) - * - * Distinguishes from ICU patterns which use repeated letters (yyyy, MM, dd). - * A pattern is considered PHP date() if it contains characteristic PHP date - * letters that do not appear in ICU patterns as single characters. - * - * @param string $pattern Pattern to check - * @return bool True if the pattern appears to be PHP date() syntax - */ - public static function isPhpDateFormat(string $pattern): bool - { - // These characters are unique to PHP date() and don't appear as single - // letters in ICU patterns in the same way - $phpOnlyChars = ['i', 'j', 'n', 'g', 'A', 'N', 'L', 'o', 'U', 'u']; - foreach ($phpOnlyChars as $char) { - if (str_contains($pattern, $char)) { - return true; - } - } - - // Single Y/m/d/H/s without repetition is PHP style - // ICU uses yyyy, MM, dd, HH, ss (repeated) - if (preg_match('/(? * @category Horde * @copyright 2026 The Horde Project diff --git a/test/Unit/FormatParseTest.php b/test/Unit/FormatParseTest.php index 9f4175b..a6842ab 100644 --- a/test/Unit/FormatParseTest.php +++ b/test/Unit/FormatParseTest.php @@ -51,17 +51,6 @@ public function testParseStrftimePattern(): void $this->assertSame('22', $dt->format('d')); } - public function testParsePhpDatePattern(): void - { - $result = Format::parse('2026-05-22', 'Y-m-d'); - - $this->assertInstanceOf(DateInterface::class, $result); - $dt = $result->toDateTimeImmutable(); - $this->assertSame('2026', $dt->format('Y')); - $this->assertSame('05', $dt->format('m')); - $this->assertSame('22', $dt->format('d')); - } - public function testParseWithTime24h(): void { $result = Format::parse('22.05.2026 14:30', 'dd.MM.yyyy HH:mm', 'de_DE'); @@ -168,27 +157,14 @@ public function testParseWithTimezone(): void $this->assertSame('30', $dt->format('i')); } - public function testIsPhpDateFormatDetectsPhpPatterns(): void + public function testParseIcuShortcutPattern(): void { - $this->assertTrue(Format::isPhpDateFormat('Y-m-d')); - $this->assertTrue(Format::isPhpDateFormat('Y-m-d H:i:s')); - $this->assertTrue(Format::isPhpDateFormat('d/m/Y')); - $this->assertTrue(Format::isPhpDateFormat('j.n.Y')); - } + $result = Format::parse('29.05.26', 'short', 'de_DE'); - public function testIsPhpDateFormatRejectsIcuPatterns(): void - { - $this->assertFalse(Format::isPhpDateFormat('dd.MM.yyyy')); - $this->assertFalse(Format::isPhpDateFormat('yyyy-MM-dd HH:mm:ss')); - $this->assertFalse(Format::isPhpDateFormat('EEEE, MMMM dd')); - } - - public function testIsPhpDateFormatRejectsStrftime(): void - { - // isPhpDateFormat is only called after isStrftimeFormat returns false, - // so it doesn't need to handle strftime patterns. But patterns without - // a % are already not strftime — verify ICU-like patterns are rejected. - $this->assertFalse(Format::isPhpDateFormat('dd.MM.yyyy')); - $this->assertFalse(Format::isPhpDateFormat('EEEE, MMMM dd')); + $this->assertInstanceOf(DateInterface::class, $result); + $dt = $result->toDateTimeImmutable(); + $this->assertSame('2026', $dt->format('Y')); + $this->assertSame('05', $dt->format('m')); + $this->assertSame('29', $dt->format('d')); } }