From 84f53ab62578f18bdece669fe83b57b9374938f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Sun, 12 Jun 2022 14:21:28 +0200 Subject: [PATCH 1/2] add formatting API to LocalDate, LocalDateTime, LocalTime, and ZonedDateTime --- composer.json | 1 + src/Formatter/DateTimeFormatContext.php | 142 +++++++++++ src/Formatter/DateTimeFormatException.php | 14 + src/Formatter/DateTimeFormatter.php | 20 ++ src/Formatter/IntlFormatter.php | 185 ++++++++++++++ src/Formatter/NativeFormatter.php | 95 +++++++ src/LocalDate.php | 12 + src/LocalDateTime.php | 12 + src/LocalTime.php | 12 + src/ZonedDateTime.php | 12 + tests/Formatter/DateTimeFormatContextTest.php | 95 +++++++ tests/Formatter/IntlFormatterTest.php | 239 ++++++++++++++++++ tests/Formatter/NativeFormatterTest.php | 85 +++++++ 13 files changed, 924 insertions(+) create mode 100644 src/Formatter/DateTimeFormatContext.php create mode 100644 src/Formatter/DateTimeFormatException.php create mode 100644 src/Formatter/DateTimeFormatter.php create mode 100644 src/Formatter/IntlFormatter.php create mode 100644 src/Formatter/NativeFormatter.php create mode 100644 tests/Formatter/DateTimeFormatContextTest.php create mode 100644 tests/Formatter/IntlFormatterTest.php create mode 100644 tests/Formatter/NativeFormatterTest.php diff --git a/composer.json b/composer.json index 8a8f0fe9..79e290b9 100644 --- a/composer.json +++ b/composer.json @@ -18,6 +18,7 @@ "vimeo/psalm": "5.15.0" }, "suggest": { + "ext-intl": "This extension is required for locale-based formatting", "ext-timezonedb": "This PECL extension provides up-to-date timezone information" }, "autoload": { diff --git a/src/Formatter/DateTimeFormatContext.php b/src/Formatter/DateTimeFormatContext.php new file mode 100644 index 00000000..b1e1bd78 --- /dev/null +++ b/src/Formatter/DateTimeFormatContext.php @@ -0,0 +1,142 @@ +> */ + private array $fields = []; + + /** + * @param LocalDate|LocalDateTime|LocalTime|ZonedDateTime $value + */ + private function __construct($value) + { + $this->value = $value; + } + + public static function ofLocalDate(LocalDate $localDate): self + { + $self = new self($localDate); + $self->addField(Field\DayOfMonth::NAME, (string) $localDate->getDay()); + $self->addField(Field\DayOfWeek::NAME, (string) $localDate->getDayOfWeek()->getValue()); + $self->addField(Field\DayOfYear::NAME, (string) $localDate->getDayOfYear()); + $self->addField(Field\WeekOfYear::NAME, (string) $localDate->getYearWeek()->getWeek()); + $self->addField(Field\MonthOfYear::NAME, (string) $localDate->getMonth()); + $self->addField(Field\Year::NAME, (string) $localDate->getYear()); + + return $self; + } + + public static function ofLocalTime(LocalTime $localTime): self + { + $self = new self($localTime); + $self->addField(Field\HourOfDay::NAME, (string) $localTime->getHour()); + $self->addField(Field\MinuteOfHour::NAME, (string) $localTime->getMinute()); + $self->addField(Field\SecondOfMinute::NAME, (string) $localTime->getSecond()); + $self->addField(Field\NanoOfSecond::NAME, (string) $localTime->getNano()); + $self->addField(Field\FractionOfSecond::NAME, (string) $localTime->getNano()); + + return $self; + } + + public static function ofLocalDateTime(LocalDateTime $localDateTime): self + { + $self = new self($localDateTime); + $self->addField(Field\DayOfMonth::NAME, (string) $localDateTime->getDate()->getDay()); + $self->addField(Field\DayOfWeek::NAME, (string) $localDateTime->getDate()->getDayOfWeek()->getValue()); + $self->addField(Field\DayOfYear::NAME, (string) $localDateTime->getDate()->getDayOfYear()); + $self->addField(Field\WeekOfYear::NAME, (string) $localDateTime->getDate()->getYearWeek()->getWeek()); + $self->addField(Field\MonthOfYear::NAME, (string) $localDateTime->getDate()->getMonth()); + $self->addField(Field\Year::NAME, (string) $localDateTime->getDate()->getYear()); + $self->addField(Field\HourOfDay::NAME, (string) $localDateTime->getTime()->getHour()); + $self->addField(Field\MinuteOfHour::NAME, (string) $localDateTime->getTime()->getMinute()); + $self->addField(Field\SecondOfMinute::NAME, (string) $localDateTime->getTime()->getSecond()); + $self->addField(Field\NanoOfSecond::NAME, (string) $localDateTime->getTime()->getNano()); + $self->addField(Field\FractionOfSecond::NAME, (string) $localDateTime->getTime()->getNano()); + + return $self; + } + + public static function ofZonedDateTime(ZonedDateTime $zonedDateTime): self + { + $self = new self($zonedDateTime); + $self->addField(Field\DayOfMonth::NAME, (string) $zonedDateTime->getDate()->getDay()); + $self->addField(Field\DayOfWeek::NAME, (string) $zonedDateTime->getDate()->getDayOfWeek()->getValue()); + $self->addField(Field\DayOfYear::NAME, (string) $zonedDateTime->getDate()->getDayOfYear()); + $self->addField(Field\WeekOfYear::NAME, (string) $zonedDateTime->getDate()->getYearWeek()->getWeek()); + $self->addField(Field\MonthOfYear::NAME, (string) $zonedDateTime->getDate()->getMonth()); + $self->addField(Field\Year::NAME, (string) $zonedDateTime->getDate()->getYear()); + $self->addField(Field\HourOfDay::NAME, (string) $zonedDateTime->getTime()->getHour()); + $self->addField(Field\MinuteOfHour::NAME, (string) $zonedDateTime->getTime()->getMinute()); + $self->addField(Field\SecondOfMinute::NAME, (string) $zonedDateTime->getTime()->getSecond()); + $self->addField(Field\NanoOfSecond::NAME, (string) $zonedDateTime->getTime()->getNano()); + $self->addField(Field\FractionOfSecond::NAME, (string) $zonedDateTime->getTime()->getNano()); + $self->addField(Field\TimeZoneOffsetHour::NAME, sprintf('%d', floor(abs($zonedDateTime->getTimeZoneOffset()->getTotalSeconds()) / LocalTime::SECONDS_PER_HOUR))); + $self->addField(Field\TimeZoneOffsetMinute::NAME, (string) ((abs($zonedDateTime->getTimeZoneOffset()->getTotalSeconds()) % LocalTime::SECONDS_PER_HOUR) / LocalTime::SECONDS_PER_MINUTE)); + $self->addField(Field\TimeZoneOffsetSign::NAME, $zonedDateTime->getTimeZoneOffset()->getTotalSeconds() === 0 ? 'Z' : ($zonedDateTime->getTimeZoneOffset()->getTotalSeconds() > 0 ? '+' : '-')); + $self->addField(Field\TimeZoneOffsetTotalSeconds::NAME, (string) $zonedDateTime->getTimeZoneOffset()->getTotalSeconds()); + $self->addField(Field\TimeZoneRegion::NAME, $zonedDateTime->getTimeZone()->getId()); + + return $self; + } + + public function addField(string $name, string $value): void + { + $this->fields[$name][] = $value; + } + + public function hasField(string $name): bool + { + return isset($this->fields[$name]) && $this->fields[$name]; + } + + public function getField(string $name): string + { + $value = $this->getOptionalField($name); + + if ($value === '') { + throw new DateTimeFormatException(sprintf('Field %s is not present in the formatting context.', $name)); + } + + return $value; + } + + public function getOptionalField(string $name): string + { + if (isset($this->fields[$name])) { + if ($this->fields[$name]) { + return array_shift($this->fields[$name]); + } + } + + return ''; + } + + /** + * @return LocalDate|LocalDateTime|LocalTime|ZonedDateTime + */ + public function getValue() + { + return $this->value; + } +} diff --git a/src/Formatter/DateTimeFormatException.php b/src/Formatter/DateTimeFormatException.php new file mode 100644 index 00000000..2ac9af07 --- /dev/null +++ b/src/Formatter/DateTimeFormatException.php @@ -0,0 +1,14 @@ +locale = $locale; + $this->dateFormat = $dateFormat; + $this->timeFormat = $timeFormat; + $this->pattern = $pattern; + + if (! extension_loaded('intl')) { + throw new DateTimeFormatException('IntlFormatter requires ext-intl to be installed and enabled.'); + } + } + + /** + * Returns a formatter of given type for a date value. + */ + public static function ofDate(string $locale, int $format): self + { + return new self($locale, $format, IntlDateFormatter::NONE, ''); + } + + /** + * Returns a formatter of given type for a time value. + */ + public static function ofTime(string $locale, int $format): self + { + return new self($locale, IntlDateFormatter::NONE, $format, ''); + } + + /** + * Returns a formatter of given type for a date-time value. + */ + public static function ofDateTime(string $locale, int $dateFormat, int $timeFormat): self + { + return new self($locale, $dateFormat, $timeFormat, ''); + } + + /** + * Returns a formatter with given ICU SimpleFormat pattern. + */ + public static function ofPattern(string $locale, string $pattern): self + { + return new self($locale, IntlDateFormatter::NONE, IntlDateFormatter::NONE, $pattern); + } + + /** + * Returns a formatter with a pattern that best matches given skeleton. + */ + public static function ofSkeleton(string $locale, string $skeleton): self + { + if (PHP_VERSION_ID < 80100) { + throw new DateTimeFormatException('IntlFormatter::ofSkeleton() is only available in PHP 8.1 and above.'); + } + + $generator = new IntlDatePatternGenerator($locale); + $pattern = $generator->getBestPattern($skeleton); + + if ($pattern === false) { + throw new DateTimeFormatException('Failed to resolve the best formatting pattern for given locale and skeleton.'); + } + + return self::ofPattern($locale, $pattern); + } + + public function format(DateTimeFormatContext $context): string + { + $value = $context->getValue(); + + if ($this->dateFormat !== IntlDateFormatter::NONE && $value instanceof LocalTime) { + throw new DateTimeFormatException('IntlFormatter with a date part cannot be used to format Brick\DateTime\LocalTime.'); + } + + if ($this->timeFormat !== IntlDateFormatter::NONE && $value instanceof LocalDate) { + throw new DateTimeFormatException('IntlFormatter with a time part cannot be used to format Brick\DateTime\LocalDate.'); + } + + if (($this->timeFormat === self::FULL || $this->timeFormat === self::LONG) && ! ($value instanceof ZonedDateTime)) { + throw new DateTimeFormatException(sprintf('IntlFormatter with a long or full time part cannot be used to format %s.', get_class($value))); + } + + if ($this->pattern !== '') { + self::checkPattern($this->pattern, $value); + } + + $timeZone = $value instanceof ZonedDateTime ? $value->getTimeZone()->toNativeDateTimeZone() : new DateTimeZone('UTC'); + $formatter = new IntlDateFormatter($this->locale, $this->dateFormat, $this->timeFormat, $timeZone, null, $this->pattern); + + return $formatter->format($value->toNativeDateTimeImmutable()); + } + + /** + * @param LocalDate|LocalDateTime|LocalTime|ZonedDateTime $value + */ + private static function checkPattern(string $pattern, $value): void + { + $supportedTypesMap = [ + LocalDate::class => true, + LocalDateTime::class => true, + LocalTime::class => true, + ZonedDateTime::class => true, + ]; + + $inString = false; + foreach (str_split($pattern) as $character) { + if ($character === '\'') { + if ($inString) { + $inString = false; + + continue; + } + + $inString = true; + + continue; + } + + if ($inString) { + continue; + } + + if (in_array($character, ['G', 'y', 'Y', 'u', 'U', 'r', 'Q', 'q', 'M', 'L', 'w', 'W', 'd', 'D', 'F', 'g', 'E', 'e', 'c'], true)) { + $supportedTypesMap[LocalTime::class] = false; + } + + if (in_array($character, ['a', 'h', 'H', 'k', 'K', 'm', 's', 'S', 'A'], true)) { + $supportedTypesMap[LocalDate::class] = false; + } + + if (in_array($character, ['z', 'Z', 'O', 'v', 'V', 'X', 'x'], true)) { + $supportedTypesMap[LocalDate::class] = false; + $supportedTypesMap[LocalDateTime::class] = false; + $supportedTypesMap[LocalTime::class] = false; + } + } + + $supportedTypes = array_keys($supportedTypesMap, true, true); + foreach ($supportedTypes as $supportedType) { + if ($value instanceof $supportedType) { + return; + } + } + + throw new DateTimeFormatException(sprintf("IntlFormatter with pattern '%s' is incompatible with type %s.", $pattern, get_class($value))); + } +} diff --git a/src/Formatter/NativeFormatter.php b/src/Formatter/NativeFormatter.php new file mode 100644 index 00000000..aaba7fd2 --- /dev/null +++ b/src/Formatter/NativeFormatter.php @@ -0,0 +1,95 @@ + */ + private array $supportedValueTypes; + + private function __construct(string $format) + { + $this->format = $format; + $this->supportedValueTypes = self::getSupportedValueTypes($format); + } + + public static function of(string $format): self + { + return new self($format); + } + + public function format(DateTimeFormatContext $context): string + { + $value = $context->getValue(); + + foreach ($this->supportedValueTypes as $supportedValueType) { + if ($value instanceof $supportedValueType) { + return $value->toNativeDateTimeImmutable()->format($this->format); + } + } + + throw new DateTimeFormatException(sprintf("Formatting pattern '%s' is incompatible with type %s.", $this->format, get_class($value))); + } + + /** + * @return list + */ + private static function getSupportedValueTypes(string $format): array + { + $supported = [ + LocalDate::class => true, + LocalDateTime::class => true, + LocalTime::class => true, + ZonedDateTime::class => true, + ]; + + $escaped = false; + foreach (str_split($format) as $character) { + if ($character === '\\') { + $escaped = true; + + continue; + } + + if ($escaped) { + $escaped = false; + + continue; + } + + if (in_array($character, ['d', 'j', 'S', 'D', 'l', 'N', 'w', 'z', 'W', 'o', 'F', 'm', 'M', 'n', 't', 'L', 'Y', 'y'], true)) { + $supported[LocalTime::class] = false; + } + + if (in_array($character, ['a', 'A', 'g', 'G', 'h', 'H', 'B', 'i', 's', 'v', 'u'], true)) { + $supported[LocalDate::class] = false; + } + + if (in_array($character, ['e', 'T', 'I', 'c', 'r', 'U', 'O', 'P', 'p', 'Z'], true)) { + $supported[LocalDate::class] = false; + $supported[LocalDateTime::class] = false; + $supported[LocalTime::class] = false; + } + } + + return array_keys($supported, true, true); + } +} diff --git a/src/LocalDate.php b/src/LocalDate.php index 59e4859d..c2513291 100644 --- a/src/LocalDate.php +++ b/src/LocalDate.php @@ -4,6 +4,8 @@ namespace Brick\DateTime; +use Brick\DateTime\Formatter\DateTimeFormatContext; +use Brick\DateTime\Formatter\DateTimeFormatter; use Brick\DateTime\Parser\DateTimeParseException; use Brick\DateTime\Parser\DateTimeParser; use Brick\DateTime\Parser\DateTimeParseResult; @@ -777,6 +779,16 @@ public function jsonSerialize(): string return $this->toISOString(); } + /** + * Formats this LocalDate using given DateTimeFormatter. + */ + public function format(DateTimeFormatter $formatter): string + { + $context = DateTimeFormatContext::ofLocalDate($this); + + return $formatter->format($context); + } + /** * Returns the ISO 8601 representation of this date. */ diff --git a/src/LocalDateTime.php b/src/LocalDateTime.php index 6da9d614..79f5b03a 100644 --- a/src/LocalDateTime.php +++ b/src/LocalDateTime.php @@ -4,6 +4,8 @@ namespace Brick\DateTime; +use Brick\DateTime\Formatter\DateTimeFormatContext; +use Brick\DateTime\Formatter\DateTimeFormatter; use Brick\DateTime\Parser\DateTimeParseException; use Brick\DateTime\Parser\DateTimeParser; use Brick\DateTime\Parser\DateTimeParseResult; @@ -736,6 +738,16 @@ public function toNativeDateTimeImmutable(): DateTimeImmutable return DateTimeImmutable::createFromMutable($this->toNativeDateTime()); } + /** + * Formats this LocalDateTime using given DateTimeFormatter. + */ + public function format(DateTimeFormatter $formatter): string + { + $context = DateTimeFormatContext::ofLocalDateTime($this); + + return $formatter->format($context); + } + /** * Serializes as a string using {@see LocalDateTime::toISOString()}. */ diff --git a/src/LocalTime.php b/src/LocalTime.php index 9ca1e99b..ef572d75 100644 --- a/src/LocalTime.php +++ b/src/LocalTime.php @@ -7,6 +7,8 @@ use Brick\DateTime\Field\HourOfDay; use Brick\DateTime\Field\MinuteOfHour; use Brick\DateTime\Field\SecondOfMinute; +use Brick\DateTime\Formatter\DateTimeFormatContext; +use Brick\DateTime\Formatter\DateTimeFormatter; use Brick\DateTime\Parser\DateTimeParseException; use Brick\DateTime\Parser\DateTimeParser; use Brick\DateTime\Parser\DateTimeParseResult; @@ -649,6 +651,16 @@ public function jsonSerialize(): string return $this->toISOString(); } + /** + * Formats this LocalTime using given DateTimeFormatter. + */ + public function format(DateTimeFormatter $formatter): string + { + $context = DateTimeFormatContext::ofLocalTime($this); + + return $formatter->format($context); + } + /** * Returns the ISO 8601 representation of this time. * diff --git a/src/ZonedDateTime.php b/src/ZonedDateTime.php index 0b1e41b9..d16d93b5 100644 --- a/src/ZonedDateTime.php +++ b/src/ZonedDateTime.php @@ -4,6 +4,8 @@ namespace Brick\DateTime; +use Brick\DateTime\Formatter\DateTimeFormatContext; +use Brick\DateTime\Formatter\DateTimeFormatter; use Brick\DateTime\Parser\DateTimeParseException; use Brick\DateTime\Parser\DateTimeParser; use Brick\DateTime\Parser\DateTimeParseResult; @@ -730,6 +732,16 @@ public function toNativeDateTimeImmutable(): DateTimeImmutable return DateTimeImmutable::createFromMutable($this->toNativeDateTime()); } + /** + * Formats this ZonedDateTime using given DateTimeFormatter. + */ + public function format(DateTimeFormatter $formatter): string + { + $context = DateTimeFormatContext::ofZonedDateTime($this); + + return $formatter->format($context); + } + /** * Serializes as a string using {@see ZonedDateTime::toISOString()}. */ diff --git a/tests/Formatter/DateTimeFormatContextTest.php b/tests/Formatter/DateTimeFormatContextTest.php new file mode 100644 index 00000000..b96ea34f --- /dev/null +++ b/tests/Formatter/DateTimeFormatContextTest.php @@ -0,0 +1,95 @@ +assertSame('8', $context->getField(Field\DayOfMonth::NAME)); + $this->assertSame('3', $context->getField(Field\DayOfWeek::NAME)); + $this->assertSame('159', $context->getField(Field\DayOfYear::NAME)); + $this->assertSame('23', $context->getField(Field\WeekOfYear::NAME)); + $this->assertSame('6', $context->getField(Field\MonthOfYear::NAME)); + $this->assertSame('2022', $context->getField(Field\Year::NAME)); + + $this->assertFalse($context->hasField(Field\HourOfDay::NAME)); + $this->assertFalse($context->hasField(Field\TimeZoneRegion::NAME)); + } + + public function testOfLocalTime(): void + { + $localTime = LocalTime::of(13, 37, 42, 999999999); + $context = DateTimeFormatContext::ofLocalTime($localTime); + + $this->assertSame('13', $context->getField(Field\HourOfDay::NAME)); + $this->assertSame('37', $context->getField(Field\MinuteOfHour::NAME)); + $this->assertSame('42', $context->getField(Field\SecondOfMinute::NAME)); + $this->assertSame('999999999', $context->getField(Field\NanoOfSecond::NAME)); + $this->assertSame('999999999', $context->getField(Field\FractionOfSecond::NAME)); + + $this->assertFalse($context->hasField(Field\DayOfMonth::NAME)); + $this->assertFalse($context->hasField(Field\TimeZoneRegion::NAME)); + } + + public function testOfLocalDateTime(): void + { + $localDateTime = LocalDateTime::of(2022, 6, 8, 13, 37, 42, 999999999); + $context = DateTimeFormatContext::ofLocalDateTime($localDateTime); + + $this->assertSame('8', $context->getField(Field\DayOfMonth::NAME)); + $this->assertSame('3', $context->getField(Field\DayOfWeek::NAME)); + $this->assertSame('159', $context->getField(Field\DayOfYear::NAME)); + $this->assertSame('23', $context->getField(Field\WeekOfYear::NAME)); + $this->assertSame('6', $context->getField(Field\MonthOfYear::NAME)); + $this->assertSame('2022', $context->getField(Field\Year::NAME)); + + $this->assertSame('13', $context->getField(Field\HourOfDay::NAME)); + $this->assertSame('37', $context->getField(Field\MinuteOfHour::NAME)); + $this->assertSame('42', $context->getField(Field\SecondOfMinute::NAME)); + $this->assertSame('999999999', $context->getField(Field\NanoOfSecond::NAME)); + $this->assertSame('999999999', $context->getField(Field\FractionOfSecond::NAME)); + + $this->assertFalse($context->hasField(Field\TimeZoneRegion::NAME)); + } + + public function testOfZonedDateTime(): void + { + $localDateTime = LocalDateTime::of(2022, 6, 8, 13, 37, 42, 999999999); + $zonedDateTime = ZonedDateTime::of($localDateTime, TimeZoneRegion::of('Europe/Prague')); + $context = DateTimeFormatContext::ofZonedDateTime($zonedDateTime); + + $this->assertSame('8', $context->getField(Field\DayOfMonth::NAME)); + $this->assertSame('3', $context->getField(Field\DayOfWeek::NAME)); + $this->assertSame('159', $context->getField(Field\DayOfYear::NAME)); + $this->assertSame('23', $context->getField(Field\WeekOfYear::NAME)); + $this->assertSame('6', $context->getField(Field\MonthOfYear::NAME)); + $this->assertSame('2022', $context->getField(Field\Year::NAME)); + + $this->assertSame('13', $context->getField(Field\HourOfDay::NAME)); + $this->assertSame('37', $context->getField(Field\MinuteOfHour::NAME)); + $this->assertSame('42', $context->getField(Field\SecondOfMinute::NAME)); + $this->assertSame('999999999', $context->getField(Field\NanoOfSecond::NAME)); + $this->assertSame('999999999', $context->getField(Field\FractionOfSecond::NAME)); + + $this->assertSame('2', $context->getField(Field\TimeZoneOffsetHour::NAME)); + $this->assertSame('0', $context->getField(Field\TimeZoneOffsetMinute::NAME)); + $this->assertSame('+', $context->getField(Field\TimeZoneOffsetSign::NAME)); + $this->assertSame('7200', $context->getField(Field\TimeZoneOffsetTotalSeconds::NAME)); + $this->assertSame('Europe/Prague', $context->getField(Field\TimeZoneRegion::NAME)); + } +} diff --git a/tests/Formatter/IntlFormatterTest.php b/tests/Formatter/IntlFormatterTest.php new file mode 100644 index 00000000..801fe8a2 --- /dev/null +++ b/tests/Formatter/IntlFormatterTest.php @@ -0,0 +1,239 @@ +expectException(get_class($expectedResult)); + $this->expectExceptionMessage($expectedResult->getMessage()); + } + + $formatted = $value->format($formatter); + $this->assertSame($expectedResult, $formatted); + } + + public function provideTestOfDateData(): iterable + { + $date = LocalDate::of(2022, 6, 8); + yield [$date, IntlFormatter::FULL, 'Wednesday, June 8, 2022']; + yield [$date, IntlFormatter::LONG, 'June 8, 2022']; + yield [$date, IntlFormatter::MEDIUM, 'Jun 8, 2022']; + yield [$date, IntlFormatter::SHORT, '6/8/22']; + + $dateTime = LocalDateTime::of(2022, 6, 8, 13, 37, 42, 999999999); + yield [$dateTime, IntlFormatter::FULL, 'Wednesday, June 8, 2022']; + + $time = LocalTime::of(13, 37, 42, 999999999); + yield [$time, IntlFormatter::FULL, new DateTimeFormatException('IntlFormatter with a date part cannot be used to format Brick\DateTime\LocalTime.')]; + + $zoned = ZonedDateTime::of($dateTime, TimeZoneRegion::of('Europe/Prague')); + yield [$zoned, IntlFormatter::FULL, 'Wednesday, June 8, 2022']; + } + + /** + * @dataProvider provideTestOfDateTimeData + * + * @param LocalDate|LocalDateTime|LocalTime|ZonedDateTime $value + * @param string|Throwable $expectedResult + */ + public function testOfDateTime($value, int $dateFormat, int $timeFormat, $expectedResult): void + { + $formatter = IntlFormatter::ofDateTime('en_US', $dateFormat, $timeFormat); + + if ($expectedResult instanceof Throwable) { + $this->expectException(get_class($expectedResult)); + $this->expectExceptionMessage($expectedResult->getMessage()); + } + + $formatted = $value->format($formatter); + $this->assertSame($expectedResult, $formatted); + } + + public function provideTestOfDateTimeData(): iterable + { + $date = LocalDate::of(2022, 6, 8); + yield [$date, IntlFormatter::FULL, IntlFormatter::FULL, new DateTimeFormatException('IntlFormatter with a time part cannot be used to format Brick\DateTime\LocalDate.')]; + + $dateTime = LocalDateTime::of(2022, 6, 8, 13, 37, 42, 999999999); + yield [$dateTime, IntlFormatter::FULL, IntlFormatter::FULL, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalDateTime.')]; + yield [$dateTime, IntlFormatter::FULL, IntlFormatter::LONG, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalDateTime.')]; + yield [$dateTime, IntlFormatter::FULL, IntlFormatter::MEDIUM, 'Wednesday, June 8, 2022 at 1:37:42 PM']; + yield [$dateTime, IntlFormatter::FULL, IntlFormatter::SHORT, 'Wednesday, June 8, 2022 at 1:37 PM']; + yield [$dateTime, IntlFormatter::LONG, IntlFormatter::SHORT, 'June 8, 2022 at 1:37 PM']; + yield [$dateTime, IntlFormatter::MEDIUM, IntlFormatter::MEDIUM, 'Jun 8, 2022, 1:37:42 PM']; + yield [$dateTime, IntlFormatter::SHORT, IntlFormatter::LONG, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalDateTime.')]; + + $time = LocalTime::of(13, 37, 42, 999999999); + yield [$time, IntlFormatter::FULL, IntlFormatter::FULL, new DateTimeFormatException('IntlFormatter with a date part cannot be used to format Brick\DateTime\LocalTime.')]; + + $zoned = ZonedDateTime::of($dateTime, TimeZoneRegion::of('Europe/Prague')); + yield [$zoned, IntlFormatter::FULL, IntlFormatter::FULL, 'Wednesday, June 8, 2022 at 1:37:42 PM Central European Summer Time']; + yield [$zoned, IntlFormatter::FULL, IntlFormatter::LONG, 'Wednesday, June 8, 2022 at 1:37:42 PM GMT+2']; + yield [$zoned, IntlFormatter::FULL, IntlFormatter::MEDIUM, 'Wednesday, June 8, 2022 at 1:37:42 PM']; + yield [$zoned, IntlFormatter::FULL, IntlFormatter::SHORT, 'Wednesday, June 8, 2022 at 1:37 PM']; + yield [$zoned, IntlFormatter::LONG, IntlFormatter::SHORT, 'June 8, 2022 at 1:37 PM']; + yield [$zoned, IntlFormatter::MEDIUM, IntlFormatter::MEDIUM, 'Jun 8, 2022, 1:37:42 PM']; + yield [$zoned, IntlFormatter::SHORT, IntlFormatter::LONG, '6/8/22, 1:37:42 PM GMT+2']; + } + + /** + * @dataProvider provideTestOfTimeData + * + * @param LocalDate|LocalDateTime|LocalTime|ZonedDateTime $value + * @param string|Throwable $expectedResult + */ + public function testOfTime($value, int $format, $expectedResult): void + { + $formatter = IntlFormatter::ofTime('en_US', $format); + + if ($expectedResult instanceof Throwable) { + $this->expectException(get_class($expectedResult)); + $this->expectExceptionMessage($expectedResult->getMessage()); + } + + $formatted = $value->format($formatter); + $this->assertSame($expectedResult, $formatted); + } + + public function provideTestOfTimeData(): iterable + { + $date = LocalDate::of(2022, 6, 8); + yield [$date, IntlFormatter::FULL, new DateTimeFormatException('IntlFormatter with a time part cannot be used to format Brick\DateTime\LocalDate.')]; + + $dateTime = LocalDateTime::of(2022, 6, 8, 13, 37, 42, 999999999); + yield [$dateTime, IntlFormatter::FULL, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalDateTime.')]; + + $time = LocalTime::of(13, 37, 42, 999999999); + yield [$time, IntlFormatter::FULL, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalTime.')]; + yield [$time, IntlFormatter::LONG, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalTime.')]; + yield [$time, IntlFormatter::MEDIUM, '1:37:42 PM']; + yield [$time, IntlFormatter::SHORT, '1:37 PM']; + + $zoned = ZonedDateTime::of($dateTime, TimeZoneRegion::of('Europe/Prague')); + yield [$zoned, IntlFormatter::FULL, '1:37:42 PM Central European Summer Time']; + } + + /** + * @dataProvider provideTestOfPatternData + * + * @param LocalDate|LocalDateTime|LocalTime|ZonedDateTime $value + * @param string|Throwable $expectedResult + */ + public function testOfPattern($value, string $pattern, $expectedResult): void + { + $formatter = IntlFormatter::ofPattern('en_US', $pattern); + + if ($expectedResult instanceof Throwable) { + $this->expectException(get_class($expectedResult)); + $this->expectExceptionMessage($expectedResult->getMessage()); + } + + $formatted = $value->format($formatter); + $this->assertSame($expectedResult, $formatted); + } + + public function provideTestOfPatternData(): iterable + { + $date = LocalDate::of(2022, 6, 8); + yield [$date, 'eee, dd.MMM.yyyy', 'Wed, 08.Jun.2022']; + yield [$date, 'H:mm:ss', new DateTimeFormatException("IntlFormatter with pattern 'H:mm:ss' is incompatible with type Brick\DateTime\LocalDate.")]; + yield [$date, 'XXX', new DateTimeFormatException("IntlFormatter with pattern 'XXX' is incompatible with type Brick\DateTime\LocalDate.")]; + + $dateTime = LocalDateTime::of(2022, 6, 8, 13, 37, 42, 999999999); + yield [$dateTime, 'eee, dd.MMM.yyyy', 'Wed, 08.Jun.2022']; + yield [$dateTime, 'H:mm:ss', '13:37:42']; + yield [$dateTime, 'XXX', new DateTimeFormatException("IntlFormatter with pattern 'XXX' is incompatible with type Brick\DateTime\LocalDateTime.")]; + + $time = LocalTime::of(13, 37, 42, 999999999); + yield [$time, 'eee, dd.MMM.yyyy', new DateTimeFormatException("IntlFormatter with pattern 'eee, dd.MMM.yyyy' is incompatible with type Brick\DateTime\LocalTime.")]; + yield [$time, 'H:mm:ss', '13:37:42']; + yield [$time, 'XXX', new DateTimeFormatException("IntlFormatter with pattern 'XXX' is incompatible with type Brick\DateTime\LocalTime.")]; + + $zoned = ZonedDateTime::of($dateTime, TimeZoneRegion::of('Europe/Prague')); + yield [$zoned, 'eee, dd.MMM.yyyy', 'Wed, 08.Jun.2022']; + yield [$zoned, 'H:mm:ss', '13:37:42']; + yield [$zoned, 'VV', 'Europe/Prague']; + yield [$zoned, 'XXX', '+02:00']; + + $zonedWithOffset = ZonedDateTime::of($dateTime, TimeZoneOffset::of(2)); + yield [$zonedWithOffset, 'eee, dd.MMM.yyyy', 'Wed, 08.Jun.2022']; + yield [$zonedWithOffset, 'H:mm:ss', '13:37:42']; + yield [$zonedWithOffset, 'VV', 'GMT+02:00']; + yield [$zonedWithOffset, 'XXX', '+02:00']; + } + + /** + * @dataProvider provideTestOfSkeletonData + * + * @param LocalDate|LocalDateTime|LocalTime|ZonedDateTime $value + * @param string|Throwable $expectedResult + */ + public function testOfSkeleton($value, string $skeleton, $expectedResult): void + { + $formatter = IntlFormatter::ofSkeleton('en_US', $skeleton); + + if ($expectedResult instanceof Throwable) { + $this->expectException(get_class($expectedResult)); + $this->expectExceptionMessage($expectedResult->getMessage()); + } + + $formatted = $value->format($formatter); + $this->assertSame($expectedResult, $formatted); + } + + public function provideTestOfSkeletonData(): iterable + { + $date = LocalDate::of(2022, 6, 8); + yield [$date, 'dMMMMyyyyeee', 'Wed, June 8, 2022']; + yield [$date, 'Hms', new DateTimeFormatException("IntlFormatter with pattern 'HH:mm:ss' is incompatible with type Brick\DateTime\LocalDate.")]; + yield [$date, 'XXX', new DateTimeFormatException("IntlFormatter with pattern 'XXX' is incompatible with type Brick\DateTime\LocalDate.")]; + + $dateTime = LocalDateTime::of(2022, 6, 8, 13, 37, 42, 999999999); + yield [$dateTime, 'dMMMMyyyyeee', 'Wed, June 8, 2022']; + yield [$dateTime, 'Hms', '13:37:42']; + yield [$dateTime, 'XXX', new DateTimeFormatException("IntlFormatter with pattern 'XXX' is incompatible with type Brick\DateTime\LocalDateTime.")]; + + $time = LocalTime::of(13, 37, 42, 999999999); + yield [$time, 'dMMMMyyyyeee', new DateTimeFormatException("IntlFormatter with pattern 'EEE, MMMM d, yyyy' is incompatible with type Brick\DateTime\LocalTime.")]; + yield [$time, 'Hms', '13:37:42']; + yield [$time, 'XXX', new DateTimeFormatException("IntlFormatter with pattern 'XXX' is incompatible with type Brick\DateTime\LocalTime.")]; + + $zoned = ZonedDateTime::of($dateTime, TimeZoneRegion::of('Europe/Prague')); + yield [$zoned, 'dMMMMyyyyeee', 'Wed, June 8, 2022']; + yield [$zoned, 'Hms', '13:37:42']; + yield [$zoned, 'VV', 'Europe/Prague']; + yield [$zoned, 'XXX', '+02:00']; + + $zonedWithOffset = ZonedDateTime::of($dateTime, TimeZoneOffset::of(2)); + yield [$zonedWithOffset, 'dMMMMyyyyeee', 'Wed, June 8, 2022']; + yield [$zonedWithOffset, 'Hms', '13:37:42']; + yield [$zonedWithOffset, 'VV', 'GMT+02:00']; + yield [$zonedWithOffset, 'XXX', '+02:00']; + } +} diff --git a/tests/Formatter/NativeFormatterTest.php b/tests/Formatter/NativeFormatterTest.php new file mode 100644 index 00000000..d8f995a2 --- /dev/null +++ b/tests/Formatter/NativeFormatterTest.php @@ -0,0 +1,85 @@ +expectException(get_class($expectedResult)); + $this->expectExceptionMessage($expectedResult->getMessage()); + } + + $formatted = $value->format($formatter); + $this->assertSame($expectedResult, $formatted); + } + + public function provideTestFormatData(): iterable + { + $date = LocalDate::of(2022, 6, 8); + yield [$date, 'd.m.y', '08.06.22']; + yield [$date, 'M j, Y', 'Jun 8, 2022']; + yield [$date, 'D', 'Wed']; + yield [$date, 'H:i:s', new DateTimeFormatException("Formatting pattern 'H:i:s' is incompatible with type Brick\DateTime\LocalDate.")]; + yield [$date, 'U', new DateTimeFormatException("Formatting pattern 'U' is incompatible with type Brick\DateTime\LocalDate.")]; + + $dateTime = LocalDateTime::of(2022, 6, 8, 13, 37, 42, 999999999); + yield [$dateTime, 'd.m.y', '08.06.22']; + yield [$dateTime, 'M j, Y', 'Jun 8, 2022']; + yield [$dateTime, 'D', 'Wed']; + yield [$dateTime, 'H:i:s', '13:37:42']; + yield [$dateTime, 'u', '999999']; + yield [$dateTime, 'U', new DateTimeFormatException("Formatting pattern 'U' is incompatible with type Brick\DateTime\LocalDateTime.")]; + + $time = LocalTime::of(13, 37, 42, 999999999); + yield [$time, 'd.m.y', new DateTimeFormatException("Formatting pattern 'd.m.y' is incompatible with type Brick\DateTime\LocalTime.")]; + yield [$time, 'D', new DateTimeFormatException("Formatting pattern 'D' is incompatible with type Brick\DateTime\LocalTime.")]; + yield [$time, 'H:i:s', '13:37:42']; + yield [$time, 'u', '999999']; + yield [$time, 'U', new DateTimeFormatException("Formatting pattern 'U' is incompatible with type Brick\DateTime\LocalTime.")]; + + $zoned = ZonedDateTime::of($dateTime, TimeZoneRegion::of('Europe/Prague')); + yield [$zoned, 'd.m.y', '08.06.22']; + yield [$zoned, 'M j, Y', 'Jun 8, 2022']; + yield [$zoned, 'D', 'Wed']; + yield [$zoned, 'H:i:s', '13:37:42']; + yield [$zoned, 'u', '999999']; + yield [$zoned, 'U', '1654688262']; + yield [$zoned, 'e', 'Europe/Prague']; + yield [$zoned, 'p', '+02:00']; + + $zonedWithOffset = ZonedDateTime::of($dateTime, TimeZoneOffset::of(2)); + yield [$zonedWithOffset, 'd.m.y', '08.06.22']; + yield [$zonedWithOffset, 'M j, Y', 'Jun 8, 2022']; + yield [$zonedWithOffset, 'D', 'Wed']; + yield [$zonedWithOffset, 'H:i:s', '13:37:42']; + yield [$zonedWithOffset, 'u', '999999']; + yield [$zonedWithOffset, 'U', '1654688262']; + yield [$zonedWithOffset, 'e', '+02:00']; + yield [$zonedWithOffset, 'p', '+02:00']; + } +} From 33aa44e69503854bf2845fd6431df316182e0bcc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ji=C5=99=C3=AD=20Pudil?= Date: Mon, 30 Oct 2023 10:25:08 +0100 Subject: [PATCH 2/2] update to v0.6 --- src/Formatter/IntlFormatter.php | 6 -- src/LocalTime.php | 18 ++-- tests/Formatter/DateTimeFormatContextTest.php | 92 +++++++++---------- tests/Formatter/IntlFormatterTest.php | 38 ++++---- tests/Formatter/NativeFormatterTest.php | 2 +- 5 files changed, 75 insertions(+), 81 deletions(-) diff --git a/src/Formatter/IntlFormatter.php b/src/Formatter/IntlFormatter.php index 35cd23a1..5a804fbe 100644 --- a/src/Formatter/IntlFormatter.php +++ b/src/Formatter/IntlFormatter.php @@ -19,8 +19,6 @@ use function sprintf; use function str_split; -use const PHP_VERSION_ID; - /** * Formats the value using the Intl extension. */ @@ -88,10 +86,6 @@ public static function ofPattern(string $locale, string $pattern): self */ public static function ofSkeleton(string $locale, string $skeleton): self { - if (PHP_VERSION_ID < 80100) { - throw new DateTimeFormatException('IntlFormatter::ofSkeleton() is only available in PHP 8.1 and above.'); - } - $generator = new IntlDatePatternGenerator($locale); $pattern = $generator->getBestPattern($skeleton); diff --git a/src/LocalTime.php b/src/LocalTime.php index ef572d75..358b34dc 100644 --- a/src/LocalTime.php +++ b/src/LocalTime.php @@ -651,15 +651,15 @@ public function jsonSerialize(): string return $this->toISOString(); } - /** - * Formats this LocalTime using given DateTimeFormatter. - */ - public function format(DateTimeFormatter $formatter): string - { - $context = DateTimeFormatContext::ofLocalTime($this); - - return $formatter->format($context); - } + /** + * Formats this LocalTime using given DateTimeFormatter. + */ + public function format(DateTimeFormatter $formatter): string + { + $context = DateTimeFormatContext::ofLocalTime($this); + + return $formatter->format($context); + } /** * Returns the ISO 8601 representation of this time. diff --git a/tests/Formatter/DateTimeFormatContextTest.php b/tests/Formatter/DateTimeFormatContextTest.php index b96ea34f..6a1e2b62 100644 --- a/tests/Formatter/DateTimeFormatContextTest.php +++ b/tests/Formatter/DateTimeFormatContextTest.php @@ -20,15 +20,15 @@ public function testOfLocalDate(): void $localDate = LocalDate::of(2022, 6, 8); $context = DateTimeFormatContext::ofLocalDate($localDate); - $this->assertSame('8', $context->getField(Field\DayOfMonth::NAME)); - $this->assertSame('3', $context->getField(Field\DayOfWeek::NAME)); - $this->assertSame('159', $context->getField(Field\DayOfYear::NAME)); - $this->assertSame('23', $context->getField(Field\WeekOfYear::NAME)); - $this->assertSame('6', $context->getField(Field\MonthOfYear::NAME)); - $this->assertSame('2022', $context->getField(Field\Year::NAME)); - - $this->assertFalse($context->hasField(Field\HourOfDay::NAME)); - $this->assertFalse($context->hasField(Field\TimeZoneRegion::NAME)); + self::assertSame('8', $context->getField(Field\DayOfMonth::NAME)); + self::assertSame('3', $context->getField(Field\DayOfWeek::NAME)); + self::assertSame('159', $context->getField(Field\DayOfYear::NAME)); + self::assertSame('23', $context->getField(Field\WeekOfYear::NAME)); + self::assertSame('6', $context->getField(Field\MonthOfYear::NAME)); + self::assertSame('2022', $context->getField(Field\Year::NAME)); + + self::assertFalse($context->hasField(Field\HourOfDay::NAME)); + self::assertFalse($context->hasField(Field\TimeZoneRegion::NAME)); } public function testOfLocalTime(): void @@ -36,14 +36,14 @@ public function testOfLocalTime(): void $localTime = LocalTime::of(13, 37, 42, 999999999); $context = DateTimeFormatContext::ofLocalTime($localTime); - $this->assertSame('13', $context->getField(Field\HourOfDay::NAME)); - $this->assertSame('37', $context->getField(Field\MinuteOfHour::NAME)); - $this->assertSame('42', $context->getField(Field\SecondOfMinute::NAME)); - $this->assertSame('999999999', $context->getField(Field\NanoOfSecond::NAME)); - $this->assertSame('999999999', $context->getField(Field\FractionOfSecond::NAME)); + self::assertSame('13', $context->getField(Field\HourOfDay::NAME)); + self::assertSame('37', $context->getField(Field\MinuteOfHour::NAME)); + self::assertSame('42', $context->getField(Field\SecondOfMinute::NAME)); + self::assertSame('999999999', $context->getField(Field\NanoOfSecond::NAME)); + self::assertSame('999999999', $context->getField(Field\FractionOfSecond::NAME)); - $this->assertFalse($context->hasField(Field\DayOfMonth::NAME)); - $this->assertFalse($context->hasField(Field\TimeZoneRegion::NAME)); + self::assertFalse($context->hasField(Field\DayOfMonth::NAME)); + self::assertFalse($context->hasField(Field\TimeZoneRegion::NAME)); } public function testOfLocalDateTime(): void @@ -51,20 +51,20 @@ public function testOfLocalDateTime(): void $localDateTime = LocalDateTime::of(2022, 6, 8, 13, 37, 42, 999999999); $context = DateTimeFormatContext::ofLocalDateTime($localDateTime); - $this->assertSame('8', $context->getField(Field\DayOfMonth::NAME)); - $this->assertSame('3', $context->getField(Field\DayOfWeek::NAME)); - $this->assertSame('159', $context->getField(Field\DayOfYear::NAME)); - $this->assertSame('23', $context->getField(Field\WeekOfYear::NAME)); - $this->assertSame('6', $context->getField(Field\MonthOfYear::NAME)); - $this->assertSame('2022', $context->getField(Field\Year::NAME)); + self::assertSame('8', $context->getField(Field\DayOfMonth::NAME)); + self::assertSame('3', $context->getField(Field\DayOfWeek::NAME)); + self::assertSame('159', $context->getField(Field\DayOfYear::NAME)); + self::assertSame('23', $context->getField(Field\WeekOfYear::NAME)); + self::assertSame('6', $context->getField(Field\MonthOfYear::NAME)); + self::assertSame('2022', $context->getField(Field\Year::NAME)); - $this->assertSame('13', $context->getField(Field\HourOfDay::NAME)); - $this->assertSame('37', $context->getField(Field\MinuteOfHour::NAME)); - $this->assertSame('42', $context->getField(Field\SecondOfMinute::NAME)); - $this->assertSame('999999999', $context->getField(Field\NanoOfSecond::NAME)); - $this->assertSame('999999999', $context->getField(Field\FractionOfSecond::NAME)); + self::assertSame('13', $context->getField(Field\HourOfDay::NAME)); + self::assertSame('37', $context->getField(Field\MinuteOfHour::NAME)); + self::assertSame('42', $context->getField(Field\SecondOfMinute::NAME)); + self::assertSame('999999999', $context->getField(Field\NanoOfSecond::NAME)); + self::assertSame('999999999', $context->getField(Field\FractionOfSecond::NAME)); - $this->assertFalse($context->hasField(Field\TimeZoneRegion::NAME)); + self::assertFalse($context->hasField(Field\TimeZoneRegion::NAME)); } public function testOfZonedDateTime(): void @@ -73,23 +73,23 @@ public function testOfZonedDateTime(): void $zonedDateTime = ZonedDateTime::of($localDateTime, TimeZoneRegion::of('Europe/Prague')); $context = DateTimeFormatContext::ofZonedDateTime($zonedDateTime); - $this->assertSame('8', $context->getField(Field\DayOfMonth::NAME)); - $this->assertSame('3', $context->getField(Field\DayOfWeek::NAME)); - $this->assertSame('159', $context->getField(Field\DayOfYear::NAME)); - $this->assertSame('23', $context->getField(Field\WeekOfYear::NAME)); - $this->assertSame('6', $context->getField(Field\MonthOfYear::NAME)); - $this->assertSame('2022', $context->getField(Field\Year::NAME)); - - $this->assertSame('13', $context->getField(Field\HourOfDay::NAME)); - $this->assertSame('37', $context->getField(Field\MinuteOfHour::NAME)); - $this->assertSame('42', $context->getField(Field\SecondOfMinute::NAME)); - $this->assertSame('999999999', $context->getField(Field\NanoOfSecond::NAME)); - $this->assertSame('999999999', $context->getField(Field\FractionOfSecond::NAME)); - - $this->assertSame('2', $context->getField(Field\TimeZoneOffsetHour::NAME)); - $this->assertSame('0', $context->getField(Field\TimeZoneOffsetMinute::NAME)); - $this->assertSame('+', $context->getField(Field\TimeZoneOffsetSign::NAME)); - $this->assertSame('7200', $context->getField(Field\TimeZoneOffsetTotalSeconds::NAME)); - $this->assertSame('Europe/Prague', $context->getField(Field\TimeZoneRegion::NAME)); + self::assertSame('8', $context->getField(Field\DayOfMonth::NAME)); + self::assertSame('3', $context->getField(Field\DayOfWeek::NAME)); + self::assertSame('159', $context->getField(Field\DayOfYear::NAME)); + self::assertSame('23', $context->getField(Field\WeekOfYear::NAME)); + self::assertSame('6', $context->getField(Field\MonthOfYear::NAME)); + self::assertSame('2022', $context->getField(Field\Year::NAME)); + + self::assertSame('13', $context->getField(Field\HourOfDay::NAME)); + self::assertSame('37', $context->getField(Field\MinuteOfHour::NAME)); + self::assertSame('42', $context->getField(Field\SecondOfMinute::NAME)); + self::assertSame('999999999', $context->getField(Field\NanoOfSecond::NAME)); + self::assertSame('999999999', $context->getField(Field\FractionOfSecond::NAME)); + + self::assertSame('2', $context->getField(Field\TimeZoneOffsetHour::NAME)); + self::assertSame('0', $context->getField(Field\TimeZoneOffsetMinute::NAME)); + self::assertSame('+', $context->getField(Field\TimeZoneOffsetSign::NAME)); + self::assertSame('7200', $context->getField(Field\TimeZoneOffsetTotalSeconds::NAME)); + self::assertSame('Europe/Prague', $context->getField(Field\TimeZoneRegion::NAME)); } } diff --git a/tests/Formatter/IntlFormatterTest.php b/tests/Formatter/IntlFormatterTest.php index 801fe8a2..59ecfe1d 100644 --- a/tests/Formatter/IntlFormatterTest.php +++ b/tests/Formatter/IntlFormatterTest.php @@ -35,7 +35,7 @@ public function testOfDate($value, int $format, $expectedResult): void } $formatted = $value->format($formatter); - $this->assertSame($expectedResult, $formatted); + self::assertSame($expectedResult, $formatted); } public function provideTestOfDateData(): iterable @@ -72,7 +72,7 @@ public function testOfDateTime($value, int $dateFormat, int $timeFormat, $expect } $formatted = $value->format($formatter); - $this->assertSame($expectedResult, $formatted); + self::assertSame($expectedResult, $formatted); } public function provideTestOfDateTimeData(): iterable @@ -83,23 +83,23 @@ public function provideTestOfDateTimeData(): iterable $dateTime = LocalDateTime::of(2022, 6, 8, 13, 37, 42, 999999999); yield [$dateTime, IntlFormatter::FULL, IntlFormatter::FULL, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalDateTime.')]; yield [$dateTime, IntlFormatter::FULL, IntlFormatter::LONG, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalDateTime.')]; - yield [$dateTime, IntlFormatter::FULL, IntlFormatter::MEDIUM, 'Wednesday, June 8, 2022 at 1:37:42 PM']; - yield [$dateTime, IntlFormatter::FULL, IntlFormatter::SHORT, 'Wednesday, June 8, 2022 at 1:37 PM']; - yield [$dateTime, IntlFormatter::LONG, IntlFormatter::SHORT, 'June 8, 2022 at 1:37 PM']; - yield [$dateTime, IntlFormatter::MEDIUM, IntlFormatter::MEDIUM, 'Jun 8, 2022, 1:37:42 PM']; + yield [$dateTime, IntlFormatter::FULL, IntlFormatter::MEDIUM, 'Wednesday, June 8, 2022 at 1:37:42 PM']; + yield [$dateTime, IntlFormatter::FULL, IntlFormatter::SHORT, 'Wednesday, June 8, 2022 at 1:37 PM']; + yield [$dateTime, IntlFormatter::LONG, IntlFormatter::SHORT, 'June 8, 2022 at 1:37 PM']; + yield [$dateTime, IntlFormatter::MEDIUM, IntlFormatter::MEDIUM, 'Jun 8, 2022, 1:37:42 PM']; yield [$dateTime, IntlFormatter::SHORT, IntlFormatter::LONG, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalDateTime.')]; $time = LocalTime::of(13, 37, 42, 999999999); yield [$time, IntlFormatter::FULL, IntlFormatter::FULL, new DateTimeFormatException('IntlFormatter with a date part cannot be used to format Brick\DateTime\LocalTime.')]; $zoned = ZonedDateTime::of($dateTime, TimeZoneRegion::of('Europe/Prague')); - yield [$zoned, IntlFormatter::FULL, IntlFormatter::FULL, 'Wednesday, June 8, 2022 at 1:37:42 PM Central European Summer Time']; - yield [$zoned, IntlFormatter::FULL, IntlFormatter::LONG, 'Wednesday, June 8, 2022 at 1:37:42 PM GMT+2']; - yield [$zoned, IntlFormatter::FULL, IntlFormatter::MEDIUM, 'Wednesday, June 8, 2022 at 1:37:42 PM']; - yield [$zoned, IntlFormatter::FULL, IntlFormatter::SHORT, 'Wednesday, June 8, 2022 at 1:37 PM']; - yield [$zoned, IntlFormatter::LONG, IntlFormatter::SHORT, 'June 8, 2022 at 1:37 PM']; - yield [$zoned, IntlFormatter::MEDIUM, IntlFormatter::MEDIUM, 'Jun 8, 2022, 1:37:42 PM']; - yield [$zoned, IntlFormatter::SHORT, IntlFormatter::LONG, '6/8/22, 1:37:42 PM GMT+2']; + yield [$zoned, IntlFormatter::FULL, IntlFormatter::FULL, 'Wednesday, June 8, 2022 at 1:37:42 PM Central European Summer Time']; + yield [$zoned, IntlFormatter::FULL, IntlFormatter::LONG, 'Wednesday, June 8, 2022 at 1:37:42 PM GMT+2']; + yield [$zoned, IntlFormatter::FULL, IntlFormatter::MEDIUM, 'Wednesday, June 8, 2022 at 1:37:42 PM']; + yield [$zoned, IntlFormatter::FULL, IntlFormatter::SHORT, 'Wednesday, June 8, 2022 at 1:37 PM']; + yield [$zoned, IntlFormatter::LONG, IntlFormatter::SHORT, 'June 8, 2022 at 1:37 PM']; + yield [$zoned, IntlFormatter::MEDIUM, IntlFormatter::MEDIUM, 'Jun 8, 2022, 1:37:42 PM']; + yield [$zoned, IntlFormatter::SHORT, IntlFormatter::LONG, '6/8/22, 1:37:42 PM GMT+2']; } /** @@ -118,7 +118,7 @@ public function testOfTime($value, int $format, $expectedResult): void } $formatted = $value->format($formatter); - $this->assertSame($expectedResult, $formatted); + self::assertSame($expectedResult, $formatted); } public function provideTestOfTimeData(): iterable @@ -132,11 +132,11 @@ public function provideTestOfTimeData(): iterable $time = LocalTime::of(13, 37, 42, 999999999); yield [$time, IntlFormatter::FULL, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalTime.')]; yield [$time, IntlFormatter::LONG, new DateTimeFormatException('IntlFormatter with a long or full time part cannot be used to format Brick\DateTime\LocalTime.')]; - yield [$time, IntlFormatter::MEDIUM, '1:37:42 PM']; - yield [$time, IntlFormatter::SHORT, '1:37 PM']; + yield [$time, IntlFormatter::MEDIUM, '1:37:42 PM']; + yield [$time, IntlFormatter::SHORT, '1:37 PM']; $zoned = ZonedDateTime::of($dateTime, TimeZoneRegion::of('Europe/Prague')); - yield [$zoned, IntlFormatter::FULL, '1:37:42 PM Central European Summer Time']; + yield [$zoned, IntlFormatter::FULL, '1:37:42 PM Central European Summer Time']; } /** @@ -155,7 +155,7 @@ public function testOfPattern($value, string $pattern, $expectedResult): void } $formatted = $value->format($formatter); - $this->assertSame($expectedResult, $formatted); + self::assertSame($expectedResult, $formatted); } public function provideTestOfPatternData(): iterable @@ -204,7 +204,7 @@ public function testOfSkeleton($value, string $skeleton, $expectedResult): void } $formatted = $value->format($formatter); - $this->assertSame($expectedResult, $formatted); + self::assertSame($expectedResult, $formatted); } public function provideTestOfSkeletonData(): iterable diff --git a/tests/Formatter/NativeFormatterTest.php b/tests/Formatter/NativeFormatterTest.php index d8f995a2..90edb2cc 100644 --- a/tests/Formatter/NativeFormatterTest.php +++ b/tests/Formatter/NativeFormatterTest.php @@ -35,7 +35,7 @@ public function testFormat($value, string $format, $expectedResult): void } $formatted = $value->format($formatter); - $this->assertSame($expectedResult, $formatted); + self::assertSame($expectedResult, $formatted); } public function provideTestFormatData(): iterable