CreateTest.php 31 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821
  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\CarbonPeriod;
  12. use BadMethodCallException;
  13. use Carbon\Carbon;
  14. use Carbon\CarbonImmutable;
  15. use Carbon\CarbonInterval;
  16. use Carbon\CarbonPeriod;
  17. use Carbon\CarbonPeriodImmutable;
  18. use Carbon\Exceptions\NotAPeriodException;
  19. use DateInterval;
  20. use DatePeriod;
  21. use DateTime;
  22. use Generator;
  23. use InvalidArgumentException;
  24. use stdClass;
  25. use Tests\AbstractTestCase;
  26. class CreateTest extends AbstractTestCase
  27. {
  28. /**
  29. * @dataProvider \Tests\CarbonPeriod\CreateTest::dataForIso8601String
  30. */
  31. public function testCreateFromIso8601String($arguments, $expected)
  32. {
  33. $periodClass = $this->periodClass;
  34. [$iso, $options] = array_pad($arguments, 2, null);
  35. $period = $periodClass::create($iso, $options);
  36. $this->assertSame(
  37. $this->standardizeDates($expected),
  38. $this->standardizeDates($period)
  39. );
  40. }
  41. public static function dataForIso8601String(): Generator
  42. {
  43. yield [
  44. ['R4/2012-07-01T00:00:00/P7D'],
  45. ['2012-07-01', '2012-07-08', '2012-07-15', '2012-07-22'],
  46. ];
  47. yield [
  48. ['R4/2012-07-01T00:00:00/P7D', CarbonPeriod::EXCLUDE_START_DATE],
  49. ['2012-07-08', '2012-07-15', '2012-07-22', '2012-07-29'],
  50. ];
  51. yield [
  52. ['2012-07-01/P2D/2012-07-07'],
  53. ['2012-07-01', '2012-07-03', '2012-07-05', '2012-07-07'],
  54. ];
  55. yield [
  56. ['2012-07-01/2012-07-04', CarbonPeriod::EXCLUDE_END_DATE],
  57. ['2012-07-01', '2012-07-02', '2012-07-03'],
  58. ];
  59. yield [
  60. ['R2/2012-07-01T10:30:45Z/P2D'],
  61. ['2012-07-01 10:30:45 UTC', '2012-07-03 10:30:45 UTC'],
  62. ];
  63. }
  64. public function testCreateFromIso8601StringWithUnboundedRecurrences()
  65. {
  66. $periodClass = $this->periodClass;
  67. $period = $periodClass::create('R/2012-07-01T00:00:00/P7D');
  68. $this->assertSame('2012-07-01', $period->getStartDate()->toDateString());
  69. $this->assertSame('P7D', $period->getDateInterval()->spec());
  70. $this->assertNull($period->getEndDate());
  71. $this->assertNull($period->getRecurrences());
  72. }
  73. public function testCreateFromIso8601StringWithInfiniteRecurrences()
  74. {
  75. $periodClass = $this->periodClass;
  76. $period = $periodClass::create('RINF/2012-07-01T00:00:00/P7D');
  77. $this->assertSame('2012-07-01', $period->getStartDate()->toDateString());
  78. $this->assertSame('P7D', $period->getDateInterval()->spec());
  79. $this->assertNull($period->getEndDate());
  80. $this->assertInfinite($period->getRecurrences());
  81. }
  82. /**
  83. * @dataProvider \Tests\CarbonPeriod\CreateTest::dataForPartialIso8601String
  84. */
  85. public function testCreateFromPartialIso8601String($iso, $from, $to)
  86. {
  87. $periodClass = $this->periodClass;
  88. $period = $periodClass::create($iso);
  89. $this->assertSame(
  90. $this->standardizeDates([$from, $to]),
  91. $this->standardizeDates([$period->getStartDate(), $period->getEndDate()])
  92. );
  93. $period = new $periodClass($iso);
  94. $this->assertSame(
  95. $this->standardizeDates([$from, $to]),
  96. $this->standardizeDates([$period->getStartDate(), $period->getEndDate()])
  97. );
  98. }
  99. public static function dataForPartialIso8601String(): Generator
  100. {
  101. yield ['2008-02-15/03-14', '2008-02-15', '2008-03-14'];
  102. yield ['2007-12-14T13:30/15:30', '2007-12-14 13:30', '2007-12-14 15:30'];
  103. }
  104. /**
  105. * @dataProvider \Tests\CarbonPeriod\CreateTest::dataForInvalidIso8601String
  106. */
  107. public function testCreateFromInvalidIso8601String($iso)
  108. {
  109. $this->expectExceptionObject(new InvalidArgumentException(
  110. "Invalid ISO 8601 specification: $iso"
  111. ));
  112. $periodClass = $this->periodClass;
  113. $periodClass::create($iso);
  114. }
  115. public static function dataForInvalidIso8601String(): Generator
  116. {
  117. yield ['R2/R4'];
  118. yield ['2008-02-15/2008-02-16/2008-02-17'];
  119. yield ['P1D/2008-02-15/P2D'];
  120. yield ['2008-02-15/R5'];
  121. yield ['P2D/R2'];
  122. yield ['/'];
  123. }
  124. /**
  125. * @dataProvider \Tests\CarbonPeriod\CreateTest::dataForStartDateAndEndDate
  126. */
  127. public function testCreateFromStartDateAndEndDate($arguments, $expected)
  128. {
  129. $periodClass = $this->periodClass;
  130. [$start, $end, $options] = array_pad($arguments, 3, null);
  131. $start = Carbon::parse($start);
  132. $end = Carbon::parse($end);
  133. $period = $periodClass::create($start, $end, $options);
  134. $this->assertSame(
  135. $this->standardizeDates($expected),
  136. $this->standardizeDates($period)
  137. );
  138. }
  139. public static function dataForStartDateAndEndDate(): Generator
  140. {
  141. yield [
  142. ['2015-09-30', '2015-10-03'],
  143. ['2015-09-30', '2015-10-01', '2015-10-02', '2015-10-03'],
  144. ];
  145. yield [
  146. ['2015-09-30', '2015-10-03', CarbonPeriod::EXCLUDE_START_DATE],
  147. ['2015-10-01', '2015-10-02', '2015-10-03'],
  148. ];
  149. yield [
  150. ['2015-09-30', '2015-10-03', CarbonPeriod::EXCLUDE_END_DATE],
  151. ['2015-09-30', '2015-10-01', '2015-10-02'],
  152. ];
  153. yield [
  154. ['2015-09-30', '2015-10-03', CarbonPeriod::EXCLUDE_START_DATE | CarbonPeriod::EXCLUDE_END_DATE],
  155. ['2015-10-01', '2015-10-02'],
  156. ];
  157. yield [
  158. ['2015-10-02', '2015-10-03', CarbonPeriod::EXCLUDE_START_DATE | CarbonPeriod::EXCLUDE_END_DATE],
  159. [],
  160. ];
  161. yield [
  162. ['2015-10-02', '2015-10-02'],
  163. ['2015-10-02'],
  164. ];
  165. yield [
  166. ['2015-10-02', '2015-10-02', CarbonPeriod::EXCLUDE_START_DATE],
  167. [],
  168. ];
  169. yield [
  170. ['2015-10-02', '2015-10-02', CarbonPeriod::EXCLUDE_END_DATE],
  171. [],
  172. ];
  173. }
  174. /**
  175. * @dataProvider \Tests\CarbonPeriod\CreateTest::dataForStartDateAndIntervalAndEndDate
  176. */
  177. public function testCreateFromStartDateAndIntervalAndEndDate($arguments, $expected)
  178. {
  179. $periodClass = $this->periodClass;
  180. [$start, $interval, $end, $options] = array_pad($arguments, 4, null);
  181. $start = Carbon::parse($start);
  182. $interval = CarbonInterval::create($interval);
  183. $end = Carbon::parse($end);
  184. $period = $periodClass::create($start, $interval, $end, $options);
  185. $this->assertSame(
  186. $this->standardizeDates($expected),
  187. $this->standardizeDates($period)
  188. );
  189. }
  190. public static function dataForStartDateAndIntervalAndEndDate(): Generator
  191. {
  192. yield [
  193. ['2018-04-21', 'P3D', '2018-04-26'],
  194. ['2018-04-21', '2018-04-24'],
  195. ];
  196. yield [
  197. ['2018-04-21 16:15', 'PT15M', '2018-04-21 16:59:59'],
  198. ['2018-04-21 16:15', '2018-04-21 16:30', '2018-04-21 16:45'],
  199. ];
  200. yield [
  201. ['2018-04-21 16:15', 'PT15M', '2018-04-21 17:00'],
  202. ['2018-04-21 16:15', '2018-04-21 16:30', '2018-04-21 16:45', '2018-04-21 17:00'],
  203. ];
  204. yield [
  205. ['2018-04-21 17:00', 'PT45S', '2018-04-21 17:02', CarbonPeriod::EXCLUDE_START_DATE],
  206. ['2018-04-21 17:00:45', '2018-04-21 17:01:30'],
  207. ];
  208. yield [
  209. ['2017-12-31 22:00', 'PT2H', '2018-01-01 4:00', CarbonPeriod::EXCLUDE_END_DATE],
  210. ['2017-12-31 22:00', '2018-01-01 0:00', '2018-01-01 2:00'],
  211. ];
  212. yield [
  213. [
  214. '2017-12-31 23:59',
  215. 'PT30S',
  216. '2018-01-01 0:01',
  217. CarbonPeriod::EXCLUDE_START_DATE | CarbonPeriod::EXCLUDE_END_DATE,
  218. ],
  219. ['2017-12-31 23:59:30', '2018-01-01 0:00', '2018-01-01 0:00:30'],
  220. ];
  221. yield [
  222. ['2018-04-21', 'P1D', '2018-04-21'],
  223. ['2018-04-21'],
  224. ];
  225. yield [
  226. ['2018-04-21', 'P1D', '2018-04-20 23:59:59'],
  227. [],
  228. ];
  229. }
  230. /**
  231. * @dataProvider \Tests\CarbonPeriod\CreateTest::dataForStartDateAndIntervalAndRecurrences
  232. */
  233. public function testCreateFromStartDateAndIntervalAndRecurrences($arguments, $expected)
  234. {
  235. $periodClass = $this->periodClass;
  236. [$start, $interval, $recurrences, $options] = array_pad($arguments, 4, null);
  237. $start = Carbon::parse($start);
  238. $interval = CarbonInterval::create($interval);
  239. $period = $periodClass::create($start, $interval, $recurrences, $options);
  240. $this->assertSame(
  241. $this->standardizeDates($expected),
  242. $this->standardizeDates($period)
  243. );
  244. }
  245. public static function dataForStartDateAndIntervalAndRecurrences(): Generator
  246. {
  247. yield [
  248. ['2018-04-16', 'P2D', 3],
  249. ['2018-04-16', '2018-04-18', '2018-04-20'],
  250. ];
  251. yield [
  252. ['2018-04-30', 'P2M', 2, CarbonPeriod::EXCLUDE_START_DATE],
  253. ['2018-06-30', '2018-08-30'],
  254. ];
  255. }
  256. /**
  257. * @dataProvider \Tests\CarbonPeriod\CreateTest::dataForStartDateAndRecurrences
  258. */
  259. public function testCreateFromStartDateAndRecurrences($arguments, $expected)
  260. {
  261. $periodClass = $this->periodClass;
  262. [$start, $recurrences, $options] = array_pad($arguments, 4, null);
  263. $start = Carbon::parse($start);
  264. $period = $periodClass::create($start, $recurrences, $options);
  265. $this->assertSame(
  266. $this->standardizeDates($expected),
  267. $this->standardizeDates($period)
  268. );
  269. }
  270. public static function dataForStartDateAndRecurrences(): Generator
  271. {
  272. yield [
  273. ['2018-04-16', 2],
  274. ['2018-04-16', '2018-04-17'],
  275. ];
  276. yield [
  277. ['2018-04-30', 1],
  278. ['2018-04-30'],
  279. ];
  280. yield [
  281. ['2018-04-30', 1, CarbonPeriod::EXCLUDE_START_DATE],
  282. ['2018-05-01'],
  283. ];
  284. yield [
  285. ['2018-05-17', 0],
  286. [],
  287. ];
  288. }
  289. public function testCreateFromBaseClasses()
  290. {
  291. $periodClass = $this->periodClass;
  292. $period = $periodClass::create(
  293. new DateTime('2018-04-16'),
  294. new DateInterval('P1M'),
  295. new DateTime('2018-07-15')
  296. );
  297. $this->assertSame(
  298. [
  299. '2018-04-16 00:00:00 -04:00',
  300. '2018-05-16 00:00:00 -04:00',
  301. '2018-06-16 00:00:00 -04:00',
  302. ],
  303. $this->standardizeDates($period)
  304. );
  305. }
  306. /**
  307. * @dataProvider \Tests\CarbonPeriod\CreateTest::dataForInvalidParameters
  308. */
  309. public function testCreateFromInvalidParameters(...$arguments)
  310. {
  311. $this->expectExceptionObject(new InvalidArgumentException(
  312. 'Invalid constructor parameters.'
  313. ));
  314. $periodClass = $this->periodClass;
  315. $periodClass::create(...$arguments);
  316. }
  317. public static function dataForInvalidParameters(): Generator
  318. {
  319. yield [new stdClass(), CarbonInterval::days(1), Carbon::tomorrow()];
  320. yield [Carbon::now(), new stdClass(), Carbon::tomorrow()];
  321. yield [Carbon::now(), CarbonInterval::days(1), new stdClass()];
  322. yield [Carbon::yesterday(), Carbon::now(), Carbon::tomorrow()];
  323. yield [CarbonInterval::day(), CarbonInterval::hour()];
  324. yield [5, CarbonPeriod::EXCLUDE_START_DATE, CarbonPeriod::EXCLUDE_END_DATE];
  325. yield ['2017-10-15/P3D', CarbonInterval::hour()];
  326. }
  327. public function testCreateOnDstForwardChange()
  328. {
  329. $periodClass = $this->periodClass;
  330. $period = $periodClass::create(
  331. '2018-03-25 1:30 Europe/Oslo',
  332. 'PT30M',
  333. '2018-03-25 3:30 Europe/Oslo'
  334. );
  335. $this->assertSame(
  336. [
  337. '2018-03-25 01:30:00 +01:00',
  338. '2018-03-25 03:00:00 +02:00',
  339. '2018-03-25 03:30:00 +02:00',
  340. ],
  341. $this->standardizeDates($period)
  342. );
  343. }
  344. /**
  345. * Incorrect behaviour is caused by a bug in DateTime handling of DST backward change.
  346. * It was fixed by incrementing date casted to UTC, but offsets are still kind of wrong.
  347. *
  348. * @see https://bugs.php.net/bug.php?id=72255
  349. * @see https://bugs.php.net/bug.php?id=74274
  350. * @see https://wiki.php.net/rfc/datetime_and_daylight_saving_time
  351. */
  352. public function testCreateOnDstBackwardChange()
  353. {
  354. $periodClass = $this->periodClass;
  355. $period = $periodClass::create(
  356. '2018-10-28 1:30 Europe/Oslo',
  357. 'PT30M',
  358. '2018-10-28 3:30 Europe/Oslo'
  359. );
  360. $this->assertSame(
  361. [
  362. '2018-10-28 01:30:00 +02:00',
  363. '2018-10-28 02:00:00 +02:00',
  364. '2018-10-28 02:30:00 +02:00',
  365. '2018-10-28 02:00:00 +01:00',
  366. '2018-10-28 02:30:00 +01:00',
  367. '2018-10-28 03:00:00 +01:00',
  368. '2018-10-28 03:30:00 +01:00',
  369. ],
  370. $this->standardizeDates($period)
  371. );
  372. }
  373. public function testInternalVariablesCannotBeIndirectlyModified()
  374. {
  375. $periodClass = $this->periodClass;
  376. $period = $periodClass::create(
  377. $start = new DateTime('2018-04-16'),
  378. $interval = new DateInterval('P1M'),
  379. $end = new DateTime('2018-07-15')
  380. );
  381. $start->modify('-5 days');
  382. $interval->d = 15;
  383. $end->modify('+5 days');
  384. $this->assertSame('2018-04-16', $period->getStartDate()->toDateString());
  385. $this->assertSame('P1M', $period->getDateInterval()->spec());
  386. $this->assertSame('2018-07-15', $period->getEndDate()->toDateString());
  387. $period = $periodClass::create(
  388. $start = new Carbon('2018-04-16'),
  389. $interval = new CarbonInterval('P1M'),
  390. $end = new Carbon('2018-07-15')
  391. );
  392. $start->subDays(5);
  393. $interval->days(15);
  394. $end->addDays(5);
  395. $this->assertSame('2018-04-16', $period->getStartDate()->toDateString());
  396. $this->assertSame('P1M', $period->getDateInterval()->spec());
  397. $this->assertSame('2018-07-15', $period->getEndDate()->toDateString());
  398. }
  399. public function testCreateFromArray()
  400. {
  401. $periodClass = $this->periodClass;
  402. $period = $periodClass::createFromArray([
  403. '2018-03-25', 'P2D', '2018-04-01', $periodClass::EXCLUDE_END_DATE,
  404. ]);
  405. $this->assertSame('2018-03-25', $period->getStartDate()->toDateString());
  406. $this->assertSame('P2D', $period->getDateInterval()->spec());
  407. $this->assertSame('2018-04-01', $period->getEndDate()->toDateString());
  408. $this->assertSame($periodClass::EXCLUDE_END_DATE, $period->getOptions());
  409. }
  410. public function testCreateFromIso()
  411. {
  412. $periodClass = $this->periodClass;
  413. $period = $periodClass::createFromIso('R3/2018-03-25/P2D/2018-04-01', $periodClass::EXCLUDE_END_DATE);
  414. $this->assertSame('2018-03-25', $period->getStartDate()->toDateString());
  415. $this->assertSame('P2D', $period->getDateInterval()->spec());
  416. $this->assertSame('2018-04-01', $period->getEndDate()->toDateString());
  417. $this->assertSame(3, $period->getRecurrences());
  418. $this->assertSame($periodClass::EXCLUDE_END_DATE, $period->getOptions());
  419. }
  420. public function testCreateEmpty()
  421. {
  422. $periodClass = $this->periodClass;
  423. $period = new $periodClass();
  424. $this->assertEquals(new Carbon(), $period->getStartDate());
  425. $this->assertSame('P1D', $period->getDateInterval()->spec());
  426. $this->assertNull($period->getEndDate());
  427. $this->assertNull($period->getRecurrences());
  428. $this->assertSame(0, $period->getOptions());
  429. }
  430. public function testCreateFromDateStringsWithTimezones()
  431. {
  432. $periodClass = $this->periodClass;
  433. $period = $periodClass::create(
  434. $start = '2018-03-25 10:15:30 Europe/Oslo',
  435. $end = '2018-03-28 17:25:30 Asia/Kamchatka'
  436. );
  437. $this->assertSame($start, $period->getStartDate()->format('Y-m-d H:i:s e'));
  438. $this->assertSame($end, $period->getEndDate()->format('Y-m-d H:i:s e'));
  439. }
  440. public function testCreateWithIntervalInFromStringFormat()
  441. {
  442. $periodClass = $this->periodClass;
  443. $period = $periodClass::create(
  444. '2018-03-25 12:00',
  445. '2 days 10 hours',
  446. '2018-04-01 13:30'
  447. );
  448. $this->assertSame(
  449. $this->standardizeDates(['2018-03-25 12:00', '2018-03-27 22:00', '2018-03-30 08:00']),
  450. $this->standardizeDates($period)
  451. );
  452. $period = $periodClass::create(
  453. '2018-04-21',
  454. '3 days',
  455. '2018-04-27'
  456. );
  457. $this->assertSame(
  458. $this->standardizeDates(['2018-04-21 00:00', '2018-04-24 00:00', '2018-04-27 00:00']),
  459. $this->standardizeDates($period)
  460. );
  461. }
  462. public function testCreateFromRelativeDates()
  463. {
  464. $periodClass = $this->periodClass;
  465. $period = $periodClass::create(
  466. $start = 'previous friday',
  467. $end = '+6 days'
  468. );
  469. $this->assertEquals(new Carbon($start), $period->getStartDate());
  470. $this->assertEquals(new Carbon($end), $period->getEndDate());
  471. }
  472. public function testCreateFromCarbonInstances()
  473. {
  474. $period = Carbon::create('2019-01-02')->toPeriod(7);
  475. $this->assertSame(24, $period->getDateInterval()->totalHours);
  476. $this->assertInstanceOf(Carbon::class, $period->getStartDate());
  477. $this->assertSame('2019-01-02', $period->getStartDate()->format('Y-m-d'));
  478. $this->assertNull($period->getEndDate());
  479. $this->assertSame(7, $period->getRecurrences());
  480. $end = $period->calculateEnd();
  481. $this->assertInstanceOf(Carbon::class, $end);
  482. $this->assertSame('2019-01-08', $end->format('Y-m-d'));
  483. $period = Carbon::create('2019-01-02')->toPeriod('2019-02-05');
  484. $this->assertNull($period->getRecurrences());
  485. $this->assertSame(24, $period->getDateInterval()->totalHours);
  486. $this->assertInstanceOf(Carbon::class, $period->getStartDate());
  487. $this->assertSame('2019-01-02', $period->getStartDate()->format('Y-m-d'));
  488. $this->assertInstanceOf(Carbon::class, $period->getEndDate());
  489. $this->assertSame('2019-02-05', $period->getEndDate()->format('Y-m-d'));
  490. $period = Carbon::create('2019-01-02')->range('2019-02-05');
  491. $this->assertInstanceOf(Carbon::class, $period->getStartDate());
  492. $this->assertSame('2019-01-02', $period->getStartDate()->format('Y-m-d'));
  493. $this->assertInstanceOf(Carbon::class, $period->getEndDate());
  494. $this->assertSame('2019-02-05', $period->getEndDate()->format('Y-m-d'));
  495. $period = Carbon::create('2019-01-02')->daysUntil('2019-02-05');
  496. $this->assertSame(24, $period->getDateInterval()->totalHours);
  497. $this->assertInstanceOf(Carbon::class, $period->getStartDate());
  498. $this->assertSame('2019-01-02', $period->getStartDate()->format('Y-m-d'));
  499. $this->assertInstanceOf(Carbon::class, $period->getEndDate());
  500. $this->assertSame('2019-02-05', $period->getEndDate()->format('Y-m-d'));
  501. $period = CarbonImmutable::create('2019-01-02')->daysUntil('2019-02-05');
  502. $this->assertInstanceOf(CarbonImmutable::class, $period->getStartDate());
  503. $this->assertSame('2019-01-02', $period->getStartDate()->format('Y-m-d'));
  504. $this->assertInstanceOf(CarbonImmutable::class, $period->getEndDate());
  505. $this->assertSame('2019-02-05', $period->getEndDate()->format('Y-m-d'));
  506. $period = CarbonImmutable::create('2019-01-02')->daysUntil(Carbon::parse('2019-02-05'));
  507. $this->assertSame(CarbonImmutable::class, $period->getDateClass());
  508. $this->assertInstanceOf(CarbonImmutable::class, $period->getStartDate());
  509. $this->assertSame('2019-01-02', $period->getStartDate()->format('Y-m-d'));
  510. $this->assertInstanceOf(CarbonImmutable::class, $period->getEndDate());
  511. $this->assertSame('2019-02-05', $period->getEndDate()->format('Y-m-d'));
  512. $period = Carbon::create('2019-01-02')->hoursUntil('2019-02-05');
  513. $this->assertSame(1, $period->getDateInterval()->totalHours);
  514. $this->assertSame('1 minute', Carbon::create('2019-01-02')->minutesUntil('2019-02-05')->getDateInterval()->forHumans());
  515. $this->assertSame('3 minutes', Carbon::create('2019-01-02')->minutesUntil('2019-02-05', 3)->getDateInterval()->forHumans());
  516. $this->assertSame('3 seconds', Carbon::create('2019-01-02')->range('2019-02-05', 3, 'seconds')->getDateInterval()->forHumans());
  517. $this->assertSame('1 second', Carbon::create('2019-01-02')->secondsUntil('2019-02-05')->getDateInterval()->forHumans());
  518. $this->assertSame(1, Carbon::create('2019-01-02')->millisecondsUntil('2019-02-05')->getDateInterval()->totalMilliseconds);
  519. $this->assertSame(1, Carbon::create('2019-01-02')->microsecondsUntil('2019-02-05')->getDateInterval()->totalMicroseconds);
  520. $this->assertSame('1 week', Carbon::create('2019-01-02')->weeksUntil('2019-02-05')->getDateInterval()->forHumans());
  521. $this->assertSame('1 month', Carbon::create('2019-01-02')->monthsUntil('2019-02-05')->getDateInterval()->forHumans());
  522. $this->assertSame('3 months', Carbon::create('2019-01-02')->quartersUntil('2019-02-05')->getDateInterval()->forHumans());
  523. $this->assertSame('1 year', Carbon::create('2019-01-02')->yearsUntil('2019-02-05')->getDateInterval()->forHumans());
  524. $this->assertSame('10 years', Carbon::create('2019-01-02')->decadesUntil('2019-02-05')->getDateInterval()->forHumans());
  525. $this->assertSame('100 years', Carbon::create('2019-01-02')->centuriesUntil('2019-02-05')->getDateInterval()->forHumans());
  526. $this->assertSame('1000 years', Carbon::create('2019-01-02')->millenniaUntil('2019-02-05')->getDateInterval()->forHumans());
  527. }
  528. public function testCreateFromCarbonInstanceInvalidMethod()
  529. {
  530. $this->expectExceptionObject(new BadMethodCallException(
  531. 'Method unknownUnitsUntil does not exist.'
  532. ));
  533. /** @var object $date */
  534. $date = Carbon::create('2019-01-02');
  535. $date->unknownUnitsUntil('2019-02-05');
  536. }
  537. public function testInstance()
  538. {
  539. $periodClass = $this->periodClass;
  540. $source = new DatePeriod(
  541. new DateTime('2012-07-01'),
  542. CarbonInterval::days(2),
  543. new DateTime('2012-07-07')
  544. );
  545. $period = $periodClass::instance($source);
  546. $this->assertInstanceOf($periodClass, $period);
  547. $this->assertSame('2012-07-01', $period->getStartDate()->format('Y-m-d'));
  548. $this->assertSame(2, $period->getDateInterval()->d);
  549. $this->assertSame('2012-07-07', $period->getEndDate()->format('Y-m-d'));
  550. $period2 = $periodClass::instance($period);
  551. $this->assertInstanceOf($periodClass, $period2);
  552. $this->assertSame('2012-07-01', $period2->getStartDate()->format('Y-m-d'));
  553. $this->assertSame(2, $period2->getDateInterval()->d);
  554. $this->assertSame('2012-07-07', $period2->getEndDate()->format('Y-m-d'));
  555. $this->assertNotSame($period, $period2);
  556. $period3 = new $periodClass($source);
  557. $this->assertInstanceOf($periodClass, $period3);
  558. $this->assertSame('2012-07-01', $period3->getStartDate()->format('Y-m-d'));
  559. $this->assertSame(2, $period3->getDateInterval()->d);
  560. $this->assertSame('2012-07-07', $period3->getEndDate()->format('Y-m-d'));
  561. $this->assertNotSame($period, $period3);
  562. $period4 = new $periodClass($period);
  563. $this->assertInstanceOf($periodClass, $period4);
  564. $this->assertSame('2012-07-01', $period4->getStartDate()->format('Y-m-d'));
  565. $this->assertSame(2, $period4->getDateInterval()->d);
  566. $this->assertSame('2012-07-07', $period4->getEndDate()->format('Y-m-d'));
  567. $this->assertNotSame($period, $period4);
  568. }
  569. /**
  570. * @requires PHP >= 8.0
  571. */
  572. public function testCreateFromCarbonInstancesNamedParameters()
  573. {
  574. $periodClass = $this->periodClass;
  575. $carbonClass = $periodClass === CarbonPeriodImmutable::class
  576. ? CarbonImmutable::class
  577. : Carbon::class;
  578. $period = eval("return \\$carbonClass::create('2019-01-02')->daysUntil(endDate: '2019-02-05');");
  579. $this->assertInstanceOf($periodClass, $period);
  580. $this->assertSame('2019-01-02', $period->getStartDate()->format('Y-m-d'));
  581. $this->assertSame('2019-02-05', $period->getEndDate()->format('Y-m-d'));
  582. $this->assertSame('1 day', (string) $period->getDateInterval());
  583. $period = eval("return \\$carbonClass::create('2019-01-02')->hoursUntil(endDate: '2019-02-05', factor: 12);");
  584. $this->assertInstanceOf($periodClass, $period);
  585. $this->assertSame('2019-01-02', $period->getStartDate()->format('Y-m-d'));
  586. $this->assertSame('2019-02-05', $period->getEndDate()->format('Y-m-d'));
  587. $this->assertSame('12 hours', (string) $period->getDateInterval());
  588. }
  589. public function testCast()
  590. {
  591. $periodClass = $this->periodClass;
  592. $period = $periodClass === CarbonPeriodImmutable::class
  593. ? (new class('2012-07-01', CarbonInterval::days(2), '2012-07-07') extends CarbonPeriodImmutable {
  594. public function foo()
  595. {
  596. return $this->getStartDate()->format('j').' '.
  597. $this->getDateInterval()->format('%d').' '.
  598. $this->getEndDate()->format('j');
  599. }
  600. })
  601. : (new class('2012-07-01', CarbonInterval::days(2), '2012-07-07') extends CarbonPeriod {
  602. public function foo()
  603. {
  604. return $this->getStartDate()->format('j').' '.
  605. $this->getDateInterval()->format('%d').' '.
  606. $this->getEndDate()->format('j');
  607. }
  608. });
  609. $subClass = \get_class($period);
  610. $this->assertInstanceOf($periodClass, $period);
  611. $this->assertNotSame($periodClass, $subClass);
  612. $this->assertSame('1 2 7', $period->foo());
  613. /** @var object $period */
  614. $period = $periodClass::create('2010-08-24', CarbonInterval::weeks(2), '2012-07-19')
  615. ->cast($subClass);
  616. $this->assertInstanceOf($subClass, $period);
  617. $this->assertSame('24 14 19', $period->foo());
  618. }
  619. public function testBadCast()
  620. {
  621. $this->expectExceptionObject(new InvalidArgumentException(
  622. 'DateTime has not the instance() method needed to cast the date.'
  623. ));
  624. $periodClass = $this->periodClass;
  625. $periodClass::create('2010-08-24', CarbonInterval::weeks(2), '2012-07-19')
  626. ->cast(DateTime::class);
  627. }
  628. public function testMake()
  629. {
  630. $periodClass = $this->periodClass;
  631. $period = $periodClass::make(new DatePeriod(
  632. new DateTime('2012-07-01'),
  633. CarbonInterval::days(2),
  634. new DateTime('2012-07-07')
  635. ));
  636. $this->assertInstanceOf($periodClass, $period);
  637. $this->assertSame('2012-07-01', $period->getStartDate()->format('Y-m-d'));
  638. $this->assertSame(2, $period->getDateInterval()->d);
  639. $this->assertSame('2012-07-07', $period->getEndDate()->format('Y-m-d'));
  640. $period2 = $periodClass::make($period);
  641. $this->assertInstanceOf($periodClass, $period2);
  642. $this->assertSame('2012-07-01', $period2->getStartDate()->format('Y-m-d'));
  643. $this->assertSame(2, $period2->getDateInterval()->d);
  644. $this->assertSame('2012-07-07', $period2->getEndDate()->format('Y-m-d'));
  645. $this->assertNotSame($period, $period2);
  646. $period2 = $periodClass::make('2012-07-01/P2D/2012-07-07');
  647. $this->assertInstanceOf($periodClass, $period2);
  648. $this->assertSame('2012-07-01', $period2->getStartDate()->format('Y-m-d'));
  649. $this->assertSame(2, $period2->getDateInterval()->d);
  650. $this->assertSame('2012-07-07', $period2->getEndDate()->format('Y-m-d'));
  651. }
  652. public function testInstanceInvalidType()
  653. {
  654. $this->expectExceptionObject(new NotAPeriodException(
  655. 'Argument 1 passed to Carbon\CarbonPeriod::Carbon\CarbonPeriod::instance() '.
  656. 'must be an instance of DatePeriod or Carbon\CarbonPeriod, string given.'
  657. ));
  658. $periodClass = $this->periodClass;
  659. $periodClass::instance('hello');
  660. }
  661. public function testInstanceInvalidInstance()
  662. {
  663. $this->expectExceptionObject(new NotAPeriodException(
  664. 'Argument 1 passed to Carbon\CarbonPeriod::Carbon\CarbonPeriod::instance() '.
  665. 'must be an instance of DatePeriod or Carbon\CarbonPeriod, instance of Carbon\Carbon given.'
  666. ));
  667. $periodClass = $this->periodClass;
  668. $periodClass::instance(Carbon::now());
  669. }
  670. public function testMutability()
  671. {
  672. $this->assertSame(
  673. [Carbon::class, Carbon::class, Carbon::class],
  674. iterator_to_array(
  675. CarbonPeriod::between(Carbon::today(), Carbon::today()->addDays(2))->map('get_class')
  676. )
  677. );
  678. $this->assertSame(
  679. [Carbon::class, Carbon::class, Carbon::class],
  680. iterator_to_array(
  681. CarbonPeriod::between(CarbonImmutable::today(), CarbonImmutable::today()->addDays(2))->map('get_class')
  682. )
  683. );
  684. $this->assertSame(
  685. [Carbon::class, Carbon::class, Carbon::class],
  686. iterator_to_array(
  687. CarbonPeriod::between('today', 'today + 2 days')->map('get_class')
  688. )
  689. );
  690. $this->assertSame(
  691. [CarbonImmutable::class, CarbonImmutable::class, CarbonImmutable::class],
  692. iterator_to_array(
  693. CarbonPeriodImmutable::between(Carbon::today(), Carbon::today()->addDays(2))->map('get_class')
  694. )
  695. );
  696. $this->assertSame(
  697. [CarbonImmutable::class, CarbonImmutable::class, CarbonImmutable::class],
  698. iterator_to_array(
  699. CarbonPeriodImmutable::between(CarbonImmutable::today(), CarbonImmutable::today()->addDays(2))->map('get_class')
  700. )
  701. );
  702. $this->assertSame(
  703. [CarbonImmutable::class, CarbonImmutable::class, CarbonImmutable::class],
  704. iterator_to_array(
  705. CarbonPeriodImmutable::between('today', 'today + 2 days')->map('get_class')
  706. )
  707. );
  708. }
  709. }