diff --git a/.github/workflows/php.yml b/.github/workflows/php.yml index b93d850..d6b89c8 100644 --- a/.github/workflows/php.yml +++ b/.github/workflows/php.yml @@ -8,17 +8,31 @@ permissions: jobs: build: - runs-on: ubuntu-latest + runs-on: ${{ matrix.operating-system }} + + strategy: + matrix: + operating-system: [ubuntu-latest, windows-latest, macOS-latest] + php-versions: ['7.4', '8.0', '8.1', '8.2', '8.3'] + + name: PHP ${{ matrix.php-versions }} Test on ${{ matrix.operating-system }} steps: - - uses: actions/checkout@v3 + - name: Checkout + uses: actions/checkout@v4 + + - name: Install PHP + uses: shivammathur/setup-php@v2 + with: + php-version: ${{ matrix.php-versions }} + extensions: intl - name: Validate composer.json and composer.lock run: composer validate --strict - name: Cache Composer packages id: composer-cache - uses: actions/cache@v3 + uses: actions/cache@v4 with: path: vendor key: ${{ runner.os }}-php-${{ hashFiles('**/composer.lock') }} diff --git a/src/IntlLocaleFormatter.php b/src/IntlLocaleFormatter.php index 4100fd7..5bb62e4 100644 --- a/src/IntlLocaleFormatter.php +++ b/src/IntlLocaleFormatter.php @@ -66,7 +66,9 @@ public function __invoke(DateTimeInterface $timestamp, string $format) // in formatted strings. // To adjust for this, a custom calendar can be supplied with a cutover date arbitrarily far in the past. $calendar = IntlGregorianCalendar::createInstance(); - $calendar->setGregorianChange(PHP_INT_MIN); + // NOTE: IntlGregorianCalendar::createInstance DOES NOT return an IntlGregorianCalendar instance when + // using a non-Gregorian locale (e.g. fa_IR)! In that case, setGregorianChange will not exist. + if (method_exists($calendar, 'setGregorianChange')) $calendar->setGregorianChange(PHP_INT_MIN); return (new IntlDateFormatter($this->locale, $date_type, $time_type, $tz, $calendar, $pattern))->format($timestamp); } diff --git a/src/php-8.1-strftime.php b/src/php-8.1-strftime.php index dfd6488..f8a8c79 100644 --- a/src/php-8.1-strftime.php +++ b/src/php-8.1-strftime.php @@ -38,14 +38,8 @@ function strftime (string $format, $timestamp = null, ?string $locale = null) : $timestamp->setTimezone(new DateTimeZone(date_default_timezone_get())); - if (empty($locale)) { - // get current locale - $locale = setlocale(LC_TIME, '0'); - } - // remove trailing part not supported by ext-intl locale - $locale = preg_replace('/[^\w-].*$/', '', $locale); - if (class_exists('\\IntlDateFormatter') && !isset($_SERVER['STRFTIME_NO_INTL'])) { + $locale = \Locale::canonicalize($locale ?? setlocale(LC_TIME, '0')); $locale_formatter = new \PHP81_BC\strftime\IntlLocaleFormatter($locale); } else { $locale_formatter = new \PHP81_BC\strftime\DateLocaleFormatter($locale); diff --git a/tests/DateLocaleFormatterTest.php b/tests/DateLocaleFormatterTest.php index 2223bbe..6b8a84d 100644 --- a/tests/DateLocaleFormatterTest.php +++ b/tests/DateLocaleFormatterTest.php @@ -13,9 +13,17 @@ class DateLocaleFormatterTest extends TestCase { public static function setUpBeforeClass () : void { date_default_timezone_set('Europe/Madrid'); $_SERVER['STRFTIME_NO_INTL'] = true; + + set_error_handler( + static function ( $errno, $errstr ) { + throw new \ErrorException( $errstr, $errno ); + }, + E_ALL + ); } public static function tearDownAfterClass () : void { unset($_SERVER['STRFTIME_NO_INTL']); + restore_error_handler(); } } diff --git a/tests/LocaleTests/Locale_en_EN_TestTrait.php b/tests/LocaleTests/Locale_en_EN_TestTrait.php index 48ac87f..1446364 100644 --- a/tests/LocaleTests/Locale_en_EN_TestTrait.php +++ b/tests/LocaleTests/Locale_en_EN_TestTrait.php @@ -11,8 +11,8 @@ public function testLocale_en_EN () { $locale = 'en-EN'; if (__CLASS__ == 'PHP81_BC\Tests\DateLocaleFormatterTest') { - $this->expectNotice(); - $this->expectNoticeMessage('Formatting without \\IntlDateFormatter only return english formats'); + $this->expectException(\ErrorException::class); + $this->expectExceptionMessage('Formatting without \\IntlDateFormatter only return english formats'); } $result = strftime('%a', '20220306 13:02:03', $locale); @@ -31,10 +31,16 @@ public function testLocale_en_EN () { $this->assertEquals('Mar', $result, '%h: Abbreviated month name, based on the locale (an alias of %b)'); $result = strftime('%X', '20220306 13:02:03', $locale); - $this->assertEquals('1:02:03 PM', $result, '%X: Preferred time representation based on locale, without the date'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('1:02:03 PM'), // PHP-7 + $this->equalTo('13:02:03') // PHP-8 + ), '%X: Preferred time representation based on locale, without the date'); $result = strftime('%c', '20220306 13:02:03', $locale); - $this->assertEquals('March 6, 2022 at 1:02 PM', $result, '%c: Preferred date and time stamp based on locale'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('March 6, 2022 at 1:02 PM'), // PHP-7 + $this->equalTo('March 6, 2022 at 13:02') // PHP-8 + ), '%c: Preferred date and time stamp based on locale'); $result = strftime('%x', '20220306 13:02:03', $locale); $this->assertEquals('3/6/22', $result, '%x: Preferred date representation based on locale, without the time'); diff --git a/tests/LocaleTests/Locale_es_ES_TestTrait.php b/tests/LocaleTests/Locale_es_ES_TestTrait.php index ff1a491..5577831 100644 --- a/tests/LocaleTests/Locale_es_ES_TestTrait.php +++ b/tests/LocaleTests/Locale_es_ES_TestTrait.php @@ -17,19 +17,28 @@ public function testLocale_es_ES () { } $result = strftime('%a', '20220306 13:02:03', $locale); - $this->assertEquals('dom.', $result, '%a: An abbreviated textual representation of the day'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('dom.'), // PHP-7 + $this->equalTo('dom') // PHP-8 + ), '%a: An abbreviated textual representation of the day'); $result = strftime('%A', '20220306 13:02:03', $locale); $this->assertEquals('domingo', $result, '%A: A full textual representation of the day'); $result = strftime('%b', '20220306 13:02:03', $locale); - $this->assertEquals('mar.', $result, '%b: Abbreviated month name, based on the locale'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('mar.'), // PHP-7 + $this->equalTo('mar') // PHP-8 + ), '%b: Abbreviated month name, based on the locale'); $result = strftime('%B', '20220306 13:02:03', $locale); $this->assertEquals('marzo', $result, '%B: Full month name, based on the locale'); $result = strftime('%h', '20220306 13:02:03', $locale); - $this->assertEquals('mar.', $result, '%h: Abbreviated month name, based on the locale (an alias of %b)'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('mar.'), // PHP-7 + $this->equalTo('mar') // PHP-8 + ), '%h: Abbreviated month name, based on the locale (an alias of %b)'); $result = strftime('%X', '20220306 13:02:03', $locale); $this->assertEquals('13:02:03', $result, '%X: Preferred time representation based on locale, without the date'); diff --git a/tests/LocaleTests/Locale_eu_TestTrait.php b/tests/LocaleTests/Locale_eu_TestTrait.php index bbe67af..75b7b28 100644 --- a/tests/LocaleTests/Locale_eu_TestTrait.php +++ b/tests/LocaleTests/Locale_eu_TestTrait.php @@ -26,7 +26,10 @@ public function testLocale_eu () { $this->assertEquals('mar.', $result, '%b: Abbreviated month name, based on the locale'); $result = strftime('%B', '20220306 13:02:03', $locale); - $this->assertEquals('martxoa', $result, '%B: Full month name, based on the locale'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('martxoak'), + $this->equalTo('martxoa') // PHP-7.4, PHP-8.3 + ), '%B: Full month name, based on the locale'); $result = strftime('%h', '20220306 13:02:03', $locale); $this->assertEquals('mar.', $result, '%h: Abbreviated month name, based on the locale (an alias of %b)'); @@ -35,7 +38,11 @@ public function testLocale_eu () { $this->assertEquals('13:02:03', $result, '%X: Preferred time representation based on locale, without the date'); $result = strftime('%c', '20220306 13:02:03', $locale); - $this->assertEquals('2022(e)ko martxoaren 6(a) 13:02', $result, '%c: Preferred date and time stamp based on locale'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('2022(e)ko martxoaren 6(a) 13:02'), // PHP-7.4 + $this->equalTo('2022(e)ko martxoakren 6(a) 13:02'), + $this->equalTo('2022(e)ko martxoaren 6(a) (13:02)') // PHP-8.3 + ), '%c: Preferred date and time stamp based on locale'); $result = strftime('%x', '20220306 13:02:03', $locale); $this->assertEquals('22/3/6', $result, '%x: Preferred date representation based on locale, without the time'); diff --git a/tests/LocaleTests/Locale_it_CH_TestTrait.php b/tests/LocaleTests/Locale_it_CH_TestTrait.php index c7abaa4..16fb747 100644 --- a/tests/LocaleTests/Locale_it_CH_TestTrait.php +++ b/tests/LocaleTests/Locale_it_CH_TestTrait.php @@ -35,7 +35,10 @@ public function testLocale_it_CH () { $this->assertEquals('13:02:03', $result, '%X: Preferred time representation based on locale, without the date'); $result = strftime('%c', '20220306 13:02:03', $locale); - $this->assertEquals('6 marzo 2022 13:02', $result, '%c: Preferred date and time stamp based on locale'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('6 marzo 2022 13:02'), + $this->equalTo('6 marzo 2022 alle ore 13:02') // PHP-8.3 + ), '%c: Preferred date and time stamp based on locale'); $result = strftime('%x', '20220306 13:02:03', $locale); $this->assertEquals('06.03.22', $result, '%x: Preferred date representation based on locale, without the time'); diff --git a/tests/LocaleTests/Locale_it_IT_TestTrait.php b/tests/LocaleTests/Locale_it_IT_TestTrait.php index 00e9076..ef2736c 100644 --- a/tests/LocaleTests/Locale_it_IT_TestTrait.php +++ b/tests/LocaleTests/Locale_it_IT_TestTrait.php @@ -35,7 +35,10 @@ public function testLocale_it_IT () { $this->assertEquals('13:02:03', $result, '%X: Preferred time representation based on locale, without the date'); $result = strftime('%c', '20220306 13:02:03', $locale); - $this->assertEquals('6 marzo 2022 13:02', $result, '%c: Preferred date and time stamp based on locale'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('6 marzo 2022 13:02'), + $this->equalTo('6 marzo 2022 alle ore 13:02') // PHP-8.3 + ), '%c: Preferred date and time stamp based on locale'); $result = strftime('%x', '20220306 13:02:03', $locale); $this->assertEquals('06/03/22', $result, '%x: Preferred date representation based on locale, without the time'); diff --git a/tests/strftimeTest.php b/tests/strftimeTest.php index 35f0e23..48d86a6 100644 --- a/tests/strftimeTest.php +++ b/tests/strftimeTest.php @@ -133,7 +133,10 @@ public function testTimeFormats () { $this->assertEquals('13:02:03', $result, '%T: Same as "%H:%M:%S"'); $result = strftime('%X', '20220306 13:02:03', 'en-EN'); - $this->assertEquals('1:02:03 PM', $result, '%X: Preferred time representation based on locale, without the date'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('1:02:03 PM'), + $this->equalTo('13:02:03') // PHP-8 + ), '%X: Preferred time representation based on locale, without the date'); $result = strftime('%z', '20220306 13:02:03'); $this->assertEquals('+0100', $result, '%z: The time zone offset'); @@ -144,7 +147,10 @@ public function testTimeFormats () { public function testStampsFormats () { $result = strftime('%c', '20220306 13:02:03', 'en-EN'); - $this->assertEquals('March 6, 2022 at 1:02 PM', $result, '%c: Preferred date and time stamp based on locale'); + $this->assertThat($result, $this->logicalOr( + $this->equalTo('March 6, 2022 at 1:02 PM'), + $this->equalTo('March 6, 2022 at 13:02') // PHP-8 + ), '%c: Preferred date and time stamp based on locale'); $result = strftime('%D', '20220306 13:02:03'); $this->assertEquals('03/06/2022', $result, '%D: Same as "%m/%d/%y"');