Skip to content

Commit a9f63ee

Browse files
committed
[Intl][Form] Update tests, TimeZoneTransformer, and DateTimeToLocalizedStringTransformer for the GMT and UTC split in ICU
The [GMT timezone has been split from the UTC](http://site.icu-project.org/download/59) timezone [in CLDR](http://cldr.unicode.org/index/downloads/cldr-31) (which ICU is based on). For example, the code blow: * before ICU 59.1 would return "GMT" in all cases * with ICU 59.1 it returns "UTC" for the first three ('z', 'zz', 'zzz') and "Coordinated Universal Time" for the last two ('zzzz', 'zzzzz'). ```php foreach (['z', 'zz', 'zzz', 'zzzz', 'zzzzz'] as $pattern) { $formatter = new \IntlDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, new \DateTimeZone('UTC'), IntlDateFormatter::GREGORIAN, $pattern); var_dump($formatter->format(new \DateTime('@0'))); } ``` Similarly Form's `DateTimeToLocalizedStringTransformer` is also affected: ```php $transformer = new DateTimeToLocalizedStringTransformer('UTC', 'UTC', null, \IntlDateFormatter::FULL); var_dump($transformer->transform(new \DateTime('2010-02-03 04:05:06 UTC'))); // ICU 58.2: '03.02.2010, 04:05:06 GMT' // ICU 59.1: '03.02.2010, 04:05:06 Koordinierte Weltzeit' ``` Refer to added and modified test cases for more changes. I split this PR in two commits for easier review. First commit updates ICU data (generated files), the second updates code and test cases to be compatible with updated data.
1 parent 48076a1 commit a9f63ee

File tree

4 files changed

+144
-17
lines changed

4 files changed

+144
-17
lines changed

DateFormatter/DateFormat/TimeZoneTransformer.php

Lines changed: 22 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,10 +33,29 @@ public function format(\DateTime $dateTime, $length)
3333
throw new NotImplementedException('Time zone different than GMT or UTC is not supported as a formatting output.');
3434
}
3535

36-
// From ICU >= 4.8, the zero offset is not more used, example: GMT instead of GMT+00:00
37-
$format = (0 !== (int) $dateTime->format('O')) ? '\G\M\TP' : '\G\M\T';
36+
if ('Etc' === $timeZone) {
37+
// i.e. Etc/GMT+1, Etc/UTC, Etc/Zulu
38+
$timeZone = substr($dateTime->getTimezone()->getName(), 4);
39+
}
40+
41+
// From ICU >= 59.1 GMT and UTC are no longer unified
42+
if (in_array($timeZone, array('UTC', 'UCT', 'Universal', 'Zulu'))) {
43+
// offset is not supported with UTC
44+
return $length > 3 ? 'Coordinated Universal Time' : 'UTC';
45+
}
46+
47+
$offset = (int) $dateTime->format('O');
48+
49+
// From ICU >= 4.8, the zero offset is no more used, example: GMT instead of GMT+00:00
50+
if (0 === $offset) {
51+
return $length > 3 ? 'Greenwich Mean Time' : 'GMT';
52+
}
53+
54+
if ($length > 3) {
55+
return $dateTime->format('\G\M\TP');
56+
}
3857

39-
return $dateTime->format($format);
58+
return sprintf('GMT%s%d', ($offset >= 0 ? '+' : ''), $offset / 100);
4059
}
4160

