AbstractIntlDateFormatterTest.php 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968
  1. <?php
  2. /*
  3. * This file is part of the Symfony package.
  4. *
  5. * (c) Fabien Potencier <fabien@symfony.com>
  6. *
  7. * For the full copyright and license information, please view the LICENSE
  8. * file that was distributed with this source code.
  9. */
  10. namespace Symfony\Component\Intl\Tests\DateFormatter;
  11. use PHPUnit\Framework\TestCase;
  12. use Symfony\Component\Intl\DateFormatter\IntlDateFormatter;
  13. use Symfony\Component\Intl\Globals\IntlGlobals;
  14. /**
  15. * Test case for IntlDateFormatter implementations.
  16. *
  17. * @author Bernhard Schussek <bschussek@gmail.com>
  18. */
  19. abstract class AbstractIntlDateFormatterTest extends TestCase
  20. {
  21. protected function setUp()
  22. {
  23. \Locale::setDefault('en');
  24. }
  25. /**
  26. * When a time zone is not specified, it uses the system default however it returns null in the getter method.
  27. *
  28. * @see StubIntlDateFormatterTest::testDefaultTimeZoneIntl()
  29. */
  30. public function testConstructorDefaultTimeZone()
  31. {
  32. $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT);
  33. $this->assertEquals(date_default_timezone_get(), $formatter->getTimeZoneId());
  34. $this->assertEquals(
  35. $this->getDateTime(0, $formatter->getTimeZoneId())->format('M j, Y, g:i A'),
  36. $formatter->format(0)
  37. );
  38. }
  39. /**
  40. * @dataProvider formatProvider
  41. */
  42. public function testFormat($pattern, $timestamp, $expected)
  43. {
  44. $errorCode = IntlGlobals::U_ZERO_ERROR;
  45. $errorMessage = 'U_ZERO_ERROR';
  46. $formatter = $this->getDefaultDateFormatter($pattern);
  47. $this->assertSame($expected, $formatter->format($timestamp));
  48. $this->assertIsIntlSuccess($formatter, $errorMessage, $errorCode);
  49. }
  50. public function formatProvider()
  51. {
  52. $dateTime = new \DateTime('@0');
  53. $formatData = array(
  54. /* general */
  55. array('y-M-d', 0, '1970-1-1'),
  56. array("EEE, MMM d, ''yy", 0, "Thu, Jan 1, '70"),
  57. array('h:mm a', 0, '12:00 AM'),
  58. array('yyyyy.MMMM.dd hh:mm aaa', 0, '01970.January.01 12:00 AM'),
  59. /* escaping */
  60. array("'M'", 0, 'M'),
  61. array("'yy'", 0, 'yy'),
  62. array("'''yy'", 0, "'yy"),
  63. array("''y", 0, "'1970"),
  64. array("''yy", 0, "'70"),
  65. array("H 'o'' clock'", 0, "0 o' clock"),
  66. /* month */
  67. array('M', 0, '1'),
  68. array('MM', 0, '01'),
  69. array('MMM', 0, 'Jan'),
  70. array('MMMM', 0, 'January'),
  71. array('MMMMM', 0, 'J'),
  72. array('MMMMMM', 0, '000001'),
  73. array('L', 0, '1'),
  74. array('LL', 0, '01'),
  75. array('LLL', 0, 'Jan'),
  76. array('LLLL', 0, 'January'),
  77. array('LLLLL', 0, 'J'),
  78. array('LLLLLL', 0, '000001'),
  79. /* year */
  80. array('y', 0, '1970'),
  81. array('yy', 0, '70'),
  82. array('yyy', 0, '1970'),
  83. array('yyyy', 0, '1970'),
  84. array('yyyyy', 0, '01970'),
  85. array('yyyyyy', 0, '001970'),
  86. /* day */
  87. array('d', 0, '1'),
  88. array('dd', 0, '01'),
  89. array('ddd', 0, '001'),
  90. /* quarter */
  91. array('Q', 0, '1'),
  92. array('QQ', 0, '01'),
  93. array('QQQ', 0, 'Q1'),
  94. array('QQQQ', 0, '1st quarter'),
  95. array('QQQQQ', 0, '1st quarter'),
  96. array('q', 0, '1'),
  97. array('qq', 0, '01'),
  98. array('qqq', 0, 'Q1'),
  99. array('qqqq', 0, '1st quarter'),
  100. array('qqqqq', 0, '1st quarter'),
  101. // 4 months
  102. array('Q', 7776000, '2'),
  103. array('QQ', 7776000, '02'),
  104. array('QQQ', 7776000, 'Q2'),
  105. array('QQQQ', 7776000, '2nd quarter'),
  106. // 7 months
  107. array('QQQQ', 15638400, '3rd quarter'),
  108. // 10 months
  109. array('QQQQ', 23587200, '4th quarter'),
  110. /* 12-hour (1-12) */
  111. array('h', 0, '12'),
  112. array('hh', 0, '12'),
  113. array('hhh', 0, '012'),
  114. array('h', 1, '12'),
  115. array('h', 3600, '1'),
  116. array('h', 43200, '12'), // 12 hours
  117. /* day of year */
  118. array('D', 0, '1'),
  119. array('D', 86400, '2'), // 1 day
  120. array('D', 31536000, '1'), // 1 year
  121. array('D', 31622400, '2'), // 1 year + 1 day
  122. /* day of week */
  123. array('E', 0, 'Thu'),
  124. array('EE', 0, 'Thu'),
  125. array('EEE', 0, 'Thu'),
  126. array('EEEE', 0, 'Thursday'),
  127. array('EEEEE', 0, 'T'),
  128. array('EEEEEE', 0, 'Th'),
  129. array('E', 1296540000, 'Tue'), // 2011-02-01
  130. array('E', 1296950400, 'Sun'), // 2011-02-06
  131. /* am/pm marker */
  132. array('a', 0, 'AM'),
  133. array('aa', 0, 'AM'),
  134. array('aaa', 0, 'AM'),
  135. array('aaaa', 0, 'AM'),
  136. // 12 hours
  137. array('a', 43200, 'PM'),
  138. array('aa', 43200, 'PM'),
  139. array('aaa', 43200, 'PM'),
  140. array('aaaa', 43200, 'PM'),
  141. /* 24-hour (0-23) */
  142. array('H', 0, '0'),
  143. array('HH', 0, '00'),
  144. array('HHH', 0, '000'),
  145. array('H', 1, '0'),
  146. array('H', 3600, '1'),
  147. array('H', 43200, '12'),
  148. array('H', 46800, '13'),
  149. /* 24-hour (1-24) */
  150. array('k', 0, '24'),
  151. array('kk', 0, '24'),
  152. array('kkk', 0, '024'),
  153. array('k', 1, '24'),
  154. array('k', 3600, '1'),
  155. array('k', 43200, '12'),
  156. array('k', 46800, '13'),
  157. /* 12-hour (0-11) */
  158. array('K', 0, '0'),
  159. array('KK', 0, '00'),
  160. array('KKK', 0, '000'),
  161. array('K', 1, '0'),
  162. array('K', 3600, '1'),
  163. array('K', 43200, '0'), // 12 hours
  164. /* minute */
  165. array('m', 0, '0'),
  166. array('mm', 0, '00'),
  167. array('mmm', 0, '000'),
  168. array('m', 1, '0'),
  169. array('m', 60, '1'),
  170. array('m', 120, '2'),
  171. array('m', 180, '3'),
  172. array('m', 3600, '0'),
  173. array('m', 3660, '1'),
  174. array('m', 43200, '0'), // 12 hours
  175. /* second */
  176. array('s', 0, '0'),
  177. array('ss', 0, '00'),
  178. array('sss', 0, '000'),
  179. array('s', 1, '1'),
  180. array('s', 2, '2'),
  181. array('s', 5, '5'),
  182. array('s', 30, '30'),
  183. array('s', 59, '59'),
  184. array('s', 60, '0'),
  185. array('s', 120, '0'),
  186. array('s', 180, '0'),
  187. array('s', 3600, '0'),
  188. array('s', 3601, '1'),
  189. array('s', 3630, '30'),
  190. array('s', 43200, '0'), // 12 hours
  191. // general
  192. array("yyyy.MM.dd 'at' HH:mm:ss zzz", 0, '1970.01.01 at 00:00:00 UTC'),
  193. array('K:mm a, z', 0, '0:00 AM, UTC'),
  194. // general, DateTime
  195. array('y-M-d', $dateTime, '1970-1-1'),
  196. array("EEE, MMM d, ''yy", $dateTime, "Thu, Jan 1, '70"),
  197. array('h:mm a', $dateTime, '12:00 AM'),
  198. array('yyyyy.MMMM.dd hh:mm aaa', $dateTime, '01970.January.01 12:00 AM'),
  199. array("yyyy.MM.dd 'at' HH:mm:ss zzz", $dateTime, '1970.01.01 at 00:00:00 UTC'),
  200. array('K:mm a, z', $dateTime, '0:00 AM, UTC'),
  201. );
  202. return $formatData;
  203. }
  204. /**
  205. * @requires PHP 5.5.10
  206. */
  207. public function testFormatUtcAndGmtAreSplit()
  208. {
  209. $pattern = "yyyy.MM.dd 'at' HH:mm:ss zzz";
  210. $gmtFormatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, 'GMT', IntlDateFormatter::GREGORIAN, $pattern);
  211. $utcFormatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, 'UTC', IntlDateFormatter::GREGORIAN, $pattern);
  212. $this->assertSame('1970.01.01 at 00:00:00 GMT', $gmtFormatter->format(new \DateTime('@0')));
  213. $this->assertSame('1970.01.01 at 00:00:00 UTC', $utcFormatter->format(new \DateTime('@0')));
  214. }
  215. /**
  216. * @dataProvider formatErrorProvider
  217. */
  218. public function testFormatIllegalArgumentError($pattern, $timestamp, $errorMessage)
  219. {
  220. $errorCode = IntlGlobals::U_ILLEGAL_ARGUMENT_ERROR;
  221. $formatter = $this->getDefaultDateFormatter($pattern);
  222. $this->assertFalse($formatter->format($timestamp));
  223. $this->assertIsIntlFailure($formatter, $errorMessage, $errorCode);
  224. }
  225. public function formatErrorProvider()
  226. {
  227. return array(
  228. array('y-M-d', 'foobar', 'datefmt_format: string \'foobar\' is not numeric, which would be required for it to be a valid date: U_ILLEGAL_ARGUMENT_ERROR'),
  229. );
  230. }
  231. /**
  232. * @dataProvider formatWithTimezoneProvider
  233. */
  234. public function testFormatWithTimezone($timestamp, $timezone, $expected)
  235. {
  236. $pattern = 'yyyy-MM-dd HH:mm:ss';
  237. $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, $timezone, IntlDateFormatter::GREGORIAN, $pattern);
  238. $this->assertSame($expected, $formatter->format($timestamp));
  239. }
  240. public function formatWithTimezoneProvider()
  241. {
  242. $data = array(
  243. array(0, 'UTC', '1970-01-01 00:00:00'),
  244. array(0, 'GMT', '1970-01-01 00:00:00'),
  245. array(0, 'GMT-03:00', '1969-12-31 21:00:00'),
  246. array(0, 'GMT+03:00', '1970-01-01 03:00:00'),
  247. array(0, 'Europe/Zurich', '1970-01-01 01:00:00'),
  248. array(0, 'Europe/Paris', '1970-01-01 01:00:00'),
  249. array(0, 'Africa/Cairo', '1970-01-01 02:00:00'),
  250. array(0, 'Africa/Casablanca', '1970-01-01 00:00:00'),
  251. array(0, 'Africa/Djibouti', '1970-01-01 03:00:00'),
  252. array(0, 'Africa/Johannesburg', '1970-01-01 02:00:00'),
  253. array(0, 'America/Antigua', '1969-12-31 20:00:00'),
  254. array(0, 'America/Toronto', '1969-12-31 19:00:00'),
  255. array(0, 'America/Vancouver', '1969-12-31 16:00:00'),
  256. array(0, 'Asia/Aqtau', '1970-01-01 05:00:00'),
  257. array(0, 'Asia/Bangkok', '1970-01-01 07:00:00'),
  258. array(0, 'Asia/Dubai', '1970-01-01 04:00:00'),
  259. array(0, 'Australia/Brisbane', '1970-01-01 10:00:00'),
  260. array(0, 'Australia/Eucla', '1970-01-01 08:45:00'),
  261. array(0, 'Australia/Melbourne', '1970-01-01 10:00:00'),
  262. array(0, 'Europe/Berlin', '1970-01-01 01:00:00'),
  263. array(0, 'Europe/Dublin', '1970-01-01 01:00:00'),
  264. array(0, 'Europe/Warsaw', '1970-01-01 01:00:00'),
  265. array(0, 'Pacific/Fiji', '1970-01-01 12:00:00'),
  266. );
  267. return $data;
  268. }
  269. /**
  270. * @dataProvider formatTimezoneProvider
  271. * @requires PHP 5.5.10
  272. */
  273. public function testFormatTimezone($pattern, $timezone, $expected)
  274. {
  275. $formatter = $this->getDefaultDateFormatter($pattern);
  276. $formatter->setTimeZone(new \DateTimeZone($timezone));
  277. $this->assertEquals($expected, $formatter->format(0));
  278. }
  279. public function formatTimezoneProvider()
  280. {
  281. $cases = array(
  282. array('z', 'GMT', 'GMT'),
  283. array('zz', 'GMT', 'GMT'),
  284. array('zzz', 'GMT', 'GMT'),
  285. array('zzzz', 'GMT', 'Greenwich Mean Time'),
  286. array('zzzzz', 'GMT', 'Greenwich Mean Time'),
  287. array('z', 'Etc/GMT', 'GMT'),
  288. array('zz', 'Etc/GMT', 'GMT'),
  289. array('zzz', 'Etc/GMT', 'GMT'),
  290. array('zzzz', 'Etc/GMT', 'Greenwich Mean Time'),
  291. array('zzzzz', 'Etc/GMT', 'Greenwich Mean Time'),
  292. array('z', 'Etc/GMT+3', 'GMT-3'),
  293. array('zz', 'Etc/GMT+3', 'GMT-3'),
  294. array('zzz', 'Etc/GMT+3', 'GMT-3'),
  295. array('zzzz', 'Etc/GMT+3', 'GMT-03:00'),
  296. array('zzzzz', 'Etc/GMT+3', 'GMT-03:00'),
  297. array('z', 'UTC', 'UTC'),
  298. array('zz', 'UTC', 'UTC'),
  299. array('zzz', 'UTC', 'UTC'),
  300. array('zzzz', 'UTC', 'Coordinated Universal Time'),
  301. array('zzzzz', 'UTC', 'Coordinated Universal Time'),
  302. array('z', 'Etc/UTC', 'UTC'),
  303. array('zz', 'Etc/UTC', 'UTC'),
  304. array('zzz', 'Etc/UTC', 'UTC'),
  305. array('zzzz', 'Etc/UTC', 'Coordinated Universal Time'),
  306. array('zzzzz', 'Etc/UTC', 'Coordinated Universal Time'),
  307. array('z', 'Etc/Universal', 'UTC'),
  308. array('z', 'Etc/Zulu', 'UTC'),
  309. array('z', 'Etc/UCT', 'UTC'),
  310. array('z', 'Etc/Greenwich', 'GMT'),
  311. array('zzzzz', 'Etc/Universal', 'Coordinated Universal Time'),
  312. array('zzzzz', 'Etc/Zulu', 'Coordinated Universal Time'),
  313. array('zzzzz', 'Etc/UCT', 'Coordinated Universal Time'),
  314. array('zzzzz', 'Etc/Greenwich', 'Greenwich Mean Time'),
  315. );
  316. if (!defined('HHVM_VERSION')) {
  317. // these timezones are not considered valid in HHVM
  318. $cases = array_merge($cases, array(
  319. array('z', 'GMT+03:00', 'GMT+3'),
  320. array('zz', 'GMT+03:00', 'GMT+3'),
  321. array('zzz', 'GMT+03:00', 'GMT+3'),
  322. array('zzzz', 'GMT+03:00', 'GMT+03:00'),
  323. array('zzzzz', 'GMT+03:00', 'GMT+03:00'),
  324. ));
  325. }
  326. return $cases;
  327. }
  328. public function testFormatWithGmtTimezone()
  329. {
  330. $formatter = $this->getDefaultDateFormatter('zzzz');
  331. $formatter->setTimeZone('GMT+03:00');
  332. $this->assertEquals('GMT+03:00', $formatter->format(0));
  333. }
  334. public function testFormatWithGmtTimeZoneAndMinutesOffset()
  335. {
  336. $formatter = $this->getDefaultDateFormatter('zzzz');
  337. $formatter->setTimeZone('GMT+00:30');
  338. $this->assertEquals('GMT+00:30', $formatter->format(0));
  339. }
  340. public function testFormatWithNonStandardTimezone()
  341. {
  342. $formatter = $this->getDefaultDateFormatter('zzzz');
  343. $formatter->setTimeZone('Pacific/Fiji');
  344. $this->assertEquals('Fiji Standard Time', $formatter->format(0));
  345. }
  346. public function testFormatWithConstructorTimezone()
  347. {
  348. $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, 'UTC');
  349. $formatter->setPattern('yyyy-MM-dd HH:mm:ss');
  350. $this->assertEquals(
  351. $this->getDateTime(0, 'UTC')->format('Y-m-d H:i:s'),
  352. $formatter->format(0)
  353. );
  354. }
  355. /**
  356. * @requires PHP 5.5.10
  357. */
  358. public function testFormatWithDateTimeZoneGmt()
  359. {
  360. $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, new \DateTimeZone('GMT'), IntlDateFormatter::GREGORIAN, 'zzz');
  361. $this->assertEquals('GMT', $formatter->format(0));
  362. }
  363. public function testFormatWithDateTimeZoneGmtOffset()
  364. {
  365. if (defined('HHVM_VERSION_ID') || \PHP_VERSION_ID <= 50509) {
  366. $this->markTestSkipped('DateTimeZone GMT offsets are supported since 5.5.10. See https://github.com/facebook/hhvm/issues/5875 for HHVM.');
  367. }
  368. $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, new \DateTimeZone('GMT+03:00'), IntlDateFormatter::GREGORIAN, 'zzzz');
  369. $this->assertEquals('GMT+03:00', $formatter->format(0));
  370. }
  371. public function testFormatWithIntlTimeZone()
  372. {
  373. if (!extension_loaded('intl')) {
  374. $this->markTestSkipped('Extension intl is required.');
  375. }
  376. $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, \IntlTimeZone::createTimeZone('GMT+03:00'), IntlDateFormatter::GREGORIAN, 'zzzz');
  377. $this->assertEquals('GMT+03:00', $formatter->format(0));
  378. }
  379. public function testFormatWithTimezoneFromPhp()
  380. {
  381. $tz = date_default_timezone_get();
  382. date_default_timezone_set('Europe/London');
  383. $formatter = $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT);
  384. $formatter->setPattern('yyyy-MM-dd HH:mm:ss');
  385. $this->assertEquals(
  386. $this->getDateTime(0, 'Europe/London')->format('Y-m-d H:i:s'),
  387. $formatter->format(0)
  388. );
  389. $this->assertEquals('Europe/London', date_default_timezone_get());
  390. // Restores TZ.
  391. date_default_timezone_set($tz);
  392. }
  393. /**
  394. * @dataProvider dateAndTimeTypeProvider
  395. */
  396. public function testDateAndTimeType($timestamp, $datetype, $timetype, $expected)
  397. {
  398. $formatter = $this->getDateFormatter('en', $datetype, $timetype, 'UTC');
  399. $this->assertSame($expected, $formatter->format($timestamp));
  400. }
  401. public function dateAndTimeTypeProvider()
  402. {
  403. return array(
  404. array(0, IntlDateFormatter::FULL, IntlDateFormatter::NONE, 'Thursday, January 1, 1970'),
  405. array(0, IntlDateFormatter::LONG, IntlDateFormatter::NONE, 'January 1, 1970'),
  406. array(0, IntlDateFormatter::MEDIUM, IntlDateFormatter::NONE, 'Jan 1, 1970'),
  407. array(0, IntlDateFormatter::SHORT, IntlDateFormatter::NONE, '1/1/70'),
  408. array(0, IntlDateFormatter::NONE, IntlDateFormatter::FULL, '12:00:00 AM Coordinated Universal Time'),
  409. array(0, IntlDateFormatter::NONE, IntlDateFormatter::LONG, '12:00:00 AM UTC'),
  410. array(0, IntlDateFormatter::NONE, IntlDateFormatter::MEDIUM, '12:00:00 AM'),
  411. array(0, IntlDateFormatter::NONE, IntlDateFormatter::SHORT, '12:00 AM'),
  412. );
  413. }
  414. public function testGetCalendar()
  415. {
  416. $formatter = $this->getDefaultDateFormatter();
  417. $this->assertEquals(IntlDateFormatter::GREGORIAN, $formatter->getCalendar());
  418. }
  419. public function testGetDateType()
  420. {
  421. $formatter = $this->getDateFormatter('en', IntlDateFormatter::FULL, IntlDateFormatter::NONE);
  422. $this->assertEquals(IntlDateFormatter::FULL, $formatter->getDateType());
  423. }
  424. public function testGetLocale()
  425. {
  426. $formatter = $this->getDefaultDateFormatter();
  427. $this->assertEquals('en', $formatter->getLocale());
  428. }
  429. public function testGetPattern()
  430. {
  431. $formatter = $this->getDateFormatter('en', IntlDateFormatter::FULL, IntlDateFormatter::NONE, 'UTC', IntlDateFormatter::GREGORIAN, 'yyyy-MM-dd');
  432. $this->assertEquals('yyyy-MM-dd', $formatter->getPattern());
  433. }
  434. public function testGetTimeType()
  435. {
  436. $formatter = $this->getDateFormatter('en', IntlDateFormatter::NONE, IntlDateFormatter::FULL);
  437. $this->assertEquals(IntlDateFormatter::FULL, $formatter->getTimeType());
  438. }
  439. /**
  440. * @dataProvider parseProvider
  441. */
  442. public function testParse($pattern, $value, $expected)
  443. {
  444. $errorCode = IntlGlobals::U_ZERO_ERROR;
  445. $errorMessage = 'U_ZERO_ERROR';
  446. $formatter = $this->getDefaultDateFormatter($pattern);
  447. $this->assertSame($expected, $formatter->parse($value));
  448. $this->assertIsIntlSuccess($formatter, $errorMessage, $errorCode);
  449. }
  450. public function parseProvider()
  451. {
  452. return array_merge(
  453. $this->parseYearProvider(),
  454. $this->parseQuarterProvider(),
  455. $this->parseMonthProvider(),
  456. $this->parseStandaloneMonthProvider(),
  457. $this->parseDayProvider(),
  458. $this->parseDayOfWeekProvider(),
  459. $this->parseDayOfYearProvider(),
  460. $this->parseHour12ClockOneBasedProvider(),
  461. $this->parseHour12ClockZeroBasedProvider(),
  462. $this->parseHour24ClockOneBasedProvider(),
  463. $this->parseHour24ClockZeroBasedProvider(),
  464. $this->parseMinuteProvider(),
  465. $this->parseSecondProvider(),
  466. $this->parseTimezoneProvider(),
  467. $this->parseAmPmProvider(),
  468. $this->parseStandaloneAmPmProvider(),
  469. $this->parseRegexMetaCharsProvider(),
  470. $this->parseQuoteCharsProvider(),
  471. $this->parseDashSlashProvider()
  472. );
  473. }
  474. public function parseYearProvider()
  475. {
  476. return array(
  477. array('y-M-d', '1970-1-1', 0),
  478. array('yy-M-d', '70-1-1', 0),
  479. );
  480. }
  481. public function parseQuarterProvider()
  482. {
  483. return array(
  484. array('Q', '1', 0),
  485. array('QQ', '01', 0),
  486. array('QQQ', 'Q1', 0),
  487. array('QQQQ', '1st quarter', 0),
  488. array('QQQQQ', '1st quarter', 0),
  489. array('Q', '2', 7776000),
  490. array('QQ', '02', 7776000),
  491. array('QQQ', 'Q2', 7776000),
  492. array('QQQQ', '2nd quarter', 7776000),
  493. array('QQQQQ', '2nd quarter', 7776000),
  494. array('q', '1', 0),
  495. array('qq', '01', 0),
  496. array('qqq', 'Q1', 0),
  497. array('qqqq', '1st quarter', 0),
  498. array('qqqqq', '1st quarter', 0),
  499. );
  500. }
  501. public function parseMonthProvider()
  502. {
  503. return array(
  504. array('y-M-d', '1970-1-1', 0),
  505. array('y-MMM-d', '1970-Jan-1', 0),
  506. array('y-MMMM-d', '1970-January-1', 0),
  507. );
  508. }
  509. public function parseStandaloneMonthProvider()
  510. {
  511. return array(
  512. array('y-L-d', '1970-1-1', 0),
  513. array('y-LLL-d', '1970-Jan-1', 0),
  514. array('y-LLLL-d', '1970-January-1', 0),
  515. );
  516. }
  517. public function parseDayProvider()
  518. {
  519. return array(
  520. array('y-M-d', '1970-1-1', 0),
  521. array('y-M-dd', '1970-1-01', 0),
  522. array('y-M-ddd', '1970-1-001', 0),
  523. );
  524. }
  525. public function parseDayOfWeekProvider()
  526. {
  527. return array(
  528. array('E', 'Thu', 0),
  529. array('EE', 'Thu', 0),
  530. array('EEE', 'Thu', 0),
  531. array('EEEE', 'Thursday', 0),
  532. array('EEEEE', 'T', 432000),
  533. array('EEEEEE', 'Th', 0),
  534. );
  535. }
  536. public function parseDayOfYearProvider()
  537. {
  538. return array(
  539. array('D', '1', 0),
  540. array('D', '2', 86400),
  541. );
  542. }
  543. public function parseHour12ClockOneBasedProvider()
  544. {
  545. return array(
  546. // 12 hours (1-12)
  547. array('y-M-d h', '1970-1-1 1', 3600),
  548. array('y-M-d h', '1970-1-1 10', 36000),
  549. array('y-M-d hh', '1970-1-1 11', 39600),
  550. array('y-M-d hh', '1970-1-1 12', 0),
  551. array('y-M-d hh a', '1970-1-1 0 AM', 0),
  552. array('y-M-d hh a', '1970-1-1 1 AM', 3600),
  553. array('y-M-d hh a', '1970-1-1 10 AM', 36000),
  554. array('y-M-d hh a', '1970-1-1 11 AM', 39600),
  555. array('y-M-d hh a', '1970-1-1 12 AM', 0),
  556. array('y-M-d hh a', '1970-1-1 23 AM', 82800),
  557. array('y-M-d hh a', '1970-1-1 24 AM', 86400),
  558. array('y-M-d hh a', '1970-1-1 0 PM', 43200),
  559. array('y-M-d hh a', '1970-1-1 1 PM', 46800),
  560. array('y-M-d hh a', '1970-1-1 10 PM', 79200),
  561. array('y-M-d hh a', '1970-1-1 11 PM', 82800),
  562. array('y-M-d hh a', '1970-1-1 12 PM', 43200),
  563. array('y-M-d hh a', '1970-1-1 23 PM', 126000),
  564. array('y-M-d hh a', '1970-1-1 24 PM', 129600),
  565. );
  566. }
  567. public function parseHour12ClockZeroBasedProvider()
  568. {
  569. return array(
  570. // 12 hours (0-11)
  571. array('y-M-d K', '1970-1-1 1', 3600),
  572. array('y-M-d K', '1970-1-1 10', 36000),
  573. array('y-M-d KK', '1970-1-1 11', 39600),
  574. array('y-M-d KK', '1970-1-1 12', 43200),
  575. array('y-M-d KK a', '1970-1-1 0 AM', 0),
  576. array('y-M-d KK a', '1970-1-1 1 AM', 3600),
  577. array('y-M-d KK a', '1970-1-1 10 AM', 36000),
  578. array('y-M-d KK a', '1970-1-1 11 AM', 39600),
  579. array('y-M-d KK a', '1970-1-1 12 AM', 43200),
  580. array('y-M-d KK a', '1970-1-1 23 AM', 82800),
  581. array('y-M-d KK a', '1970-1-1 24 AM', 86400),
  582. array('y-M-d KK a', '1970-1-1 0 PM', 43200),
  583. array('y-M-d KK a', '1970-1-1 1 PM', 46800),
  584. array('y-M-d KK a', '1970-1-1 10 PM', 79200),
  585. array('y-M-d KK a', '1970-1-1 11 PM', 82800),
  586. array('y-M-d KK a', '1970-1-1 12 PM', 86400),
  587. array('y-M-d KK a', '1970-1-1 23 PM', 126000),
  588. array('y-M-d KK a', '1970-1-1 24 PM', 129600),
  589. );
  590. }
  591. public function parseHour24ClockOneBasedProvider()
  592. {
  593. return array(
  594. // 24 hours (1-24)
  595. array('y-M-d k', '1970-1-1 1', 3600),
  596. array('y-M-d k', '1970-1-1 10', 36000),
  597. array('y-M-d kk', '1970-1-1 11', 39600),
  598. array('y-M-d kk', '1970-1-1 12', 43200),
  599. array('y-M-d kk', '1970-1-1 23', 82800),
  600. array('y-M-d kk', '1970-1-1 24', 0),
  601. array('y-M-d kk a', '1970-1-1 0 AM', 0),
  602. array('y-M-d kk a', '1970-1-1 1 AM', 0),
  603. array('y-M-d kk a', '1970-1-1 10 AM', 0),
  604. array('y-M-d kk a', '1970-1-1 11 AM', 0),
  605. array('y-M-d kk a', '1970-1-1 12 AM', 0),
  606. array('y-M-d kk a', '1970-1-1 23 AM', 0),
  607. array('y-M-d kk a', '1970-1-1 24 AM', 0),
  608. array('y-M-d kk a', '1970-1-1 0 PM', 43200),
  609. array('y-M-d kk a', '1970-1-1 1 PM', 43200),
  610. array('y-M-d kk a', '1970-1-1 10 PM', 43200),
  611. array('y-M-d kk a', '1970-1-1 11 PM', 43200),
  612. array('y-M-d kk a', '1970-1-1 12 PM', 43200),
  613. array('y-M-d kk a', '1970-1-1 23 PM', 43200),
  614. array('y-M-d kk a', '1970-1-1 24 PM', 43200),
  615. );
  616. }
  617. public function parseHour24ClockZeroBasedProvider()
  618. {
  619. return array(
  620. // 24 hours (0-23)
  621. array('y-M-d H', '1970-1-1 0', 0),
  622. array('y-M-d H', '1970-1-1 1', 3600),
  623. array('y-M-d H', '1970-1-1 10', 36000),
  624. array('y-M-d HH', '1970-1-1 11', 39600),
  625. array('y-M-d HH', '1970-1-1 12', 43200),
  626. array('y-M-d HH', '1970-1-1 23', 82800),
  627. array('y-M-d HH a', '1970-1-1 0 AM', 0),
  628. array('y-M-d HH a', '1970-1-1 1 AM', 0),
  629. array('y-M-d HH a', '1970-1-1 10 AM', 0),
  630. array('y-M-d HH a', '1970-1-1 11 AM', 0),
  631. array('y-M-d HH a', '1970-1-1 12 AM', 0),
  632. array('y-M-d HH a', '1970-1-1 23 AM', 0),
  633. array('y-M-d HH a', '1970-1-1 24 AM', 0),
  634. array('y-M-d HH a', '1970-1-1 0 PM', 43200),
  635. array('y-M-d HH a', '1970-1-1 1 PM', 43200),
  636. array('y-M-d HH a', '1970-1-1 10 PM', 43200),
  637. array('y-M-d HH a', '1970-1-1 11 PM', 43200),
  638. array('y-M-d HH a', '1970-1-1 12 PM', 43200),
  639. array('y-M-d HH a', '1970-1-1 23 PM', 43200),
  640. array('y-M-d HH a', '1970-1-1 24 PM', 43200),
  641. );
  642. }
  643. public function parseMinuteProvider()
  644. {
  645. return array(
  646. array('y-M-d HH:m', '1970-1-1 0:1', 60),
  647. array('y-M-d HH:mm', '1970-1-1 0:10', 600),
  648. );
  649. }
  650. public function parseSecondProvider()
  651. {
  652. return array(
  653. array('y-M-d HH:mm:s', '1970-1-1 00:01:1', 61),
  654. array('y-M-d HH:mm:ss', '1970-1-1 00:01:10', 70),
  655. );
  656. }
  657. public function parseTimezoneProvider()
  658. {
  659. return array(
  660. array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-03:00', 10800),
  661. array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-04:00', 14400),
  662. array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-00:00', 0),
  663. array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT+03:00', -10800),
  664. array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT+04:00', -14400),
  665. array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT-0300', 10800),
  666. array('y-M-d HH:mm:ss zzzz', '1970-1-1 00:00:00 GMT+0300', -10800),
  667. // a previous timezone parsing should not change the timezone for the next parsing
  668. array('y-M-d HH:mm:ss', '1970-1-1 00:00:00', 0),
  669. );
  670. }
  671. public function parseAmPmProvider()
  672. {
  673. return array(
  674. // AM/PM (already covered by hours tests)
  675. array('y-M-d HH:mm:ss a', '1970-1-1 00:00:00 AM', 0),
  676. array('y-M-d HH:mm:ss a', '1970-1-1 00:00:00 PM', 43200),
  677. );
  678. }
  679. public function parseStandaloneAmPmProvider()
  680. {
  681. return array(
  682. array('a', 'AM', 0),
  683. array('a', 'PM', 43200),
  684. );
  685. }
  686. public function parseRegexMetaCharsProvider()
  687. {
  688. return array(
  689. // regexp meta chars in the pattern string
  690. array('y[M-d', '1970[1-1', 0),
  691. array('y[M/d', '1970[1/1', 0),
  692. );
  693. }
  694. public function parseQuoteCharsProvider()
  695. {
  696. return array(
  697. array("'M'", 'M', 0),
  698. array("'yy'", 'yy', 0),
  699. array("'''yy'", "'yy", 0),
  700. array("''y", "'1970", 0),
  701. array("H 'o'' clock'", "0 o' clock", 0),
  702. );
  703. }
  704. public function parseDashSlashProvider()
  705. {
  706. return array(
  707. array('y-M-d', '1970/1/1', 0),
  708. array('yy-M-d', '70/1/1', 0),
  709. array('y/M/d', '1970-1-1', 0),
  710. array('yy/M/d', '70-1-1', 0),
  711. );
  712. }
  713. /**
  714. * @dataProvider parseErrorProvider
  715. */
  716. public function testParseError($pattern, $value)
  717. {
  718. $errorCode = IntlGlobals::U_PARSE_ERROR;
  719. $errorMessage = 'Date parsing failed: U_PARSE_ERROR';
  720. $formatter = $this->getDefaultDateFormatter($pattern);
  721. $this->assertFalse($formatter->parse($value));
  722. $this->assertIsIntlFailure($formatter, $errorMessage, $errorCode);
  723. }
  724. public function parseErrorProvider()
  725. {
  726. return array(
  727. // 1 char month
  728. array('y-MMMMM-d', '1970-J-1'),
  729. array('y-MMMMM-d', '1970-S-1'),
  730. // standalone 1 char month
  731. array('y-LLLLL-d', '1970-J-1'),
  732. array('y-LLLLL-d', '1970-S-1'),
  733. );
  734. }
  735. /*
  736. * https://github.com/symfony/symfony/issues/4242
  737. */
  738. public function testParseAfterError()
  739. {
  740. $this->testParseError('y-MMMMM-d', '1970-J-1');
  741. $this->testParse('y-M-d', '1970-1-1', 0);
  742. }
  743. public function testParseWithNullPositionValue()
  744. {
  745. $position = null;
  746. $formatter = $this->getDefaultDateFormatter('y');
  747. $this->assertSame(0, $formatter->parse('1970', $position));
  748. $this->assertNull($position);
  749. }
  750. public function testSetPattern()
  751. {
  752. $formatter = $this->getDefaultDateFormatter();
  753. $formatter->setPattern('yyyy-MM-dd');
  754. $this->assertEquals('yyyy-MM-dd', $formatter->getPattern());
  755. }
  756. /**
  757. * @dataProvider setTimeZoneIdProvider
  758. */
  759. public function testSetTimeZoneId($timeZoneId, $expectedTimeZoneId)
  760. {
  761. $formatter = $this->getDefaultDateFormatter();
  762. $formatter->setTimeZone($timeZoneId);
  763. $this->assertEquals($expectedTimeZoneId, $formatter->getTimeZoneId());
  764. }
  765. public function setTimeZoneIdProvider()
  766. {
  767. return array(
  768. array('UTC', 'UTC'),
  769. array('GMT', 'GMT'),
  770. array('GMT-03:00', 'GMT-03:00'),
  771. array('Europe/Zurich', 'Europe/Zurich'),
  772. array(null, date_default_timezone_get()),
  773. array('Foo/Bar', 'UTC'),
  774. array('GMT+00:AA', 'UTC'),
  775. array('GMT+00AA', 'UTC'),
  776. );
  777. }
  778. protected function getDefaultDateFormatter($pattern = null)
  779. {
  780. return $this->getDateFormatter('en', IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT, 'UTC', IntlDateFormatter::GREGORIAN, $pattern);
  781. }
  782. protected function getDateTime($timestamp, $timeZone)
  783. {
  784. $dateTime = new \DateTime();
  785. $dateTime->setTimestamp(null === $timestamp ? time() : $timestamp);
  786. $dateTime->setTimezone(new \DateTimeZone($timeZone ?: getenv('TZ') ?: 'UTC'));
  787. return $dateTime;
  788. }
  789. protected function assertIsIntlFailure($formatter, $errorMessage, $errorCode)
  790. {
  791. $this->assertSame($errorMessage, $this->getIntlErrorMessage());
  792. $this->assertSame($errorCode, $this->getIntlErrorCode());
  793. $this->assertTrue($this->isIntlFailure($this->getIntlErrorCode()));
  794. $this->assertSame($errorMessage, $formatter->getErrorMessage());
  795. $this->assertSame($errorCode, $formatter->getErrorCode());
  796. $this->assertTrue($this->isIntlFailure($formatter->getErrorCode()));
  797. }
  798. protected function assertIsIntlSuccess($formatter, $errorMessage, $errorCode)
  799. {
  800. /* @var IntlDateFormatter $formatter */
  801. $this->assertSame($errorMessage, $this->getIntlErrorMessage());
  802. $this->assertSame($errorCode, $this->getIntlErrorCode());
  803. $this->assertFalse($this->isIntlFailure($this->getIntlErrorCode()));
  804. $this->assertSame($errorMessage, $formatter->getErrorMessage());
  805. $this->assertSame($errorCode, $formatter->getErrorCode());
  806. $this->assertFalse($this->isIntlFailure($formatter->getErrorCode()));
  807. }
  808. /**
  809. * @param $locale
  810. * @param $datetype
  811. * @param $timetype
  812. * @param null $timezone
  813. * @param int $calendar
  814. * @param null $pattern
  815. *
  816. * @return mixed
  817. */
  818. abstract protected function getDateFormatter($locale, $datetype, $timetype, $timezone = null, $calendar = IntlDateFormatter::GREGORIAN, $pattern = null);
  819. /**
  820. * @return string
  821. */
  822. abstract protected function getIntlErrorMessage();
  823. /**
  824. * @return int
  825. */
  826. abstract protected function getIntlErrorCode();
  827. /**
  828. * @param int $errorCode
  829. *
  830. * @return bool
  831. */
  832. abstract protected function isIntlFailure($errorCode);
  833. }