TimeTest.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  11. * @link https://cakephp.org CakePHP(tm) Project
  12. * @since 1.2.0
  13. * @license https://opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\Test\TestCase\I18n;
  16. use Cake\I18n\FrozenTime;
  17. use Cake\I18n\I18n;
  18. use Cake\I18n\Time;
  19. use Cake\TestSuite\TestCase;
  20. /**
  21. * TimeTest class
  22. */
  23. class TimeTest extends TestCase
  24. {
  25. /**
  26. * setUp method
  27. *
  28. * @return void
  29. */
  30. public function setUp()
  31. {
  32. parent::setUp();
  33. $this->now = Time::getTestNow();
  34. $this->frozenNow = FrozenTime::getTestNow();
  35. $this->locale = Time::getDefaultLocale();
  36. Time::setDefaultLocale('en_US');
  37. FrozenTime::setDefaultLocale('en_US');
  38. }
  39. /**
  40. * tearDown method
  41. *
  42. * @return void
  43. */
  44. public function tearDown()
  45. {
  46. parent::tearDown();
  47. Time::setTestNow($this->now);
  48. Time::setDefaultLocale($this->locale);
  49. Time::resetToStringFormat();
  50. Time::setJsonEncodeFormat("yyyy-MM-dd'T'HH:mm:ssxxx");
  51. FrozenTime::setTestNow($this->frozenNow);
  52. FrozenTime::setDefaultLocale($this->locale);
  53. FrozenTime::resetToStringFormat();
  54. FrozenTime::setJsonEncodeFormat("yyyy-MM-dd'T'HH:mm:ssxxx");
  55. date_default_timezone_set('UTC');
  56. I18n::setLocale(I18n::DEFAULT_LOCALE);
  57. }
  58. /**
  59. * Restored the original system timezone
  60. *
  61. * @return void
  62. */
  63. protected function _restoreSystemTimezone()
  64. {
  65. date_default_timezone_set($this->_systemTimezoneIdentifier);
  66. }
  67. /**
  68. * Provider for ensuring that Time and FrozenTime work the same way.
  69. *
  70. * @return void
  71. */
  72. public static function classNameProvider()
  73. {
  74. return ['mutable' => ['Cake\I18n\Time'], 'immutable' => ['Cake\I18n\FrozenTime']];
  75. }
  76. /**
  77. * Ensure that instances can be built from other objects.
  78. *
  79. * @dataProvider classNameProvider
  80. * @return void
  81. */
  82. public function testConstructFromAnotherInstance($class)
  83. {
  84. $time = '2015-01-22 10:33:44';
  85. $frozen = new FrozenTime($time, 'America/Chicago');
  86. $subject = new $class($frozen);
  87. $this->assertEquals($time, $subject->format('Y-m-d H:i:s'), 'frozen time construction');
  88. $mut = new Time($time, 'America/Chicago');
  89. $subject = new $class($mut);
  90. $this->assertEquals($time, $subject->format('Y-m-d H:i:s'), 'mutable time construction');
  91. }
  92. /**
  93. * provider for timeAgoInWords() tests
  94. *
  95. * @return array
  96. */
  97. public static function timeAgoProvider()
  98. {
  99. return [
  100. ['-12 seconds', '12 seconds ago'],
  101. ['-12 minutes', '12 minutes ago'],
  102. ['-2 hours', '2 hours ago'],
  103. ['-1 day', '1 day ago'],
  104. ['-2 days', '2 days ago'],
  105. ['-2 days -3 hours', '2 days, 3 hours ago'],
  106. ['-1 week', '1 week ago'],
  107. ['-2 weeks -2 days', '2 weeks, 2 days ago'],
  108. ['+1 week', '1 week'],
  109. ['+1 week 1 day', '1 week, 1 day'],
  110. ['+2 weeks 2 day', '2 weeks, 2 days'],
  111. ['2007-9-24', 'on 9/24/07'],
  112. ['now', 'just now'],
  113. ];
  114. }
  115. /**
  116. * testTimeAgoInWords method
  117. *
  118. * @dataProvider timeAgoProvider
  119. * @return void
  120. */
  121. public function testTimeAgoInWords($input, $expected)
  122. {
  123. $time = new Time($input);
  124. $result = $time->timeAgoInWords();
  125. $this->assertEquals($expected, $result);
  126. }
  127. /**
  128. * testTimeAgoInWords method
  129. *
  130. * @dataProvider timeAgoProvider
  131. * @return void
  132. */
  133. public function testTimeAgoInWordsFrozenTime($input, $expected)
  134. {
  135. $time = new FrozenTime($input);
  136. $result = $time->timeAgoInWords();
  137. $this->assertEquals($expected, $result);
  138. }
  139. /**
  140. * provider for timeAgo with an end date.
  141. *
  142. * @return void
  143. */
  144. public function timeAgoEndProvider()
  145. {
  146. return [
  147. [
  148. '+4 months +2 weeks +3 days',
  149. '4 months, 2 weeks, 3 days',
  150. '8 years'
  151. ],
  152. [
  153. '+4 months +2 weeks +1 day',
  154. '4 months, 2 weeks, 1 day',
  155. '8 years'
  156. ],
  157. [
  158. '+3 months +2 weeks',
  159. '3 months, 2 weeks',
  160. '8 years'
  161. ],
  162. [
  163. '+3 months +2 weeks +1 day',
  164. '3 months, 2 weeks, 1 day',
  165. '8 years'
  166. ],
  167. [
  168. '+1 months +1 week +1 day',
  169. '1 month, 1 week, 1 day',
  170. '8 years'
  171. ],
  172. [
  173. '+2 months +2 days',
  174. '2 months, 2 days',
  175. '+2 months +2 days'
  176. ],
  177. [
  178. '+2 months +12 days',
  179. '2 months, 1 week, 5 days',
  180. '3 months'
  181. ],
  182. ];
  183. }
  184. /**
  185. * test the timezone option for timeAgoInWords
  186. *
  187. * @dataProvider classNameProvider
  188. * @return void
  189. */
  190. public function testTimeAgoInWordsTimezone($class)
  191. {
  192. $time = new FrozenTime('1990-07-31 20:33:00 UTC');
  193. $result = $time->timeAgoInWords(
  194. [
  195. 'timezone' => 'America/Vancouver',
  196. 'end' => '+1month',
  197. 'format' => 'dd-MM-YYYY HH:mm:ss'
  198. ]
  199. );
  200. $this->assertEquals('on 31-07-1990 13:33:00', $result);
  201. }
  202. /**
  203. * test the end option for timeAgoInWords
  204. *
  205. * @dataProvider timeAgoEndProvider
  206. * @return void
  207. */
  208. public function testTimeAgoInWordsEnd($input, $expected, $end)
  209. {
  210. $time = new Time($input);
  211. $result = $time->timeAgoInWords(['end' => $end]);
  212. $this->assertEquals($expected, $result);
  213. }
  214. /**
  215. * test the custom string options for timeAgoInWords
  216. *
  217. * @dataProvider classNameProvider
  218. * @return void
  219. */
  220. public function testTimeAgoInWordsCustomStrings($class)
  221. {
  222. $time = new $class('-8 years -4 months -2 weeks -3 days');
  223. $result = $time->timeAgoInWords([
  224. 'relativeString' => 'at least %s ago',
  225. 'accuracy' => ['year' => 'year'],
  226. 'end' => '+10 years'
  227. ]);
  228. $expected = 'at least 8 years ago';
  229. $this->assertEquals($expected, $result);
  230. $time = new $class('+4 months +2 weeks +3 days');
  231. $result = $time->timeAgoInWords([
  232. 'absoluteString' => 'exactly on %s',
  233. 'accuracy' => ['year' => 'year'],
  234. 'end' => '+2 months'
  235. ]);
  236. $expected = 'exactly on ' . date('n/j/y', strtotime('+4 months +2 weeks +3 days'));
  237. $this->assertEquals($expected, $result);
  238. }
  239. /**
  240. * Test the accuracy option for timeAgoInWords()
  241. *
  242. * @dataProvider classNameProvider
  243. * @return void
  244. */
  245. public function testTimeAgoInWordsAccuracy($class)
  246. {
  247. $time = new $class('+8 years +4 months +2 weeks +3 days');
  248. $result = $time->timeAgoInWords([
  249. 'accuracy' => ['year' => 'year'],
  250. 'end' => '+10 years'
  251. ]);
  252. $expected = '8 years';
  253. $this->assertEquals($expected, $result);
  254. $time = new $class('+8 years +4 months +2 weeks +3 days');
  255. $result = $time->timeAgoInWords([
  256. 'accuracy' => ['year' => 'month'],
  257. 'end' => '+10 years'
  258. ]);
  259. $expected = '8 years, 4 months';
  260. $this->assertEquals($expected, $result);
  261. $time = new $class('+8 years +4 months +2 weeks +3 days');
  262. $result = $time->timeAgoInWords([
  263. 'accuracy' => ['year' => 'week'],
  264. 'end' => '+10 years'
  265. ]);
  266. $expected = '8 years, 4 months, 2 weeks';
  267. $this->assertEquals($expected, $result);
  268. $time = new $class('+8 years +4 months +2 weeks +3 days');
  269. $result = $time->timeAgoInWords([
  270. 'accuracy' => ['year' => 'day'],
  271. 'end' => '+10 years'
  272. ]);
  273. $expected = '8 years, 4 months, 2 weeks, 3 days';
  274. $this->assertEquals($expected, $result);
  275. $time = new $class('+1 years +5 weeks');
  276. $result = $time->timeAgoInWords([
  277. 'accuracy' => ['year' => 'year'],
  278. 'end' => '+10 years'
  279. ]);
  280. $expected = '1 year';
  281. $this->assertEquals($expected, $result);
  282. $time = new $class('+58 minutes');
  283. $result = $time->timeAgoInWords([
  284. 'accuracy' => 'hour'
  285. ]);
  286. $expected = 'in about an hour';
  287. $this->assertEquals($expected, $result);
  288. $time = new $class('+23 hours');
  289. $result = $time->timeAgoInWords([
  290. 'accuracy' => 'day'
  291. ]);
  292. $expected = 'in about a day';
  293. $this->assertEquals($expected, $result);
  294. $time = new $class('+20 days');
  295. $result = $time->timeAgoInWords(['accuracy' => 'month']);
  296. $this->assertEquals('in about a month', $result);
  297. }
  298. /**
  299. * Test the format option of timeAgoInWords()
  300. *
  301. * @dataProvider classNameProvider
  302. * @return void
  303. */
  304. public function testTimeAgoInWordsWithFormat($class)
  305. {
  306. $time = new $class('2007-9-25');
  307. $result = $time->timeAgoInWords(['format' => 'yyyy-MM-dd']);
  308. $this->assertEquals('on 2007-09-25', $result);
  309. $time = new $class('+2 weeks +2 days');
  310. $result = $time->timeAgoInWords(['format' => 'yyyy-MM-dd']);
  311. $this->assertRegExp('/^2 weeks, [1|2] day(s)?$/', $result);
  312. $time = new $class('+2 months +2 days');
  313. $result = $time->timeAgoInWords(['end' => '1 month', 'format' => 'yyyy-MM-dd']);
  314. $this->assertEquals('on ' . date('Y-m-d', strtotime('+2 months +2 days')), $result);
  315. }
  316. /**
  317. * test timeAgoInWords() with negative values.
  318. *
  319. * @dataProvider classNameProvider
  320. * @return void
  321. */
  322. public function testTimeAgoInWordsNegativeValues($class)
  323. {
  324. $time = new $class('-2 months -2 days');
  325. $result = $time->timeAgoInWords(['end' => '3 month']);
  326. $this->assertEquals('2 months, 2 days ago', $result);
  327. $time = new $class('-2 months -2 days');
  328. $result = $time->timeAgoInWords(['end' => '3 month']);
  329. $this->assertEquals('2 months, 2 days ago', $result);
  330. $time = new $class('-2 months -2 days');
  331. $result = $time->timeAgoInWords(['end' => '1 month', 'format' => 'yyyy-MM-dd']);
  332. $this->assertEquals('on ' . date('Y-m-d', strtotime('-2 months -2 days')), $result);
  333. $time = new $class('-2 years -5 months -2 days');
  334. $result = $time->timeAgoInWords(['end' => '3 years']);
  335. $this->assertEquals('2 years, 5 months, 2 days ago', $result);
  336. $time = new $class('-2 weeks -2 days');
  337. $result = $time->timeAgoInWords(['format' => 'yyyy-MM-dd']);
  338. $this->assertEquals('2 weeks, 2 days ago', $result);
  339. $time = new $class('-3 years -12 months');
  340. $result = $time->timeAgoInWords();
  341. $expected = 'on ' . $time->format('n/j/y');
  342. $this->assertEquals($expected, $result);
  343. $time = new $class('-1 month -1 week -6 days');
  344. $result = $time->timeAgoInWords(
  345. ['end' => '1 year', 'accuracy' => ['month' => 'month']]
  346. );
  347. $this->assertEquals('1 month ago', $result);
  348. $time = new $class('-1 years -2 weeks -3 days');
  349. $result = $time->timeAgoInWords(
  350. ['accuracy' => ['year' => 'year']]
  351. );
  352. $expected = 'on ' . $time->format('n/j/y');
  353. $this->assertEquals($expected, $result);
  354. $time = new $class('-13 months -5 days');
  355. $result = $time->timeAgoInWords(['end' => '2 years']);
  356. $this->assertEquals('1 year, 1 month, 5 days ago', $result);
  357. $time = new $class('-58 minutes');
  358. $result = $time->timeAgoInWords(['accuracy' => 'hour']);
  359. $this->assertEquals('about an hour ago', $result);
  360. $time = new $class('-23 hours');
  361. $result = $time->timeAgoInWords(['accuracy' => 'day']);
  362. $this->assertEquals('about a day ago', $result);
  363. $time = new $class('-20 days');
  364. $result = $time->timeAgoInWords(['accuracy' => 'month']);
  365. $this->assertEquals('about a month ago', $result);
  366. }
  367. /**
  368. * testNice method
  369. *
  370. * @dataProvider classNameProvider
  371. * @return void
  372. */
  373. public function testNice($class)
  374. {
  375. $time = new $class('2014-04-20 20:00', 'UTC');
  376. $this->assertTimeFormat('Apr 20, 2014, 8:00 PM', $time->nice());
  377. $result = $time->nice('America/New_York');
  378. $this->assertTimeFormat('Apr 20, 2014, 4:00 PM', $result);
  379. $this->assertEquals('UTC', $time->getTimezone()->getName());
  380. $this->assertTimeFormat('20 avr. 2014 20:00', $time->nice(null, 'fr-FR'));
  381. $this->assertTimeFormat('20 avr. 2014 16:00', $time->nice('America/New_York', 'fr-FR'));
  382. }
  383. /**
  384. * test formatting dates taking in account preferred i18n locale file
  385. *
  386. * @dataProvider classNameProvider
  387. * @return void
  388. */
  389. public function testI18nFormat($class)
  390. {
  391. $time = new $class('Thu Jan 14 13:59:28 2010');
  392. $result = $time->i18nFormat();
  393. $expected = '1/14/10, 1:59 PM';
  394. $this->assertTimeFormat($expected, $result);
  395. $result = $time->i18nFormat(\IntlDateFormatter::FULL, null, 'es-ES');
  396. $expected = 'jueves, 14 de enero de 2010, 13:59:28 (GMT)';
  397. $this->assertTimeFormat($expected, $result);
  398. $format = [\IntlDateFormatter::NONE, \IntlDateFormatter::SHORT];
  399. $result = $time->i18nFormat($format);
  400. $expected = '1:59 PM';
  401. $this->assertTimeFormat($expected, $result);
  402. $result = $time->i18nFormat('HH:mm:ss', 'Australia/Sydney');
  403. $expected = '00:59:28';
  404. $this->assertTimeFormat($expected, $result);
  405. $class::setDefaultLocale('fr-FR');
  406. $result = $time->i18nFormat(\IntlDateFormatter::FULL);
  407. $expected = 'jeudi 14 janvier 2010 13:59:28 UTC';
  408. $this->assertTimeFormat($expected, $result);
  409. $result = $time->i18nFormat(\IntlDateFormatter::FULL, null, 'es-ES');
  410. $expected = 'jueves, 14 de enero de 2010, 13:59:28 (GMT)';
  411. $this->assertTimeFormat($expected, $result, 'Default locale should not be used');
  412. $result = $time->i18nFormat(\IntlDateFormatter::FULL, null, 'fa-SA');
  413. $expected = 'پنجشنبه ۱۴ ژانویهٔ ۲۰۱۰، ساعت ۱۳:۵۹:۲۸ (GMT)';
  414. $this->assertTimeFormat($expected, $result, 'fa-SA locale should be used');
  415. $result = $time->i18nFormat(\IntlDateFormatter::FULL, null, 'en-IR@calendar=persian');
  416. $expected = 'Thursday, Dey 24, 1388 at 1:59:28 PM GMT';
  417. $this->assertTimeFormat($expected, $result);
  418. $result = $time->i18nFormat(\IntlDateFormatter::FULL, null, 'ps-IR@calendar=persian');
  419. $expected = 'پينځنۍ د ۱۳۸۸ د مرغومی ۲۴ ۱۳:۵۹:۲۸ (GMT)';
  420. $this->assertTimeFormat($expected, $result);
  421. $result = $time->i18nFormat(\IntlDateFormatter::FULL, null, 'en-KW@calendar=islamic');
  422. $expected = 'Thursday, Muharram 29, 1431 at 1:59:28 PM GMT';
  423. $this->assertTimeFormat($expected, $result);
  424. $result = $time->i18nFormat(\IntlDateFormatter::FULL, 'Asia/Tokyo', 'ja-JP@calendar=japanese');
  425. $expected = '平成22年1月14日木曜日 22時59分28秒 日本標準時';
  426. $this->assertTimeFormat($expected, $result);
  427. $result = $time->i18nFormat(\IntlDateFormatter::FULL, 'Asia/Tokyo', 'ja-JP@calendar=japanese');
  428. $expected = '平成22年1月14日木曜日 22時59分28秒 日本標準時';
  429. $this->assertTimeFormat($expected, $result);
  430. }
  431. /**
  432. * test formatting dates with offset style timezone
  433. *
  434. * @dataProvider classNameProvider
  435. * @see https://github.com/facebook/hhvm/issues/3637
  436. * @return void
  437. */
  438. public function testI18nFormatWithOffsetTimezone($class)
  439. {
  440. $time = new $class('2014-01-01T00:00:00+00');
  441. $result = $time->i18nFormat(\IntlDateFormatter::FULL);
  442. $expected = 'Wednesday January 1 2014 12:00:00 AM GMT';
  443. $this->assertTimeFormat($expected, $result);
  444. $time = new $class('2014-01-01T00:00:00+09');
  445. $result = $time->i18nFormat(\IntlDateFormatter::FULL);
  446. $expected = 'Wednesday January 1 2014 12:00:00 AM GMT+09:00';
  447. $this->assertTimeFormat($expected, $result);
  448. $time = new $class('2014-01-01T00:00:00-01:30');
  449. $result = $time->i18nFormat(\IntlDateFormatter::FULL);
  450. $expected = 'Wednesday January 1 2014 12:00:00 AM GMT-01:30';
  451. $this->assertTimeFormat($expected, $result);
  452. $time = new $class('2014-01-01T00:00Z');
  453. $result = $time->i18nFormat(\IntlDateFormatter::FULL);
  454. $expected = 'Wednesday January 1 2014 12:00:00 AM GMT';
  455. $this->assertTimeFormat($expected, $result);
  456. }
  457. /**
  458. * testListTimezones
  459. *
  460. * @dataProvider classNameProvider
  461. * @return void
  462. */
  463. public function testListTimezones($class)
  464. {
  465. $return = $class::listTimezones();
  466. $this->assertTrue(isset($return['Asia']['Asia/Bangkok']));
  467. $this->assertEquals('Bangkok', $return['Asia']['Asia/Bangkok']);
  468. $this->assertTrue(isset($return['America']['America/Argentina/Buenos_Aires']));
  469. $this->assertEquals('Argentina/Buenos_Aires', $return['America']['America/Argentina/Buenos_Aires']);
  470. $this->assertTrue(isset($return['UTC']['UTC']));
  471. $this->assertArrayNotHasKey('Cuba', $return);
  472. $this->assertArrayNotHasKey('US', $return);
  473. $return = $class::listTimezones('#^Asia/#');
  474. $this->assertTrue(isset($return['Asia']['Asia/Bangkok']));
  475. $this->assertArrayNotHasKey('Pacific', $return);
  476. $return = $class::listTimezones(null, null, ['abbr' => true]);
  477. $this->assertTrue(isset($return['Asia']['Asia/Jakarta']));
  478. $this->assertEquals('Jakarta - WIB', $return['Asia']['Asia/Jakarta']);
  479. $this->assertEquals('Regina - CST', $return['America']['America/Regina']);
  480. $return = $class::listTimezones(null, null, [
  481. 'abbr' => true,
  482. 'before' => ' (',
  483. 'after' => ')',
  484. ]);
  485. $this->assertEquals('Jayapura (WIT)', $return['Asia']['Asia/Jayapura']);
  486. $this->assertEquals('Regina (CST)', $return['America']['America/Regina']);
  487. $return = $class::listTimezones('#^(America|Pacific)/#', null, false);
  488. $this->assertArrayHasKey('America/Argentina/Buenos_Aires', $return);
  489. $this->assertArrayHasKey('Pacific/Tahiti', $return);
  490. $return = $class::listTimezones(\DateTimeZone::ASIA);
  491. $this->assertTrue(isset($return['Asia']['Asia/Bangkok']));
  492. $this->assertArrayNotHasKey('Pacific', $return);
  493. $return = $class::listTimezones(\DateTimeZone::PER_COUNTRY, 'US', false);
  494. $this->assertArrayHasKey('Pacific/Honolulu', $return);
  495. $this->assertArrayNotHasKey('Asia/Bangkok', $return);
  496. }
  497. /**
  498. * Tests that __toString uses the i18n formatter
  499. *
  500. * @dataProvider classNameProvider
  501. * @return void
  502. */
  503. public function testToString($class)
  504. {
  505. $time = new $class('2014-04-20 22:10');
  506. $class::setDefaultLocale('fr-FR');
  507. $class::setToStringFormat(\IntlDateFormatter::FULL);
  508. $this->assertTimeFormat('dimanche 20 avril 2014 22:10:00 UTC', (string)$time);
  509. }
  510. /**
  511. * Data provider for invalid values.
  512. *
  513. * @return array
  514. */
  515. public function invalidDataProvider()
  516. {
  517. return [
  518. [null],
  519. [false],
  520. [''],
  521. ];
  522. }
  523. /**
  524. * Test that invalid datetime values do not trigger errors.
  525. *
  526. * @dataProvider invalidDataProvider
  527. * @return void
  528. */
  529. public function testToStringInvalid($value)
  530. {
  531. $time = new Time($value);
  532. $this->assertInternalType('string', (string)$time);
  533. $this->assertNotEmpty((string)$time);
  534. }
  535. /**
  536. * Test that invalid datetime values do not trigger errors.
  537. *
  538. * @dataProvider invalidDataProvider
  539. * @return void
  540. */
  541. public function testToStringInvalidFrozen($value)
  542. {
  543. $time = new FrozenTime($value);
  544. $this->assertInternalType('string', (string)$time);
  545. $this->assertNotEmpty((string)$time);
  546. }
  547. /**
  548. * These invalid values are not invalid on windows :(
  549. *
  550. * @dataProvider classNameProvider
  551. * @return void
  552. */
  553. public function testToStringInvalidZeros($class)
  554. {
  555. $this->skipIf(DS === '\\', 'All zeros are valid on windows.');
  556. $this->skipIf(PHP_INT_SIZE === 4, 'IntlDateFormatter throws exceptions on 32-bit systems');
  557. $time = new $class('0000-00-00');
  558. $this->assertInternalType('string', (string)$time);
  559. $this->assertNotEmpty((string)$time);
  560. $time = new $class('0000-00-00 00:00:00');
  561. $this->assertInternalType('string', (string)$time);
  562. $this->assertNotEmpty((string)$time);
  563. }
  564. /**
  565. * Tests diffForHumans
  566. *
  567. * @dataProvider classNameProvider
  568. * @return void
  569. */
  570. public function testDiffForHumans($class)
  571. {
  572. $time = new $class('2014-04-20 10:10:10');
  573. $other = new $class('2014-04-27 10:10:10');
  574. $this->assertEquals('1 week before', $time->diffForHumans($other));
  575. $other = new $class('2014-04-21 09:10:10');
  576. $this->assertEquals('23 hours before', $time->diffForHumans($other));
  577. $other = new $class('2014-04-13 09:10:10');
  578. $this->assertEquals('1 week after', $time->diffForHumans($other));
  579. $other = new $class('2014-04-06 09:10:10');
  580. $this->assertEquals('2 weeks after', $time->diffForHumans($other));
  581. $other = new $class('2014-04-21 10:10:10');
  582. $this->assertEquals('1 day before', $time->diffForHumans($other));
  583. $other = new $class('2014-04-22 10:10:10');
  584. $this->assertEquals('2 days before', $time->diffForHumans($other));
  585. $other = new $class('2014-04-20 10:11:10');
  586. $this->assertEquals('1 minute before', $time->diffForHumans($other));
  587. $other = new $class('2014-04-20 10:12:10');
  588. $this->assertEquals('2 minutes before', $time->diffForHumans($other));
  589. $other = new $class('2014-04-20 10:10:09');
  590. $this->assertEquals('1 second after', $time->diffForHumans($other));
  591. $other = new $class('2014-04-20 10:10:08');
  592. $this->assertEquals('2 seconds after', $time->diffForHumans($other));
  593. }
  594. /**
  595. * Tests diffForHumans absolute
  596. *
  597. * @dataProvider classNameProvider
  598. * @return void
  599. */
  600. public function testDiffForHumansAbsolute($class)
  601. {
  602. $class::setTestNow(new $class('2015-12-12 10:10:10'));
  603. $time = new $class('2014-04-20 10:10:10');
  604. $this->assertEquals('1 year', $time->diffForHumans(null, ['absolute' => true]));
  605. $other = new $class('2014-04-27 10:10:10');
  606. $this->assertEquals('1 week', $time->diffForHumans($other, ['absolute' => true]));
  607. $time = new $class('2016-04-20 10:10:10');
  608. $this->assertEquals('4 months', $time->diffForHumans(null, ['absolute' => true]));
  609. }
  610. /**
  611. * Tests diffForHumans with now
  612. *
  613. * @dataProvider classNameProvider
  614. * @return void
  615. */
  616. public function testDiffForHumansNow($class)
  617. {
  618. $class::setTestNow(new $class('2015-12-12 10:10:10'));
  619. $time = new $class('2014-04-20 10:10:10');
  620. $this->assertEquals('1 year ago', $time->diffForHumans());
  621. $time = new $class('2016-04-20 10:10:10');
  622. $this->assertEquals('4 months from now', $time->diffForHumans());
  623. }
  624. /**
  625. * Tests encoding a Time object as json
  626. *
  627. * @dataProvider classNameProvider
  628. * @return void
  629. */
  630. public function testJsonEncode($class)
  631. {
  632. if (version_compare(INTL_ICU_VERSION, '50.0', '<')) {
  633. $this->markTestSkipped('ICU 5x is needed');
  634. }
  635. $time = new $class('2014-04-20 10:10:10');
  636. $this->assertEquals('"2014-04-20T10:10:10+00:00"', json_encode($time));
  637. $class::setJsonEncodeFormat('yyyy-MM-dd HH:mm:ss');
  638. $this->assertEquals('"2014-04-20 10:10:10"', json_encode($time));
  639. $class::setJsonEncodeFormat($class::UNIX_TIMESTAMP_FORMAT);
  640. $this->assertEquals('1397988610', json_encode($time));
  641. }
  642. /**
  643. * Test jsonSerialize no side-effects
  644. *
  645. * @dataProvider classNameProvider
  646. * @return void
  647. */
  648. public function testJsonEncodeSideEffectFree($class)
  649. {
  650. if (version_compare(INTL_ICU_VERSION, '50.0', '<')) {
  651. $this->markTestSkipped('ICU 5x is needed');
  652. }
  653. $date = new \Cake\I18n\FrozenTime('2016-11-29 09:00:00');
  654. $this->assertInstanceOf('DateTimeZone', $date->timezone);
  655. $result = json_encode($date);
  656. $this->assertEquals('"2016-11-29T09:00:00+00:00"', $result);
  657. $this->assertInstanceOf('DateTimeZone', $date->getTimezone());
  658. }
  659. /**
  660. * Tests debugInfo
  661. *
  662. * @dataProvider classNameProvider
  663. * @return void
  664. */
  665. public function testDebugInfo($class)
  666. {
  667. $time = new $class('2014-04-20 10:10:10');
  668. $expected = [
  669. 'time' => '2014-04-20T10:10:10+00:00',
  670. 'timezone' => 'UTC',
  671. 'fixedNowTime' => $class::getTestNow()->toIso8601String()
  672. ];
  673. $this->assertEquals($expected, $time->__debugInfo());
  674. }
  675. /**
  676. * Tests parsing a string into a Time object based on the locale format.
  677. *
  678. * @dataProvider classNameProvider
  679. * @return void
  680. */
  681. public function testParseDateTime($class)
  682. {
  683. $time = $class::parseDateTime('01/01/1970 00:00am');
  684. $this->assertNotNull($time);
  685. $this->assertEquals('1970-01-01 00:00', $time->format('Y-m-d H:i'));
  686. $time = $class::parseDateTime('10/13/2013 12:54am');
  687. $this->assertNotNull($time);
  688. $this->assertEquals('2013-10-13 00:54', $time->format('Y-m-d H:i'));
  689. $class::setDefaultLocale('fr-FR');
  690. $time = $class::parseDateTime('13 10, 2013 12:54');
  691. $this->assertNotNull($time);
  692. $this->assertEquals('2013-10-13 12:54', $time->format('Y-m-d H:i'));
  693. $time = $class::parseDateTime('13 foo 10 2013 12:54');
  694. $this->assertNull($time);
  695. }
  696. /**
  697. * Tests parsing a string into a Time object based on the locale format.
  698. *
  699. * @dataProvider classNameProvider
  700. * @return void
  701. */
  702. public function testParseDate($class)
  703. {
  704. $time = $class::parseDate('10/13/2013 12:54am');
  705. $this->assertNotNull($time);
  706. $this->assertEquals('2013-10-13 00:00', $time->format('Y-m-d H:i'));
  707. $time = $class::parseDate('10/13/2013');
  708. $this->assertNotNull($time);
  709. $this->assertEquals('2013-10-13 00:00', $time->format('Y-m-d H:i'));
  710. $class::setDefaultLocale('fr-FR');
  711. $time = $class::parseDate('13 10, 2013 12:54');
  712. $this->assertNotNull($time);
  713. $this->assertEquals('2013-10-13 00:00', $time->format('Y-m-d H:i'));
  714. $time = $class::parseDate('13 foo 10 2013 12:54');
  715. $this->assertNull($time);
  716. $time = $class::parseDate('13 10, 2013', 'dd M, y');
  717. $this->assertNotNull($time);
  718. $this->assertEquals('2013-10-13', $time->format('Y-m-d'));
  719. }
  720. /**
  721. * Tests parsing times using the parseTime function
  722. *
  723. * @dataProvider classNameProvider
  724. * @return void
  725. */
  726. public function testParseTime($class)
  727. {
  728. $time = $class::parseTime('12:54am');
  729. $this->assertNotNull($time);
  730. $this->assertEquals('00:54:00', $time->format('H:i:s'));
  731. $class::setDefaultLocale('fr-FR');
  732. $time = $class::parseTime('23:54');
  733. $this->assertNotNull($time);
  734. $this->assertEquals('23:54:00', $time->format('H:i:s'));
  735. $time = $class::parseTime('31c2:54');
  736. $this->assertNull($time);
  737. }
  738. /**
  739. * Tests that timeAgoInWords when using a russian locale does not break things
  740. *
  741. * @dataProvider classNameProvider
  742. * @return void
  743. */
  744. public function testRussianTimeAgoInWords($class)
  745. {
  746. I18n::setLocale('ru_RU');
  747. $time = new $class('5 days ago');
  748. $result = $time->timeAgoInWords();
  749. $this->assertEquals('5 days ago', $result);
  750. }
  751. /**
  752. * Tests that parsing a date respects de default timezone in PHP.
  753. *
  754. * @dataProvider classNameProvider
  755. * @return void
  756. */
  757. public function testParseDateDifferentTimezone($class)
  758. {
  759. date_default_timezone_set('Europe/Paris');
  760. $class::setDefaultLocale('fr-FR');
  761. $result = $class::parseDate('12/03/2015');
  762. $this->assertEquals('2015-03-12', $result->format('Y-m-d'));
  763. $this->assertEquals(new \DateTimeZone('Europe/Paris'), $result->tz);
  764. }
  765. /**
  766. * Tests the default locale setter.
  767. *
  768. * @dataProvider classNameProvider
  769. * @return void
  770. */
  771. public function testGetSetDefaultLocale($class)
  772. {
  773. $class::setDefaultLocale('fr-FR');
  774. $this->assertSame('fr-FR', $class::getDefaultLocale());
  775. }
  776. /**
  777. * Tests the default locale setter.
  778. *
  779. * @dataProvider classNameProvider
  780. * @return void
  781. */
  782. public function testDefaultLocaleEffectsFormatting($class)
  783. {
  784. $result = $class::parseDate('12/03/2015');
  785. $this->assertRegExp('/Dec 3, 2015[ ,]+12:00 AM/', $result->nice());
  786. $class::setDefaultLocale('fr-FR');
  787. $result = $class::parseDate('12/03/2015');
  788. $this->assertRegexp('/12 mars 2015 (?:à )?00:00/', $result->nice());
  789. $expected = 'Y-m-d';
  790. $result = $class::parseDate('12/03/2015');
  791. $this->assertEquals('2015-03-12', $result->format($expected));
  792. }
  793. /**
  794. * Custom assert to allow for variation in the version of the intl library, where
  795. * some translations contain a few extra commas.
  796. *
  797. * @param string $expected
  798. * @param string $result
  799. * @return void
  800. */
  801. public function assertTimeFormat($expected, $result, $message = '')
  802. {
  803. $expected = str_replace([',', '(', ')', ' at', ' م.', ' ه‍.ش.', ' AP', ' AH', ' SAKA', 'à '], '', $expected);
  804. $expected = str_replace([' '], ' ', $expected);
  805. $result = str_replace('Temps universel coordonné', 'UTC', $result);
  806. $result = str_replace('tiempo universal coordinado', 'GMT', $result);
  807. $result = str_replace('Coordinated Universal Time', 'GMT', $result);
  808. $result = str_replace([',', '(', ')', ' at', ' م.', ' ه‍.ش.', ' AP', ' AH', ' SAKA', 'à '], '', $result);
  809. $result = str_replace(['گرینویچ'], 'GMT', $result);
  810. $result = str_replace('زمان هماهنگ جهانی', 'GMT', $result);
  811. $result = str_replace('همغږۍ نړیواله موده', 'GMT', $result);
  812. $result = str_replace([' '], ' ', $result);
  813. $this->assertSame($expected, $result, $message);
  814. }
  815. }