4261
/**

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,7 @@ Resources
1515
* [Report issues](https://github.com/symfony/symfony/issues) and
1616
[send Pull Requests](https://github.com/symfony/symfony/pulls)
1717
in the [main Symfony repository](https://github.com/symfony/symfony)
18+
* [Docker images with intl support](https://hub.docker.com/r/jakzal/php-intl)
19+
(for the Intl component development)
1820

1921
[0]: http://www.php.net/manual/en/intl.setup.php

Tests/DateFormatter/AbstractIntlDateFormatterTest.php

Lines changed: 92 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -231,15 +231,8 @@ public function formatProvider()
231231
array('s', 43200, '0'), // 12 hours
232232

233233
// general
234-
array("yyyy.MM.dd 'at' HH:mm:ss zzz", 0, '1970.01.01 at 00:00:00 GMT'),
235-
array('K:mm a, z', 0, '0:00 AM, GMT'),
236-
237-
// timezone
238-
array('z', 0, 'GMT'),
239-
array('zz', 0, 'GMT'),
240-
array('zzz', 0, 'GMT'),
241-
array('zzzz', 0, 'GMT'),
242-
array('zzzzz', 0, 'GMT'),
234+
array("yyyy.MM.dd 'at' HH:mm:ss zzz", 0, '1970.01.01 at 00:00:00 UTC'),
235+
array('K:mm a, z', 0, '0:00 AM, UTC'),
243236
);
244237

245238
$dateTime = new \DateTime('@0');
@@ -250,12 +243,25 @@ public function formatProvider()
250243
$formatData[] = array('h:mm a', $dateTime, '12:00 AM');
251244
$formatData[] = array('yyyyy.MMMM.dd hh:mm aaa', $dateTime, '01970.January.01 12:00 AM');
252245

253-
$formatData[] = array("yyyy.MM.dd 'at' HH:mm:ss zzz", $dateTime, '1970.01.01 at 00:00:00 GMT');
254-
$formatData[] = array('K:mm a, z', $dateTime, '0:00 AM, GMT');
246+
$formatData[] = array("yyyy.MM.dd 'at' HH:mm:ss zzz", $dateTime, '1970.01.01 at 00:00:00 UTC');
247+
$formatData[] = array('K:mm a, z', $dateTime, '0:00 AM, UTC');
255248

256249
return $formatData;
257250
}
258251

252+
/**
253+
* @requires PHP 5.5.10
254+
*/
255+
public function testFormatUtcAndGmtAreSplit()
256+
{
257+
$pattern = "yyyy.MM.dd 'at' HH:mm:ss zzz";
258+
$gmtFormatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, 'GMT', IntlDateFormatter::GREGORIAN, $pattern);
259+
$utcFormatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, 'UTC', IntlDateFormatter::GREGORIAN, $pattern);
260+
261+
$this->assertSame('1970.01.01 at 00:00:00 GMT', $gmtFormatter->format(new \DateTime('@0')));
262+
$this->assertSame('1970.01.01 at 00:00:00 UTC', $utcFormatter->format(new \DateTime('@0')));
263+
}
264+
259265
/**
260266
* @dataProvider formatErrorProvider
261267
*/
@@ -334,6 +340,75 @@ public function formatWithTimezoneProvider()
334340
return $data;
335341
}
336342

343+
/**
344+
* @dataProvider formatTimezoneProvider
345+
* @requires PHP 5.5
346+
*/
347+
public function testFormatTimezone($pattern, $timezone, $expected)
348+
{
349+
$formatter = $this->getDefaultDateFormatter($pattern);
350+
$formatter->setTimeZone(new \DateTimeZone($timezone));
351+
352+
$this->assertEquals($expected, $formatter->format(0));
353+
}
354+
355+
public function formatTimezoneProvider()
356+
{
357+
$cases = array(
358+
array('z', 'GMT', 'GMT'),
359+
array('zz', 'GMT', 'GMT'),
360+
array('zzz', 'GMT', 'GMT'),
361+
array('zzzz', 'GMT', 'Greenwich Mean Time'),
362+
array('zzzzz', 'GMT', 'Greenwich Mean Time'),
363+
364+
array('z', 'Etc/GMT', 'GMT'),
365+
array('zz', 'Etc/GMT', 'GMT'),
366+
array('zzz', 'Etc/GMT', 'GMT'),
367+
array('zzzz', 'Etc/GMT', 'Greenwich Mean Time'),
368+
array('zzzzz', 'Etc/GMT', 'Greenwich Mean Time'),
369+
370+
array('z', 'Etc/GMT+3', 'GMT-3'),
371+
array('zz', 'Etc/GMT+3', 'GMT-3'),
372+
array('zzz', 'Etc/GMT+3', 'GMT-3'),
373+
array('zzzz', 'Etc/GMT+3', 'GMT-03:00'),
374+
array('zzzzz', 'Etc/GMT+3', 'GMT-03:00'),
375+
376+
array('z', 'UTC', 'UTC'),
377+
array('zz', 'UTC', 'UTC'),
378+
array('zzz', 'UTC', 'UTC'),
379+
array('zzzz', 'UTC', 'Coordinated Universal Time'),
380+
array('zzzzz', 'UTC', 'Coordinated Universal Time'),
381+
382+
array('z', 'Etc/UTC', 'UTC'),
383+
array('zz', 'Etc/UTC', 'UTC'),
384+
array('zzz', 'Etc/UTC', 'UTC'),
385+
array('zzzz', 'Etc/UTC', 'Coordinated Universal Time'),
386+
array('zzzzz', 'Etc/UTC', 'Coordinated Universal Time'),
387+
388+
array('z', 'Etc/Universal', 'UTC'),
389+
array('z', 'Etc/Zulu', 'UTC'),
390+
array('z', 'Etc/UCT', 'UTC'),
391+
array('z', 'Etc/Greenwich', 'GMT'),
392+
array('zzzzz', 'Etc/Universal', 'Coordinated Universal Time'),
393+
array('zzzzz', 'Etc/Zulu', 'Coordinated Universal Time'),
394+
array('zzzzz', 'Etc/UCT', 'Coordinated Universal Time'),
395+
array('zzzzz', 'Etc/Greenwich', 'Greenwich Mean Time'),
396+
);
397+
398+
if (!defined('HHVM_VERSION')) {
399+
// these timezones are not considered valid in HHVM
400+
$cases = array_merge($cases, array(
401+
array('z', 'GMT+03:00', 'GMT+3'),
402+
array('zz', 'GMT+03:00', 'GMT+3'),
403+
array('zzz', 'GMT+03:00', 'GMT+3'),
404+
array('zzzz', 'GMT+03:00', 'GMT+03:00'),
405+
array('zzzzz', 'GMT+03:00', 'GMT+03:00'),
406+
));
407+
}
408+
409+
return $cases;
410+
}
411+
337412
public function testFormatWithGmtTimezone()
338413
{
339414
$formatter = $this->getDefaultDateFormatter('zzzz');
@@ -389,8 +464,11 @@ public function testFormatWithDateTimeZoneGmt()
389464
if (PHP_VERSION_ID < 50500 && !(extension_loaded('intl') && method_exists('IntlDateFormatter', 'setTimeZone'))) {
390465
$this->markTestSkipped('Only in PHP 5.5+ IntlDateFormatter allows to use DateTimeZone objects.');
391466
}
467+
if (PHP_VERSION_ID < 50510) {
468+
$this->markTestSkipped('Before PHP 5.5.10 the GMT timezone used to be converted to UTC.');
469+
}
392470

393-
$formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, new \DateTimeZone('GMT'), IntlDateFormatter::GREGORIAN, 'zzzz');
471+
$formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, new \DateTimeZone('GMT'), IntlDateFormatter::GREGORIAN, 'zzz');
394472

