ForHumansTest.php 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * This file is part of the Carbon package.
  5. *
  6. * (c) Brian Nesbitt <brian@nesbot.com>
  7. *
  8. * For the full copyright and license information, please view the LICENSE
  9. * file that was distributed with this source code.
  10. */
  11. namespace Tests\CarbonInterval;
  12. use Carbon\Carbon;
  13. use Carbon\CarbonInterface;
  14. use Carbon\CarbonInterval;
  15. use Carbon\Translator as CarbonTranslator;
  16. use Symfony\Component\Translation\Loader\ArrayLoader;
  17. use Symfony\Component\Translation\Translator;
  18. use Tests\AbstractTestCase;
  19. use Tests\CarbonInterval\Fixtures\MyCarbonInterval;
  20. class ForHumansTest extends AbstractTestCase
  21. {
  22. protected function tearDown(): void
  23. {
  24. parent::tearDown();
  25. CarbonInterval::setLocale('en');
  26. }
  27. public function testGetTranslator()
  28. {
  29. /** @var CarbonTranslator $t */
  30. $t = CarbonInterval::getTranslator();
  31. $this->assertNotNull($t);
  32. $this->assertSame('en', $t->getLocale());
  33. $this->assertSame('en', CarbonInterval::day()->locale());
  34. }
  35. public function testResetTranslator()
  36. {
  37. /** @var CarbonTranslator $t */
  38. $t = MyCarbonInterval::getTranslator();
  39. $this->assertNotNull($t);
  40. $this->assertSame('en', $t->getLocale());
  41. $this->assertSame('en', CarbonInterval::day()->locale());
  42. }
  43. public function testSetTranslator()
  44. {
  45. /** @var CarbonTranslator $ori */
  46. $ori = CarbonInterval::getTranslator();
  47. $t = new Translator('fr');
  48. $t->addLoader('array', new ArrayLoader());
  49. CarbonInterval::setTranslator($t);
  50. /** @var CarbonTranslator $t */
  51. $t = CarbonInterval::getTranslator();
  52. $this->assertNotNull($t);
  53. $this->assertSame('fr', $t->getLocale());
  54. CarbonInterval::setTranslator($ori);
  55. }
  56. public function testDumpTranslator()
  57. {
  58. $t = new CarbonTranslator('tr_CY');
  59. $this->assertSame([
  60. 'locale' => 'tr_CY',
  61. ], $t->__debugInfo());
  62. }
  63. public function testGetLocale()
  64. {
  65. CarbonInterval::setLocale('en');
  66. $this->assertSame('en', CarbonInterval::getLocale());
  67. }
  68. public function testSetLocale()
  69. {
  70. CarbonInterval::setLocale('en');
  71. $this->assertSame('en', CarbonInterval::getLocale());
  72. CarbonInterval::setLocale('fr');
  73. $this->assertSame('fr', CarbonInterval::getLocale());
  74. }
  75. public function testOptions()
  76. {
  77. CarbonInterval::setLocale('en');
  78. $this->assertSame('1 year 2 months ago', CarbonInterval::year()->add(CarbonInterval::months(2))->forHumans(CarbonInterface::DIFF_RELATIVE_TO_NOW));
  79. $this->assertSame('1 year before', CarbonInterval::year()->add(CarbonInterval::months(2))->forHumans(CarbonInterface::DIFF_RELATIVE_TO_OTHER, 1));
  80. }
  81. public function testYear()
  82. {
  83. CarbonInterval::setLocale('en');
  84. $this->assertSame('1 year', CarbonInterval::year()->forHumans());
  85. }
  86. public function testYearToString()
  87. {
  88. CarbonInterval::setLocale('en');
  89. $this->assertSame('1 year:abc', CarbonInterval::year().':abc');
  90. }
  91. public function testYears()
  92. {
  93. CarbonInterval::setLocale('en');
  94. $this->assertSame('2 years', CarbonInterval::years(2)->forHumans());
  95. }
  96. public function testYearsAndMonth()
  97. {
  98. CarbonInterval::setLocale('en');
  99. $this->assertSame('2 years 1 month', CarbonInterval::create(2, 1)->forHumans());
  100. }
  101. public function testAll()
  102. {
  103. CarbonInterval::setLocale('en');
  104. $ci = CarbonInterval::create(11, 1, 2, 5, 22, 33, 55)->forHumans();
  105. $this->assertSame('11 years 1 month 2 weeks 5 days 22 hours 33 minutes 55 seconds', $ci);
  106. }
  107. public function testYearsAndMonthInFrench()
  108. {
  109. CarbonInterval::setLocale('fr');
  110. $this->assertSame('2 ans 1 mois', CarbonInterval::create(2, 1)->forHumans());
  111. }
  112. public function testYearsAndMonthInGerman()
  113. {
  114. CarbonInterval::setLocale('de');
  115. $this->assertSame('1 Jahr 1 Monat', CarbonInterval::create(1, 1)->forHumans());
  116. $this->assertSame('2 Jahre 1 Monat', CarbonInterval::create(2, 1)->forHumans());
  117. }
  118. public function testYearsAndMonthInBulgarian()
  119. {
  120. CarbonInterval::setLocale('bg');
  121. $this->assertSame('1 година 1 месец', CarbonInterval::create(1, 1)->forHumans());
  122. $this->assertSame('2 години 1 месец', CarbonInterval::create(2, 1)->forHumans());
  123. }
  124. public function testYearsAndMonthInCatalan()
  125. {
  126. CarbonInterval::setLocale('ca');
  127. $this->assertSame('1 any 1 mes', CarbonInterval::create(1, 1)->forHumans());
  128. $this->assertSame('2 anys 1 mes', CarbonInterval::create(2, 1)->forHumans());
  129. }
  130. public function testYearsAndMonthInCzech()
  131. {
  132. CarbonInterval::setLocale('cs');
  133. $this->assertSame('1 rok 1 měsíc', CarbonInterval::create(1, 1)->forHumans());
  134. $this->assertSame('2 roky 1 měsíc', CarbonInterval::create(2, 1)->forHumans());
  135. }
  136. public function testYearsAndMonthInGreek()
  137. {
  138. CarbonInterval::setLocale('el');
  139. $this->assertSame('1 χρόνος 1 μήνας', CarbonInterval::create(1, 1)->forHumans());
  140. $this->assertSame('2 χρόνια 1 μήνας', CarbonInterval::create(2, 1)->forHumans());
  141. }
  142. public function testYearsAndMonthsInDanish()
  143. {
  144. CarbonInterval::setLocale('da');
  145. $this->assertSame('1 år 1 måned', CarbonInterval::create(1, 1)->forHumans());
  146. $this->assertSame('2 år 1 måned', CarbonInterval::create(2, 1)->forHumans());
  147. }
  148. public function testCustomJoin()
  149. {
  150. $interval = CarbonInterval::create(1, 1, 0, 1, 1)->locale('fr');
  151. $this->assertSame('1 an 1 mois 1 jour 1 heure', $interval->forHumans());
  152. $this->assertSame('1 an, 1 mois, 1 jour et 1 heure', $interval->forHumans([
  153. 'join' => true,
  154. ]));
  155. $this->assertSame('တစ်နှစ် တစ်လ တစ်ရက် တစ်နာရီ', $interval->copy()->locale('my')->forHumans([
  156. 'join' => true,
  157. ]));
  158. $this->assertSame('un an, un mois, un jour, une heure', $interval->forHumans([
  159. 'join' => ', ',
  160. 'aUnit' => true,
  161. ]));
  162. $this->assertSame('un an et un mois et un jour et aussi une heure', $interval->forHumans([
  163. 'join' => [' et ', ' et aussi '],
  164. 'aUnit' => true,
  165. ]));
  166. $interval = CarbonInterval::create(1, 1, 0, 1, 1)->locale('en');
  167. $this->assertSame('1 year 1 month 1 day 1 hour', $interval->forHumans());
  168. $this->assertSame('1 year, 1 month, 1 day and 1 hour', $interval->forHumans([
  169. 'join' => true,
  170. ]));
  171. $this->assertSame('1 year, 1 month, 1 day, 1 hour', $interval->forHumans([
  172. 'join' => ', ',
  173. ]));
  174. $this->assertSame('a year and a month and a day and also an hour', $interval->forHumans([
  175. 'join' => [' and ', ' and also '],
  176. 'aUnit' => true,
  177. ]));
  178. $this->assertSame('[1 year;1 month;1 day;1 hour]', $interval->forHumans([
  179. 'join' => function ($list) {
  180. return '['.implode(';', $list).']';
  181. },
  182. ]));
  183. }
  184. public function testChineseJoin()
  185. {
  186. $interval = CarbonInterval::create(1, 1, 0, 1, 1)->locale('zh_Hans');
  187. $this->assertSame('1年1个月1天1小时', $interval->forHumans());
  188. $this->assertSame('1年1个月1天1小时', $interval->forHumans([
  189. 'join' => true,
  190. ]));
  191. $this->assertSame('1 年 1 个月 1 天 1 小时', $interval->forHumans([
  192. 'join' => false,
  193. ]));
  194. $this->assertSame('1年1个月1天1小时', $interval->forHumans([
  195. 'join' => '',
  196. ]));
  197. $this->assertSame('1 年 1 个月 1 天 1 小时', $interval->forHumans([
  198. 'join' => ' ',
  199. ]));
  200. $this->assertSame('1年-1个月-1天-1小时', $interval->forHumans([
  201. 'join' => '-',
  202. ]));
  203. }
  204. public function testOptionsAsArray()
  205. {
  206. $interval = CarbonInterval::create(1, 1, 0, 1, 1)->locale('fr');
  207. $this->assertSame('1 an', $interval->forHumans([
  208. 'join' => 'foo',
  209. 'parts' => 1,
  210. ]));
  211. $this->assertSame('il y a un an', $interval->forHumans([
  212. 'join' => 'foo',
  213. 'aUnit' => true,
  214. 'parts' => 1,
  215. 'syntax' => CarbonInterface::DIFF_RELATIVE_TO_NOW,
  216. ]));
  217. $interval = CarbonInterval::day();
  218. $this->assertSame('1d', $interval->forHumans([
  219. 'short' => true,
  220. ]));
  221. $interval = CarbonInterval::day();
  222. $this->assertSame('yesterday', $interval->forHumans([
  223. 'parts' => 1,
  224. 'syntax' => CarbonInterface::DIFF_RELATIVE_TO_NOW,
  225. 'options' => CarbonInterface::ONE_DAY_WORDS,
  226. ]));
  227. }
  228. public function testRoundYears()
  229. {
  230. CarbonInterval::setLocale('en');
  231. $interval = CarbonInterval::years(2)->months(11);
  232. $this->assertSame('3 years', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  233. $interval = CarbonInterval::years(2)->months(5)->days(70);
  234. $this->assertSame('3 years', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  235. }
  236. public function testRoundMonths()
  237. {
  238. CarbonInterval::setLocale('en');
  239. $interval = CarbonInterval::months(2)->weeks(3);
  240. $this->assertSame('3 months', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  241. }
  242. public function testRoundUp()
  243. {
  244. CarbonInterval::setLocale('en');
  245. $interval = CarbonInterval::days(2)->hours(23);
  246. $this->assertSame('3 days', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  247. }
  248. public function testRoundDown()
  249. {
  250. CarbonInterval::setLocale('en');
  251. $interval = CarbonInterval::days(2)->hours(11);
  252. $this->assertSame('2 days', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  253. }
  254. public function testRoundMinutes()
  255. {
  256. CarbonInterval::setLocale('en');
  257. $interval = CarbonInterval::days(2)->hours(11)->minutes(15);
  258. $this->assertSame('2 days 11 hours', $interval->forHumans(['parts' => 2, 'options' => CarbonInterface::ROUND]));
  259. }
  260. public function testRoundDownWhenNextIntervalIsNonSequential()
  261. {
  262. CarbonInterval::setLocale('en');
  263. $interval = CarbonInterval::days(2)->minutes(59);
  264. $this->assertSame('2 days', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  265. }
  266. public function testRoundMultipleParts()
  267. {
  268. CarbonInterval::setLocale('en');
  269. $interval = CarbonInterval::days(2)->minutes(45)->seconds(59);
  270. $this->assertSame('2 days 46 minutes', $interval->forHumans(['parts' => 2, 'options' => CarbonInterface::ROUND]));
  271. }
  272. public function testRoundMultiplePartsGap()
  273. {
  274. CarbonInterval::setLocale('en');
  275. $interval = CarbonInterval::days(2)->seconds(59);
  276. $this->assertSame('2 days 59 seconds', $interval->forHumans(['parts' => 2]));
  277. $this->assertSame('2 days 59 seconds', $interval->forHumans(['parts' => 2, 'options' => CarbonInterface::ROUND]));
  278. $this->assertSame('2 days', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  279. }
  280. public function testRoundWeeks()
  281. {
  282. CarbonInterval::setLocale('en');
  283. $interval = CarbonInterval::days(13);
  284. $this->assertSame('2 weeks', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  285. }
  286. public function testRoundWeeksWithMultipleParts()
  287. {
  288. CarbonInterval::setLocale('en');
  289. $interval = CarbonInterval::days(13);
  290. $this->assertSame('1 week 6 days', $interval->forHumans(['parts' => 2, 'options' => CarbonInterface::ROUND]));
  291. }
  292. public function testRoundOverflowNonSequentialRoundUp()
  293. {
  294. CarbonInterval::setLocale('en');
  295. $interval = CarbonInterval::years(2)->months(35);
  296. $this->assertSame('5 years', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  297. }
  298. public function testRoundOverflowNonSequentialRoundDown()
  299. {
  300. CarbonInterval::setLocale('en');
  301. $interval = CarbonInterval::years(2)->months(37);
  302. $this->assertSame('5 years', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  303. }
  304. public function testRoundCarryOverDoesntMatter()
  305. {
  306. CarbonInterval::setLocale('en');
  307. $interval = CarbonInterval::days(2)->hours(11)->minutes(59);
  308. $this->assertSame('2 days', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  309. $interval = CarbonInterval::days(2)->minutes(780);
  310. $this->assertSame('3 days', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  311. $interval = CarbonInterval::days(2)->minutes(59)->seconds(58);
  312. $this->assertSame('2 days 1 hour', $interval->forHumans(['parts' => 2, 'options' => CarbonInterface::ROUND]));
  313. $interval = CarbonInterval::days(2)->minutes(59)->seconds(1);
  314. $this->assertSame('2 days 1 hour', $interval->forHumans(['parts' => 2, 'options' => CarbonInterface::CEIL]));
  315. $interval = CarbonInterval::days(2)->minutes(59)->seconds(58);
  316. $this->assertSame('2 days 59 minutes', $interval->forHumans(['parts' => 2, 'options' => CarbonInterface::FLOOR]));
  317. // Floor is not the default behavior, see the difference below:
  318. $interval = CarbonInterval::days(2)->minutes(59)->seconds(62);
  319. $this->assertSame('2 days 1 hour', $interval->forHumans(['parts' => 2, 'options' => CarbonInterface::FLOOR]));
  320. $interval = CarbonInterval::days(2)->minutes(59)->seconds(62);
  321. $this->assertSame('2 days 59 minutes', $interval->forHumans(['parts' => 2]));
  322. $start = Carbon::create(2009, 9)->startOfMonth();
  323. $end = Carbon::create(2012, 2)->endOfMonth();
  324. $interval = CarbonInterval::instance($start->diff($end))->forHumans(
  325. Carbon::DIFF_ABSOLUTE,
  326. false,
  327. 2,
  328. Carbon::CEIL
  329. );
  330. $this->assertSame('2 years 6 months', $interval);
  331. $interval = CarbonInterval::months(2)->days(30);
  332. $this->assertSame('3 months', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  333. $interval = CarbonInterval::months(2)->days(31);
  334. $this->assertSame('3 months', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  335. $interval = CarbonInterval::months(2)->days(32);
  336. $this->assertSame('3 months', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::ROUND]));
  337. $interval = CarbonInterval::months(2)->days(30);
  338. $this->assertSame('3 months', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::CEIL]));
  339. $interval = CarbonInterval::months(2)->days(31);
  340. $this->assertSame('3 months', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::CEIL]));
  341. $interval = CarbonInterval::months(2)->days(32);
  342. $this->assertSame('4 months', $interval->forHumans(['parts' => 1, 'options' => CarbonInterface::CEIL]));
  343. }
  344. public function testCeilShortMonth()
  345. {
  346. Carbon::setTestNow('2022-02-08T10:27:03Z');
  347. $this->assertSame(
  348. '4 weeks ago',
  349. Carbon::parse('2022-01-11 15:36:29')->diffForHumans(['parts' => 1, 'options' => Carbon::CEIL])
  350. );
  351. $this->assertSame(
  352. '1 month ago',
  353. Carbon::parse('2022-01-10 16:57:38')->diffForHumans(['parts' => 1, 'options' => Carbon::CEIL])
  354. );
  355. }
  356. public function testSkipUnits()
  357. {
  358. CarbonInterval::setLocale('en');
  359. $interval = CarbonInterval::days(15)->hours(11)->minutes(15);
  360. $this->assertSame('15 days 11 hours', $interval->forHumans([
  361. 'parts' => 2,
  362. 'skip' => 'week',
  363. ]));
  364. $this->assertSame('15 days 675 minutes', $interval->forHumans([
  365. 'parts' => 2,
  366. 'skip' => ['weeks', 'hours'],
  367. ]));
  368. $interval = CarbonInterval::days(15)->hours(11)->minutes(15);
  369. $this->assertSame('15 days 675 minutes', $interval->forHumans([
  370. 'parts' => 2,
  371. 'skip' => ['weeks', 'hours'],
  372. ]));
  373. $factors = CarbonInterval::getCascadeFactors();
  374. CarbonInterval::setCascadeFactors(['weeks' => [5, 'days']]);
  375. $interval = CarbonInterval::days(15)->hours(11)->minutes(15);
  376. $this->assertSame('15 days 675 minutes', $interval->forHumans([
  377. 'parts' => 2,
  378. 'skip' => ['weeks', 'hours'],
  379. ]));
  380. CarbonInterval::setCascadeFactors([
  381. 'weeks' => [5, 'days'],
  382. 'hours' => [30, 'minutes'],
  383. ]);
  384. $interval = CarbonInterval::days(15)->hours(11)->minutes(15);
  385. $this->assertSame('15 days 345 minutes', $interval->forHumans([
  386. 'parts' => 2,
  387. 'skip' => ['weeks', 'hours'],
  388. ]));
  389. CarbonInterval::setCascadeFactors($factors);
  390. }
  391. public function testGetValuesSequence()
  392. {
  393. $this->assertSame([], CarbonInterval::days(0)->getValuesSequence());
  394. }
  395. public function testMinimumUnitDefault()
  396. {
  397. CarbonInterval::setLocale('en');
  398. $interval = CarbonInterval::fromString('1 second 114 milliseconds');
  399. $this->assertSame('1 second', $interval->forHumans(['parts' => 3]));
  400. }
  401. public function testMinimumUnitHours()
  402. {
  403. CarbonInterval::setLocale('en');
  404. $interval = CarbonInterval::fromString('1 hour 1 second 114 milliseconds');
  405. $this->assertSame('1 hour', $interval->forHumans(['parts' => 3, 'minimumUnit' => 'hour']));
  406. }
  407. public function testMinimumUnitMillisecondsShort()
  408. {
  409. CarbonInterval::setLocale('en');
  410. $interval = CarbonInterval::fromString('1 second 114 milliseconds');
  411. $this->assertSame('1s 114ms', $interval->forHumans(['parts' => 3, 'short' => true, 'minimumUnit' => 'ms']));
  412. }
  413. public function testMinimumUnitMicroseconds()
  414. {
  415. CarbonInterval::setLocale('fr');
  416. $interval = CarbonInterval::fromString('1s 114ms 584µs');
  417. $this->assertEquals(
  418. '1 seconde, 114 millisecondes et 584 microsecondes',
  419. $interval->forHumans(['parts' => 3, 'join' => true, 'minimumUnit' => 'µs'])
  420. );
  421. }
  422. public function testMinimumUnitMillisecondsInFrenchAndGerman()
  423. {
  424. $interval = CarbonInterval::fromString('98756 milliseconds')->cascade();
  425. $this->assertEquals(
  426. 'une minute, 38 secondes et 756 millisecondes',
  427. $interval->locale('fr')->forHumans(['parts' => 3, 'minimumUnit' => 'ms', 'join' => true, 'aUnit' => true])
  428. );
  429. $this->assertEquals(
  430. 'eine Minute, 38 Sekunden und 756 Millisekunden',
  431. $interval->locale('de')->forHumans(['parts' => 3, 'minimumUnit' => 'ms', 'join' => true, 'aUnit' => true])
  432. );
  433. }
  434. public function testMinimumUnitNoInterval()
  435. {
  436. CarbonInterval::setLocale('en');
  437. $interval = CarbonInterval::fromString('1 second 114 milliseconds');
  438. // Test with and without NO_ZERO_DIFF
  439. $this->assertSame('1 hour', $interval->forHumans(['parts' => 3, 'minimumUnit' => 'hour', 'options' => CarbonInterface::NO_ZERO_DIFF]));
  440. $this->assertSame('0 hours', $interval->forHumans(['parts' => 3, 'minimumUnit' => 'hour', 'options' => 0]));
  441. }
  442. }