DateTest.php 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587
  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\Date;
  17. use Cake\I18n\FrozenDate;
  18. use Cake\TestSuite\TestCase;
  19. use DateTimeZone;
  20. /**
  21. * DateTest class
  22. */
  23. class DateTest extends TestCase
  24. {
  25. /**
  26. * Backup the locale property
  27. *
  28. * @var string
  29. */
  30. protected $locale;
  31. /**
  32. * setup
  33. *
  34. * @return void
  35. */
  36. public function setUp()
  37. {
  38. parent::setUp();
  39. $this->locale = Date::getDefaultLocale();
  40. }
  41. /**
  42. * Teardown
  43. *
  44. * @return void
  45. */
  46. public function tearDown()
  47. {
  48. parent::tearDown();
  49. Date::setDefaultLocale($this->locale);
  50. FrozenDate::setDefaultLocale($this->locale);
  51. date_default_timezone_set('UTC');
  52. }
  53. /**
  54. * Provider for ensuring that Date and FrozenDate work the same way.
  55. *
  56. * @return array
  57. */
  58. public static function classNameProvider()
  59. {
  60. return ['mutable' => ['Cake\I18n\Date'], 'immutable' => ['Cake\I18n\FrozenDate']];
  61. }
  62. /**
  63. * Ensure that instances can be built from other objects.
  64. *
  65. * @dataProvider classNameProvider
  66. * @return void
  67. */
  68. public function testConstructFromAnotherInstance($class)
  69. {
  70. $time = '2015-01-22';
  71. $frozen = new FrozenDate($time, 'America/Chicago');
  72. $subject = new $class($frozen);
  73. $this->assertEquals($time, $subject->format('Y-m-d'), 'frozen date construction');
  74. $mut = new Date($time, 'America/Chicago');
  75. $subject = new $class($mut);
  76. $this->assertEquals($time, $subject->format('Y-m-d'), 'mutable date construction');
  77. }
  78. /**
  79. * test formatting dates taking in account preferred i18n locale file
  80. *
  81. * @dataProvider classNameProvider
  82. * @return void
  83. */
  84. public function testI18nFormat($class)
  85. {
  86. $time = new $class('Thu Jan 14 13:59:28 2010');
  87. $result = $time->i18nFormat();
  88. $expected = '1/14/10';
  89. $this->assertEquals($expected, $result);
  90. $format = [\IntlDateFormatter::NONE, \IntlDateFormatter::SHORT];
  91. $result = $time->i18nFormat($format);
  92. $expected = '12:00 AM';
  93. $this->assertEquals($expected, $result);
  94. $result = $time->i18nFormat('HH:mm:ss', 'Australia/Sydney');
  95. $expected = '00:00:00';
  96. $this->assertEquals($expected, $result);
  97. $class::setDefaultLocale('fr-FR');
  98. $result = $time->i18nFormat(\IntlDateFormatter::FULL);
  99. $result = str_replace(' à', '', $result);
  100. $expected = 'jeudi 14 janvier 2010 00:00:00';
  101. $this->assertStringStartsWith($expected, $result);
  102. $result = $time->i18nFormat(\IntlDateFormatter::FULL, null, 'es-ES');
  103. $this->assertContains('14 de enero de 2010', $result, 'Default locale should not be used');
  104. $time = new $class('2014-01-01T00:00:00Z');
  105. $result = $time->i18nFormat(\IntlDateFormatter::FULL, null, 'en-US');
  106. $expected = 'Wednesday, January 1, 2014 at 12:00:00 AM';
  107. $this->assertStringStartsWith($expected, $result);
  108. }
  109. /**
  110. * test __toString
  111. *
  112. * @dataProvider classNameProvider
  113. * @return void
  114. */
  115. public function testToString($class)
  116. {
  117. $date = new $class('2015-11-06 11:32:45');
  118. $this->assertEquals('11/6/15', (string)$date);
  119. }
  120. /**
  121. * test nice()
  122. *
  123. * @dataProvider classNameProvider
  124. * @return void
  125. */
  126. public function testNice($class)
  127. {
  128. $date = new $class('2015-11-06 11:32:45');
  129. $this->assertEquals('Nov 6, 2015', $date->nice());
  130. $this->assertEquals('Nov 6, 2015', $date->nice(new DateTimeZone('America/New_York')));
  131. $this->assertEquals('6 nov. 2015', $date->nice(null, 'fr-FR'));
  132. }
  133. /**
  134. * test jsonSerialize()
  135. *
  136. * @dataProvider classNameProvider
  137. * @return void
  138. */
  139. public function testJsonSerialize($class)
  140. {
  141. if (version_compare(INTL_ICU_VERSION, '50.0', '<')) {
  142. $this->markTestSkipped('ICU 5x is needed');
  143. }
  144. $date = new $class('2015-11-06 11:32:45');
  145. $this->assertEquals('"2015-11-06T00:00:00+00:00"', json_encode($date));
  146. }
  147. /**
  148. * Tests change json encoding format
  149. *
  150. * @dataProvider classNameProvider
  151. * @return void
  152. */
  153. public function testSetJsonEncodeFormat($class)
  154. {
  155. $date = new $class('2015-11-06 11:32:45');
  156. $class::setJsonEncodeFormat(static function ($d) {
  157. return $d->format(DATE_ATOM);
  158. });
  159. $this->assertEquals('"2015-11-06T00:00:00+00:00"', json_encode($date));
  160. $class::setJsonEncodeFormat("yyyy-MM-dd'T'HH':'mm':'ssZZZZZ");
  161. $this->assertEquals('"2015-11-06T00:00:00Z"', json_encode($date));
  162. }
  163. /**
  164. * test parseDate()
  165. *
  166. * @dataProvider classNameProvider
  167. * @return void
  168. */
  169. public function testParseDate($class)
  170. {
  171. $date = $class::parseDate('11/6/15');
  172. $this->assertEquals('2015-11-06 00:00:00', $date->format('Y-m-d H:i:s'));
  173. $class::setDefaultLocale('fr-FR');
  174. $date = $class::parseDate('13 10, 2015');
  175. $this->assertEquals('2015-10-13 00:00:00', $date->format('Y-m-d H:i:s'));
  176. }
  177. /**
  178. * test parseDateTime()
  179. *
  180. * @dataProvider classNameProvider
  181. * @return void
  182. */
  183. public function testParseDateTime($class)
  184. {
  185. $date = $class::parseDate('11/6/15 12:33:12');
  186. $this->assertEquals('2015-11-06 00:00:00', $date->format('Y-m-d H:i:s'));
  187. $class::setDefaultLocale('fr-FR');
  188. $date = $class::parseDate('13 10, 2015 12:54:12');
  189. $this->assertEquals('2015-10-13 00:00:00', $date->format('Y-m-d H:i:s'));
  190. }
  191. /**
  192. * Tests disabling leniency when parsing locale format.
  193. *
  194. * @dataProvider classNameProvider
  195. * @return void
  196. */
  197. public function testLenientParseDate($class)
  198. {
  199. $class::setDefaultLocale('pt_BR');
  200. $class::disableLenientParsing();
  201. $date = $class::parseDate('04/21/2013');
  202. $this->assertSame(null, $date);
  203. $class::enableLenientParsing();
  204. $date = $class::parseDate('04/21/2013');
  205. $this->assertSame('2014-09-04', $date->format('Y-m-d'));
  206. }
  207. /**
  208. * provider for timeAgoInWords() tests
  209. *
  210. * @return array
  211. */
  212. public static function timeAgoProvider()
  213. {
  214. return [
  215. ['-12 seconds', 'today'],
  216. ['-12 minutes', 'today'],
  217. ['-2 hours', 'today'],
  218. ['-1 day', '1 day ago'],
  219. ['-2 days', '2 days ago'],
  220. ['-1 week', '1 week ago'],
  221. ['-2 weeks -2 days', '2 weeks, 2 days ago'],
  222. ['+1 second', 'today'],
  223. ['+1 minute, +10 seconds', 'today'],
  224. ['+1 week', '1 week'],
  225. ['+1 week 1 day', '1 week, 1 day'],
  226. ['+2 weeks 2 day', '2 weeks, 2 days'],
  227. ['2007-9-24', 'on 9/24/07'],
  228. ['now', 'today'],
  229. ];
  230. }
  231. /**
  232. * testTimeAgoInWords method
  233. *
  234. * @dataProvider timeAgoProvider
  235. * @return void
  236. */
  237. public function testTimeAgoInWords($input, $expected)
  238. {
  239. $date = new Date($input);
  240. $result = $date->timeAgoInWords();
  241. $this->assertEquals($expected, $result);
  242. }
  243. /**
  244. * testTimeAgoInWords with Frozen Date
  245. *
  246. * @dataProvider timeAgoProvider
  247. * @return void
  248. */
  249. public function testTimeAgoInWordsFrozenDate($input, $expected)
  250. {
  251. $date = new FrozenDate($input);
  252. $result = $date->timeAgoInWords();
  253. $this->assertEquals($expected, $result);
  254. }
  255. /**
  256. * test the timezone option for timeAgoInWords
  257. *
  258. * @dataProvider classNameProvider
  259. * @return void
  260. */
  261. public function testTimeAgoInWordsTimezone($class)
  262. {
  263. $date = new $class('1990-07-31 20:33:00 UTC');
  264. $result = $date->timeAgoInWords(
  265. [
  266. 'timezone' => 'America/Vancouver',
  267. 'end' => '+1month',
  268. 'format' => 'dd-MM-YYYY',
  269. ]
  270. );
  271. $this->assertEquals('on 31-07-1990', $result);
  272. }
  273. /**
  274. * provider for timeAgo with an end date.
  275. *
  276. * @return array
  277. */
  278. public function timeAgoEndProvider()
  279. {
  280. return [
  281. [
  282. '+4 months +2 weeks +3 days',
  283. '4 months, 2 weeks, 3 days',
  284. '8 years',
  285. ],
  286. [
  287. '+4 months +2 weeks +1 day',
  288. '4 months, 2 weeks, 1 day',
  289. '8 years',
  290. ],
  291. [
  292. '+3 months +2 weeks',
  293. '3 months, 2 weeks',
  294. '8 years',
  295. ],
  296. [
  297. '+3 months +2 weeks +1 day',
  298. '3 months, 2 weeks, 1 day',
  299. '8 years',
  300. ],
  301. [
  302. '+1 months +1 week +1 day',
  303. '1 month, 1 week, 1 day',
  304. '8 years',
  305. ],
  306. [
  307. '+2 months +2 days',
  308. '2 months, 2 days',
  309. '+2 months +2 days',
  310. ],
  311. [
  312. '+2 months +12 days',
  313. '2 months, 1 week, 5 days',
  314. '3 months',
  315. ],
  316. ];
  317. }
  318. /**
  319. * test the end option for timeAgoInWords
  320. *
  321. * @dataProvider timeAgoEndProvider
  322. * @return void
  323. */
  324. public function testTimeAgoInWordsEnd($input, $expected, $end)
  325. {
  326. $time = new Date($input);
  327. $result = $time->timeAgoInWords(['end' => $end]);
  328. $this->assertEquals($expected, $result);
  329. }
  330. /**
  331. * test the end option for timeAgoInWords
  332. *
  333. * @dataProvider timeAgoEndProvider
  334. * @return void
  335. */
  336. public function testTimeAgoInWordsEndFrozenDate($input, $expected, $end)
  337. {
  338. $time = new FrozenDate($input);
  339. $result = $time->timeAgoInWords(['end' => $end]);
  340. $this->assertEquals($expected, $result);
  341. }
  342. /**
  343. * test the custom string options for timeAgoInWords
  344. *
  345. * @dataProvider classNameProvider
  346. * @return void
  347. */
  348. public function testTimeAgoInWordsCustomStrings($class)
  349. {
  350. $date = new $class('-8 years -4 months -2 weeks -3 days');
  351. $result = $date->timeAgoInWords([
  352. 'relativeString' => 'at least %s ago',
  353. 'accuracy' => ['year' => 'year'],
  354. 'end' => '+10 years',
  355. ]);
  356. $expected = 'at least 8 years ago';
  357. $this->assertEquals($expected, $result);
  358. $date = new $class('+4 months +2 weeks +3 days');
  359. $result = $date->timeAgoInWords([
  360. 'absoluteString' => 'exactly on %s',
  361. 'accuracy' => ['year' => 'year'],
  362. 'end' => '+2 months',
  363. ]);
  364. $expected = 'exactly on ' . date('n/j/y', strtotime('+4 months +2 weeks +3 days'));
  365. $this->assertEquals($expected, $result);
  366. }
  367. /**
  368. * Test the accuracy option for timeAgoInWords()
  369. *
  370. * @dataProvider classNameProvider
  371. * @return void
  372. */
  373. public function testDateAgoInWordsAccuracy($class)
  374. {
  375. $date = new $class('+8 years +4 months +2 weeks +3 days');
  376. $result = $date->timeAgoInWords([
  377. 'accuracy' => ['year' => 'year'],
  378. 'end' => '+10 years',
  379. ]);
  380. $expected = '8 years';
  381. $this->assertEquals($expected, $result);
  382. $date = new $class('+8 years +4 months +2 weeks +3 days');
  383. $result = $date->timeAgoInWords([
  384. 'accuracy' => ['year' => 'month'],
  385. 'end' => '+10 years',
  386. ]);
  387. $expected = '8 years, 4 months';
  388. $this->assertEquals($expected, $result);
  389. $date = new $class('+8 years +4 months +2 weeks +3 days');
  390. $result = $date->timeAgoInWords([
  391. 'accuracy' => ['year' => 'week'],
  392. 'end' => '+10 years',
  393. ]);
  394. $expected = '8 years, 4 months, 2 weeks';
  395. $this->assertEquals($expected, $result);
  396. $date = new $class('+8 years +4 months +2 weeks +3 days');
  397. $result = $date->timeAgoInWords([
  398. 'accuracy' => ['year' => 'day'],
  399. 'end' => '+10 years',
  400. ]);
  401. $expected = '8 years, 4 months, 2 weeks, 3 days';
  402. $this->assertEquals($expected, $result);
  403. $date = new $class('+1 years +5 weeks');
  404. $result = $date->timeAgoInWords([
  405. 'accuracy' => ['year' => 'year'],
  406. 'end' => '+10 years',
  407. ]);
  408. $expected = '1 year';
  409. $this->assertEquals($expected, $result);
  410. $date = new $class('+23 hours');
  411. $result = $date->timeAgoInWords([
  412. 'accuracy' => 'day',
  413. ]);
  414. $expected = 'today';
  415. $this->assertEquals($expected, $result);
  416. }
  417. /**
  418. * Test the format option of timeAgoInWords()
  419. *
  420. * @dataProvider classNameProvider
  421. * @return void
  422. */
  423. public function testDateAgoInWordsWithFormat($class)
  424. {
  425. $date = new $class('2007-9-25');
  426. $result = $date->timeAgoInWords(['format' => 'yyyy-MM-dd']);
  427. $this->assertEquals('on 2007-09-25', $result);
  428. $date = new $class('2007-9-25');
  429. $result = $date->timeAgoInWords(['format' => 'yyyy-MM-dd']);
  430. $this->assertEquals('on 2007-09-25', $result);
  431. $date = new $class('+2 weeks +2 days');
  432. $result = $date->timeAgoInWords(['format' => 'yyyy-MM-dd']);
  433. $this->assertRegExp('/^2 weeks, [1|2] day(s)?$/', $result);
  434. $date = new $class('+2 months +2 days');
  435. $result = $date->timeAgoInWords(['end' => '1 month', 'format' => 'yyyy-MM-dd']);
  436. $this->assertEquals('on ' . date('Y-m-d', strtotime('+2 months +2 days')), $result);
  437. }
  438. /**
  439. * test timeAgoInWords() with negative values.
  440. *
  441. * @dataProvider classNameProvider
  442. * @return void
  443. */
  444. public function testDateAgoInWordsNegativeValues($class)
  445. {
  446. $date = new $class('-2 months -2 days');
  447. $result = $date->timeAgoInWords(['end' => '3 month']);
  448. $this->assertEquals('2 months, 2 days ago', $result);
  449. $date = new $class('-2 months -2 days');
  450. $result = $date->timeAgoInWords(['end' => '3 month']);
  451. $this->assertEquals('2 months, 2 days ago', $result);
  452. $date = new $class('-2 months -2 days');
  453. $result = $date->timeAgoInWords(['end' => '1 month', 'format' => 'yyyy-MM-dd']);
  454. $this->assertEquals('on ' . date('Y-m-d', strtotime('-2 months -2 days')), $result);
  455. $date = new $class('-2 years -5 months -2 days');
  456. $result = $date->timeAgoInWords(['end' => '3 years']);
  457. $this->assertEquals('2 years, 5 months, 2 days ago', $result);
  458. $date = new $class('-2 weeks -2 days');
  459. $result = $date->timeAgoInWords(['format' => 'yyyy-MM-dd']);
  460. $this->assertEquals('2 weeks, 2 days ago', $result);
  461. $date = new $class('-3 years -12 months');
  462. $result = $date->timeAgoInWords();
  463. $expected = 'on ' . $date->format('n/j/y');
  464. $this->assertEquals($expected, $result);
  465. $date = new $class('-1 month -1 week -6 days');
  466. $result = $date->timeAgoInWords(
  467. ['end' => '1 year', 'accuracy' => ['month' => 'month']]
  468. );
  469. $this->assertEquals('1 month ago', $result);
  470. $date = new $class('-1 years -2 weeks -3 days');
  471. $result = $date->timeAgoInWords(
  472. ['accuracy' => ['year' => 'year']]
  473. );
  474. $expected = 'on ' . $date->format('n/j/y');
  475. $this->assertEquals($expected, $result);
  476. $date = new $class('-13 months -5 days');
  477. $result = $date->timeAgoInWords(['end' => '2 years']);
  478. $this->assertEquals('1 year, 1 month, 5 days ago', $result);
  479. $date = new $class('-23 hours');
  480. $result = $date->timeAgoInWords(['accuracy' => 'day']);
  481. $this->assertEquals('today', $result);
  482. }
  483. /**
  484. * Tests that parsing a date in a timezone other than UTC
  485. * will not alter the date
  486. *
  487. * @dataProvider classNameProvider
  488. * @return void
  489. */
  490. public function testParseDateDifferentTimezone($class)
  491. {
  492. date_default_timezone_set('Europe/Paris');
  493. $result = $class::parseDate('25-02-2016', 'd-M-y');
  494. $this->assertEquals('25-02-2016', $result->format('d-m-Y'));
  495. }
  496. /**
  497. * Tests the default locale setter.
  498. *
  499. * @dataProvider classNameProvider
  500. * @return void
  501. */
  502. public function testGetSetDefaultLocale($class)
  503. {
  504. $class::setDefaultLocale('fr-FR');
  505. $this->assertSame('fr-FR', $class::getDefaultLocale());
  506. }
  507. /**
  508. * Tests the default locale setter.
  509. *
  510. * @dataProvider classNameProvider
  511. * @return void
  512. */
  513. public function testDefaultLocaleEffectsFormatting($class)
  514. {
  515. $result = $class::parseDate('12/03/2015');
  516. $this->assertEquals('Dec 3, 2015', $result->nice());
  517. $class::setDefaultLocale('fr-FR');
  518. $result = $class::parseDate('12/03/2015');
  519. $this->assertEquals('12 mars 2015', $result->nice());
  520. $expected = 'Y-m-d';
  521. $result = $class::parseDate('12/03/2015');
  522. $this->assertEquals('2015-03-12', $result->format($expected));
  523. }
  524. }