395473
$this->assertEquals('GMT', $formatter->format(0));
396474
}
@@ -482,8 +560,8 @@ public function dateAndTimeTypeProvider()
482560
array(0, IntlDateFormatter::LONG, IntlDateFormatter::NONE, 'January 1, 1970'),
483561
array(0, IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE, 'Jan 1, 1970'),
484562
array(0, IntlDateFormatter::SHORT, IntlDateFormatter::NONE, '1/1/70'),
485-
array(0, IntlDateFormatter::NONE, IntlDateFormatter::FULL, '12:00:00 AM GMT'),
486-
array(0, IntlDateFormatter::NONE, IntlDateFormatter::LONG, '12:00:00 AM GMT'),
563+
array(0, IntlDateFormatter::NONE, IntlDateFormatter::FULL, '12:00:00 AM Coordinated Universal Time'),
564+
array(0, IntlDateFormatter::NONE, IntlDateFormatter::LONG, '12:00:00 AM UTC'),
487565
array(0, IntlDateFormatter::NONE, IntlDateFormatter::MEDIUM, '12:00:00 AM'),
488566
array(0, IntlDateFormatter::NONE, IntlDateFormatter::SHORT, '12:00 AM'),
489567
);

Tests/DateFormatter/Verification/IntlDateFormatterTest.php

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,34 @@ public function testFormatWithTimezoneFromEnvironmentVariable()
4343
parent::testFormatWithTimezoneFromEnvironmentVariable();
4444
}
4545

46+
/**
47+
* @dataProvider formatTimezoneProvider
48+
* @requires PHP 5.5
49+
*/
50+
public function testFormatTimezone($pattern, $timezone, $expected)
51+
{
52+
IntlTestHelper::requireFullIntl($this, '59.1');
53+
54+
parent::testFormatTimezone($pattern, $timezone, $expected);
55+
}
56+
57+
public function testFormatUtcAndGmtAreSplit()
58+
{
59+
IntlTestHelper::requireFullIntl($this, '59.1');
60+
61+
parent::testFormatUtcAndGmtAreSplit();
62+
}
63+
64+
/**
65+
* @dataProvider dateAndTimeTypeProvider
66+
*/
67+
public function testDateAndTimeType($timestamp, $datetype, $timetype, $expected)
68+
{
69+
IntlTestHelper::requireFullIntl($this, '59.1');
70+
71+
parent::testDateAndTimeType($timestamp, $datetype, $timetype, $expected);
72+
}
73+
4674
protected function getDateFormatter($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null)
4775
{
4876
IntlTestHelper::requireFullIntl($this, '55.1');

0 commit comments

Comments
 (0)