mirror of
https://github.com/twigphp/Twig.git
synced 2026-09-12 18:36:53 +00:00
Honor date formatter prototype calendars
This commit is contained in:
@@ -15,7 +15,7 @@
|
|||||||
* Deprecate the `sandboxed` argument of the `include` function, use `Twig\Sandbox\Sandbox` instead
|
* Deprecate the `sandboxed` argument of the `include` function, use `Twig\Sandbox\Sandbox` instead
|
||||||
* Deprecate `SandboxExtension::enableSandbox()`, `disableSandbox()`, and `isSandboxedGlobally()`
|
* Deprecate `SandboxExtension::enableSandbox()`, `disableSandbox()`, and `isSandboxedGlobally()`
|
||||||
* Normalize destructuring variable AST nodes as assignment targets
|
* Normalize destructuring variable AST nodes as assignment targets
|
||||||
* Fix `IntlExtension` ignoring explicit date/time formats and the `format_date`/`format_time` filters when a date formatter prototype is configured
|
* Fix `IntlExtension` ignoring explicit date/time formats and configured calendars when using a date formatter prototype
|
||||||
* Add a `format_list` filter to `IntlExtension` to format a list of strings using PHP 8.5's `IntlListFormatter`
|
* Add a `format_list` filter to `IntlExtension` to format a list of strings using PHP 8.5's `IntlListFormatter`
|
||||||
* Fix array access with a `Stringable` key coercing the key to string for `ArrayAccess` objects that use object keys (such as `SplObjectStorage`)
|
* Fix array access with a `Stringable` key coercing the key to string for `ArrayAccess` objects that use object keys (such as `SplObjectStorage`)
|
||||||
* Fix duplicated macro argument names triggering a PHP fatal error instead of a `SyntaxError`
|
* Fix duplicated macro argument names triggering a PHP fatal error instead of a `SyntaxError`
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
``format_date``
|
``format_date``
|
||||||
===============
|
===============
|
||||||
|
|
||||||
The ``format_date`` filter formats a date. It behaves in the exact same way as
|
The ``format_date`` filter formats a date. It supports the same locale,
|
||||||
the :doc:`format_datetime<format_datetime>` filter, but without the time.
|
timezone, calendar and format options as the
|
||||||
|
:doc:`format_datetime<format_datetime>` filter, but without the time.
|
||||||
|
|
||||||
|
When no format or pattern is provided, Twig uses the
|
||||||
|
:ref:`application default date format <intl-date-format-defaults>`, or
|
||||||
|
``medium`` when none is configured. To use a custom pattern, pass it explicitly.
|
||||||
|
The :ref:`application default pattern <intl-date-format-defaults>` is not used
|
||||||
|
for date-only formatting.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@@ -31,8 +38,8 @@ Arguments
|
|||||||
|
|
||||||
* ``locale``: The locale code as defined in `RFC 5646`_
|
* ``locale``: The locale code as defined in `RFC 5646`_
|
||||||
* ``dateFormat``: The date format
|
* ``dateFormat``: The date format
|
||||||
* ``pattern``: A date time pattern
|
* ``pattern``: A date pattern
|
||||||
* ``timezone``: The date timezone
|
* ``timezone``: The date timezone
|
||||||
* ``calendar``: The calendar ("gregorian" by default)
|
* ``calendar``: The calendar
|
||||||
|
|
||||||
.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646
|
.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646
|
||||||
|
|||||||
@@ -30,7 +30,6 @@ The ``format_datetime`` filter formats a date time:
|
|||||||
$twig = new \Twig\Environment(...);
|
$twig = new \Twig\Environment(...);
|
||||||
$twig->addExtension(new IntlExtension());
|
$twig->addExtension(new IntlExtension());
|
||||||
|
|
||||||
|
|
||||||
Format
|
Format
|
||||||
------
|
------
|
||||||
|
|
||||||
@@ -49,6 +48,10 @@ You can tweak the output for the date part and the time part:
|
|||||||
|
|
||||||
Supported values are: ``none``, ``short``, ``medium``, ``long``, and ``full``.
|
Supported values are: ``none``, ``short``, ``medium``, ``long``, and ``full``.
|
||||||
|
|
||||||
|
When no pattern is provided, each omitted format uses the corresponding
|
||||||
|
:ref:`application default <intl-date-format-defaults>`, or ``medium`` when none
|
||||||
|
is configured.
|
||||||
|
|
||||||
.. versionadded:: 3.6
|
.. versionadded:: 3.6
|
||||||
|
|
||||||
``relative_short``, ``relative_medium``, ``relative_long``, and ``relative_full`` are also supported when running on
|
``relative_short``, ``relative_medium``, ``relative_long``, and ``relative_full`` are also supported when running on
|
||||||
@@ -60,19 +63,38 @@ For greater flexibility, you can even define your own pattern
|
|||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
{# 11 oclock PM, GMT #}
|
{# 11 o'clock PM, GMT #}
|
||||||
{{ '2019-08-07 23:39:12'|format_datetime(pattern: "hh 'oclock' a, zzzz") }}
|
{{ '2019-08-07 23:39:12'|format_datetime(pattern: "hh 'oclock' a, zzzz") }}
|
||||||
|
|
||||||
|
When no pattern, date format or time format is provided, Twig uses the
|
||||||
|
:ref:`application default pattern <intl-date-format-defaults>`, if any.
|
||||||
|
|
||||||
Locale
|
Locale
|
||||||
------
|
------
|
||||||
|
|
||||||
By default, the filter uses the current locale. You can pass it explicitly:
|
By default, the filter uses the
|
||||||
|
:ref:`application default locale <intl-date-format-defaults>`, or the current
|
||||||
|
locale when none is configured. You can override it explicitly:
|
||||||
|
|
||||||
.. code-block:: twig
|
.. code-block:: twig
|
||||||
|
|
||||||
{# 7 août 2019 23:39:12 #}
|
{# 7 août 2019 23:39:12 #}
|
||||||
{{ '2019-08-07 23:39:12'|format_datetime(locale: 'fr') }}
|
{{ '2019-08-07 23:39:12'|format_datetime(locale: 'fr') }}
|
||||||
|
|
||||||
|
Calendar
|
||||||
|
--------
|
||||||
|
|
||||||
|
By default, the filter uses the
|
||||||
|
:ref:`application default calendar <intl-date-format-defaults>`, or the
|
||||||
|
Gregorian calendar when none is configured. You can override it explicitly:
|
||||||
|
|
||||||
|
.. code-block:: twig
|
||||||
|
|
||||||
|
{{ '2019-08-07 23:39:12'|format_datetime(
|
||||||
|
calendar: 'traditional',
|
||||||
|
locale: 'th_TH',
|
||||||
|
) }}
|
||||||
|
|
||||||
Timezone
|
Timezone
|
||||||
--------
|
--------
|
||||||
|
|
||||||
@@ -118,6 +140,33 @@ The default timezone can also be set globally by calling ``setTimezone()``::
|
|||||||
$twig = new \Twig\Environment(...);
|
$twig = new \Twig\Environment(...);
|
||||||
$twig->addExtension(new IntlExtension());
|
$twig->addExtension(new IntlExtension());
|
||||||
|
|
||||||
|
.. _intl-date-format-defaults:
|
||||||
|
|
||||||
|
Configure Defaults
|
||||||
|
------------------
|
||||||
|
|
||||||
|
You can configure the locale, formats and calendar once for all date and time
|
||||||
|
filters by passing an ``IntlDateFormatter`` when registering the extension::
|
||||||
|
|
||||||
|
use Twig\Extra\Intl\IntlExtension;
|
||||||
|
|
||||||
|
$dateFormatter = new \IntlDateFormatter(
|
||||||
|
locale: 'fr_FR',
|
||||||
|
dateType: \IntlDateFormatter::LONG,
|
||||||
|
timeType: \IntlDateFormatter::SHORT,
|
||||||
|
calendar: \IntlDateFormatter::GREGORIAN,
|
||||||
|
);
|
||||||
|
|
||||||
|
$twig->addExtension(new IntlExtension(
|
||||||
|
dateFormatterPrototype: $dateFormatter,
|
||||||
|
));
|
||||||
|
|
||||||
|
Arguments passed to a filter override these defaults. You can also configure a
|
||||||
|
pattern with the ``pattern`` argument.
|
||||||
|
|
||||||
|
When using a dependency injection container, pass the formatter as the
|
||||||
|
``$dateFormatterPrototype`` argument of the ``IntlExtension`` service.
|
||||||
|
|
||||||
Arguments
|
Arguments
|
||||||
---------
|
---------
|
||||||
|
|
||||||
@@ -126,7 +175,7 @@ Arguments
|
|||||||
* ``timeFormat``: The time format
|
* ``timeFormat``: The time format
|
||||||
* ``pattern``: A date time pattern
|
* ``pattern``: A date time pattern
|
||||||
* ``timezone``: The date timezone name
|
* ``timezone``: The date timezone name
|
||||||
* ``calendar``: The calendar ("gregorian" by default)
|
* ``calendar``: The calendar
|
||||||
|
|
||||||
.. _ICU user guide: https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax
|
.. _ICU user guide: https://unicode-org.github.io/icu/userguide/format_parse/datetime/#datetime-format-syntax
|
||||||
.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646
|
.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646
|
||||||
|
|||||||
@@ -1,8 +1,15 @@
|
|||||||
``format_time``
|
``format_time``
|
||||||
===============
|
===============
|
||||||
|
|
||||||
The ``format_time`` filter formats a time. It behaves in the exact same way as
|
The ``format_time`` filter formats a time. It supports the same locale,
|
||||||
the :doc:`format_datetime<format_datetime>` filter, but without the date.
|
timezone, calendar and format options as the
|
||||||
|
:doc:`format_datetime<format_datetime>` filter, but without the date.
|
||||||
|
|
||||||
|
When no format or pattern is provided, Twig uses the
|
||||||
|
:ref:`application default time format <intl-date-format-defaults>`, or
|
||||||
|
``medium`` when none is configured. To use a custom pattern, pass it explicitly.
|
||||||
|
The :ref:`application default pattern <intl-date-format-defaults>` is not used
|
||||||
|
for time-only formatting.
|
||||||
|
|
||||||
.. note::
|
.. note::
|
||||||
|
|
||||||
@@ -31,8 +38,8 @@ Arguments
|
|||||||
|
|
||||||
* ``locale``: The locale code as defined in `RFC 5646`_
|
* ``locale``: The locale code as defined in `RFC 5646`_
|
||||||
* ``timeFormat``: The time format
|
* ``timeFormat``: The time format
|
||||||
* ``pattern``: A date time pattern
|
* ``pattern``: A time pattern
|
||||||
* ``timezone``: The date timezone
|
* ``timezone``: The date timezone
|
||||||
* ``calendar``: The calendar ("gregorian" by default)
|
* ``calendar``: The calendar
|
||||||
|
|
||||||
.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646
|
.. _RFC 5646: https://www.rfc-editor.org/info/rfc5646
|
||||||
|
|||||||
@@ -405,7 +405,7 @@ final class IntlExtension extends AbstractExtension
|
|||||||
* @param \DateTimeInterface|string|null $date A date or null to use the current time
|
* @param \DateTimeInterface|string|null $date A date or null to use the current time
|
||||||
* @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged
|
* @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged
|
||||||
*/
|
*/
|
||||||
public function formatDateTime(Environment $env, $date, ?string $dateFormat = null, ?string $timeFormat = null, string $pattern = '', $timezone = null, string $calendar = 'gregorian', ?string $locale = null): string
|
public function formatDateTime(Environment $env, $date, ?string $dateFormat = null, ?string $timeFormat = null, string $pattern = '', $timezone = null, ?string $calendar = null, ?string $locale = null): string
|
||||||
{
|
{
|
||||||
$date = $env->getExtension(CoreExtension::class)->convertDate($date, $timezone);
|
$date = $env->getExtension(CoreExtension::class)->convertDate($date, $timezone);
|
||||||
|
|
||||||
@@ -428,7 +428,7 @@ final class IntlExtension extends AbstractExtension
|
|||||||
* @param \DateTimeInterface|string|null $date A date or null to use the current time
|
* @param \DateTimeInterface|string|null $date A date or null to use the current time
|
||||||
* @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged
|
* @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged
|
||||||
*/
|
*/
|
||||||
public function formatDate(Environment $env, $date, ?string $dateFormat = null, string $pattern = '', $timezone = null, string $calendar = 'gregorian', ?string $locale = null): string
|
public function formatDate(Environment $env, $date, ?string $dateFormat = null, string $pattern = '', $timezone = null, ?string $calendar = null, ?string $locale = null): string
|
||||||
{
|
{
|
||||||
return $this->formatDateTime($env, $date, $dateFormat, 'none', $pattern, $timezone, $calendar, $locale);
|
return $this->formatDateTime($env, $date, $dateFormat, 'none', $pattern, $timezone, $calendar, $locale);
|
||||||
}
|
}
|
||||||
@@ -437,7 +437,7 @@ final class IntlExtension extends AbstractExtension
|
|||||||
* @param \DateTimeInterface|string|null $date A date or null to use the current time
|
* @param \DateTimeInterface|string|null $date A date or null to use the current time
|
||||||
* @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged
|
* @param \DateTimeZone|string|false|null $timezone The target timezone, null to use the default, false to leave unchanged
|
||||||
*/
|
*/
|
||||||
public function formatTime(Environment $env, $date, ?string $timeFormat = null, string $pattern = '', $timezone = null, string $calendar = 'gregorian', ?string $locale = null): string
|
public function formatTime(Environment $env, $date, ?string $timeFormat = null, string $pattern = '', $timezone = null, ?string $calendar = null, ?string $locale = null): string
|
||||||
{
|
{
|
||||||
return $this->formatDateTime($env, $date, 'none', $timeFormat, $pattern, $timezone, $calendar, $locale);
|
return $this->formatDateTime($env, $date, 'none', $timeFormat, $pattern, $timezone, $calendar, $locale);
|
||||||
}
|
}
|
||||||
@@ -454,7 +454,7 @@ final class IntlExtension extends AbstractExtension
|
|||||||
return $this->createListFormatter($locale, $type, $width)->format($strings);
|
return $this->createListFormatter($locale, $type, $width)->format($strings);
|
||||||
}
|
}
|
||||||
|
|
||||||
private function createDateFormatter(?string $locale, ?string $dateFormat, ?string $timeFormat, string $pattern, ?\DateTimeZone $timezone, string $calendar): \IntlDateFormatter
|
private function createDateFormatter(?string $locale, ?string $dateFormat, ?string $timeFormat, string $pattern, ?\DateTimeZone $timezone, ?string $calendar): \IntlDateFormatter
|
||||||
{
|
{
|
||||||
$dateFormats = self::availableDateFormats();
|
$dateFormats = self::availableDateFormats();
|
||||||
|
|
||||||
@@ -473,16 +473,25 @@ final class IntlExtension extends AbstractExtension
|
|||||||
$locale = $locale ?: \Locale::getDefault();
|
$locale = $locale ?: \Locale::getDefault();
|
||||||
}
|
}
|
||||||
|
|
||||||
$calendar = 'gregorian' === $calendar ? \IntlDateFormatter::GREGORIAN : \IntlDateFormatter::TRADITIONAL;
|
$calendar = null === $calendar ? null : ('gregorian' === $calendar ? \IntlDateFormatter::GREGORIAN : \IntlDateFormatter::TRADITIONAL);
|
||||||
|
|
||||||
$dateFormatValue = null === $dateFormat ? null : $dateFormats[$dateFormat];
|
$dateFormatValue = null === $dateFormat ? null : $dateFormats[$dateFormat];
|
||||||
$timeFormatValue = null === $timeFormat ? null : self::TIME_FORMATS[$timeFormat];
|
$timeFormatValue = null === $timeFormat ? null : self::TIME_FORMATS[$timeFormat];
|
||||||
|
|
||||||
if ($this->dateFormatterPrototype) {
|
if ($this->dateFormatterPrototype) {
|
||||||
$dateFormatValue ??= $this->dateFormatterPrototype->getDateType();
|
if (null === $dateFormatValue && false !== $prototypeDateFormat = $this->dateFormatterPrototype->getDateType()) {
|
||||||
$timeFormatValue ??= $this->dateFormatterPrototype->getTimeType();
|
$dateFormatValue = $prototypeDateFormat;
|
||||||
|
}
|
||||||
|
if (null === $timeFormatValue && false !== $prototypeTimeFormat = $this->dateFormatterPrototype->getTimeType()) {
|
||||||
|
$timeFormatValue = $prototypeTimeFormat;
|
||||||
|
}
|
||||||
$timezone = $timezone ?: $this->dateFormatterPrototype->getTimeZone()->toDateTimeZone();
|
$timezone = $timezone ?: $this->dateFormatterPrototype->getTimeZone()->toDateTimeZone();
|
||||||
$calendar = $calendar ?: $this->dateFormatterPrototype->getCalendar();
|
if (null === $calendar) {
|
||||||
|
$calendar = $this->dateFormatterPrototype->getCalendar();
|
||||||
|
if (false === $calendar) {
|
||||||
|
$calendar = $this->dateFormatterPrototype->getCalendarObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
// fall back to the prototype's pattern only when nothing else was given, else it would override the explicit date/time formats;
|
// fall back to the prototype's pattern only when nothing else was given, else it would override the explicit date/time formats;
|
||||||
// a pattern describes a full datetime rendering, so it cannot be honored by format_date/format_time, which pass 'none' for the other part
|
// a pattern describes a full datetime rendering, so it cannot be honored by format_date/format_time, which pass 'none' for the other part
|
||||||
if ('' === $pattern && null === $dateFormat && null === $timeFormat) {
|
if ('' === $pattern && null === $dateFormat && null === $timeFormat) {
|
||||||
@@ -492,9 +501,15 @@ final class IntlExtension extends AbstractExtension
|
|||||||
|
|
||||||
$dateFormatValue ??= \IntlDateFormatter::MEDIUM;
|
$dateFormatValue ??= \IntlDateFormatter::MEDIUM;
|
||||||
$timeFormatValue ??= \IntlDateFormatter::MEDIUM;
|
$timeFormatValue ??= \IntlDateFormatter::MEDIUM;
|
||||||
|
if (null === $calendar || false === $calendar) {
|
||||||
|
$calendar = \IntlDateFormatter::GREGORIAN;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($calendar instanceof \IntlCalendar) {
|
||||||
|
return new \IntlDateFormatter($locale, $dateFormatValue, $timeFormatValue, $timezone, $calendar, $pattern);
|
||||||
|
}
|
||||||
|
|
||||||
$timezoneName = $timezone ? $timezone->getName() : '(none)';
|
$timezoneName = $timezone ? $timezone->getName() : '(none)';
|
||||||
|
|
||||||
$hash = $locale.'|'.$dateFormatValue.'|'.$timeFormatValue.'|'.$timezoneName.'|'.$calendar.'|'.$pattern;
|
$hash = $locale.'|'.$dateFormatValue.'|'.$timeFormatValue.'|'.$timezoneName.'|'.$calendar.'|'.$pattern;
|
||||||
|
|
||||||
if (!isset($this->dateFormatters[$hash])) {
|
if (!isset($this->dateFormatters[$hash])) {
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ class IntlExtensionTest extends TestCase
|
|||||||
$this->assertContains(
|
$this->assertContains(
|
||||||
$ext->formatDateTime($env, new \DateTime('2020-02-20T13:37:00+00:00', new \DateTimeZone('Europe/Paris'))),
|
$ext->formatDateTime($env, new \DateTime('2020-02-20T13:37:00+00:00', new \DateTimeZone('Europe/Paris'))),
|
||||||
[
|
[
|
||||||
'jeudi 20 février 2020 à 13:37:00 heure normale d’Europe centrale',
|
'jeudi 20 février 2020 à 13:37:00 heure normale d’Europe centrale', // codespell:ignore normale
|
||||||
'jeudi 20 février 2020 à 13:37:00 temps universel coordonné',
|
'jeudi 20 février 2020 à 13:37:00 temps universel coordonné',
|
||||||
]
|
]
|
||||||
);
|
);
|
||||||
@@ -109,6 +109,76 @@ class IntlExtensionTest extends TestCase
|
|||||||
$this->assertSame('donderdag 20 februari 2020', $ext->formatDateTime($env, $date, 'full', 'none'));
|
$this->assertSame('donderdag 20 februari 2020', $ext->formatDateTime($env, $date, 'full', 'none'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public function testFormatterCalendarPrecedence(): void
|
||||||
|
{
|
||||||
|
$env = new Environment(new ArrayLoader());
|
||||||
|
$date = new \DateTime('2020-02-20T00:00:00+00:00');
|
||||||
|
$gregorianProto = new \IntlDateFormatter('th_TH', \IntlDateFormatter::NONE, \IntlDateFormatter::NONE, 'UTC', \IntlDateFormatter::GREGORIAN);
|
||||||
|
$traditionalProto = new \IntlDateFormatter('th_TH', \IntlDateFormatter::NONE, \IntlDateFormatter::NONE, 'UTC', \IntlDateFormatter::TRADITIONAL);
|
||||||
|
$hebrewProto = new \IntlDateFormatter('en_US', \IntlDateFormatter::NONE, \IntlDateFormatter::NONE, 'UTC', \IntlCalendar::createInstance('UTC', 'en_US@calendar=hebrew'));
|
||||||
|
$failedCalendarProto = new class('th_TH', \IntlDateFormatter::NONE, \IntlDateFormatter::NONE, 'UTC') extends \IntlDateFormatter {
|
||||||
|
public function getCalendar(): int|false
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getCalendarObject(): \IntlCalendar|false|null
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
$this->assertSame('2563', (new IntlExtension($gregorianProto))->formatDate($env, $date, pattern: 'yyyy', timezone: 'UTC', calendar: 'traditional', locale: 'th_TH'));
|
||||||
|
$this->assertSame('2020', (new IntlExtension($traditionalProto))->formatDate($env, $date, pattern: 'yyyy', timezone: 'UTC', calendar: 'gregorian', locale: 'th_TH'));
|
||||||
|
$this->assertSame('2563', (new IntlExtension($traditionalProto))->formatDate($env, $date, pattern: 'yyyy', timezone: 'UTC', locale: 'th_TH'));
|
||||||
|
$this->assertSame('5780', (new IntlExtension($hebrewProto))->formatDate($env, $date, pattern: 'yyyy', timezone: 'UTC', locale: 'en_US'));
|
||||||
|
$this->assertSame('2020', (new IntlExtension($failedCalendarProto))->formatDate($env, $date, pattern: 'yyyy', timezone: 'UTC', locale: 'th_TH'));
|
||||||
|
$this->assertSame('2020', (new IntlExtension())->formatDate($env, $date, pattern: 'yyyy', timezone: 'UTC', locale: 'th_TH'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatterObjectCalendarChangesAreApplied(): void
|
||||||
|
{
|
||||||
|
$env = new Environment(new ArrayLoader());
|
||||||
|
$date = new \DateTime('2021-01-01T00:00:00+00:00');
|
||||||
|
$calendar = \IntlCalendar::createInstance('UTC', 'en_US@calendar=gregorian');
|
||||||
|
$calendar->setFirstDayOfWeek(\IntlCalendar::DOW_MONDAY);
|
||||||
|
$calendar->setMinimalDaysInFirstWeek(4);
|
||||||
|
$proto = new \IntlDateFormatter('en_US', \IntlDateFormatter::NONE, \IntlDateFormatter::NONE, 'UTC', $calendar);
|
||||||
|
$ext = new IntlExtension($proto);
|
||||||
|
|
||||||
|
$this->assertSame('2020-53', $ext->formatDate($env, $date, pattern: 'Y-ww', timezone: 'UTC', locale: 'en_US'));
|
||||||
|
|
||||||
|
$calendar = \IntlCalendar::createInstance('UTC', 'en_US@calendar=gregorian');
|
||||||
|
$calendar->setFirstDayOfWeek(\IntlCalendar::DOW_SUNDAY);
|
||||||
|
$calendar->setMinimalDaysInFirstWeek(1);
|
||||||
|
$proto->setCalendar($calendar);
|
||||||
|
|
||||||
|
$this->assertSame('2021-01', $ext->formatDate($env, $date, pattern: 'Y-ww', timezone: 'UTC', locale: 'en_US'));
|
||||||
|
}
|
||||||
|
|
||||||
|
public function testFormatterProtoFormatFailuresFallBackToMedium(): void
|
||||||
|
{
|
||||||
|
$env = new Environment(new ArrayLoader());
|
||||||
|
$date = new \DateTime('2020-02-20T00:00:00+00:00');
|
||||||
|
$proto = new class('en_US', \IntlDateFormatter::FULL, \IntlDateFormatter::FULL, 'UTC') extends \IntlDateFormatter {
|
||||||
|
public function getDateType(): int|false
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getTimeType(): int|false
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
$ext = new IntlExtension($proto);
|
||||||
|
$expectedDate = (new \IntlDateFormatter('en_US', \IntlDateFormatter::MEDIUM, \IntlDateFormatter::NONE, 'UTC', \IntlDateFormatter::GREGORIAN))->format($date);
|
||||||
|
$expectedTime = (new \IntlDateFormatter('en_US', \IntlDateFormatter::NONE, \IntlDateFormatter::MEDIUM, 'UTC', \IntlDateFormatter::GREGORIAN))->format($date);
|
||||||
|
|
||||||
|
$this->assertSame($expectedDate, $ext->formatDate($env, $date, timezone: 'UTC', locale: 'en_US'));
|
||||||
|
$this->assertSame($expectedTime, $ext->formatTime($env, $date, timezone: 'UTC', locale: 'en_US'));
|
||||||
|
}
|
||||||
|
|
||||||
public function testFormatterProtoWithCustomPatternIsUsedByDefault(): void
|
public function testFormatterProtoWithCustomPatternIsUsedByDefault(): void
|
||||||
{
|
{
|
||||||
$dateFormatterProto = new \IntlDateFormatter('nl_NL', \IntlDateFormatter::MEDIUM, \IntlDateFormatter::MEDIUM, new \DateTimeZone('Europe/Amsterdam'), \IntlDateFormatter::GREGORIAN, 'yyyy-MM-dd');
|
$dateFormatterProto = new \IntlDateFormatter('nl_NL', \IntlDateFormatter::MEDIUM, \IntlDateFormatter::MEDIUM, new \DateTimeZone('Europe/Amsterdam'), \IntlDateFormatter::GREGORIAN, 'yyyy-MM-dd');
|
||||||
|
|||||||
Reference in New Issue
Block a user