blob: ee00666c1e47b964e5a03779706833e160cc2c49 [file] [log] [blame]
Matthias Andreas Benkard7b2a3a12021-08-16 10:57:25 +02001<?php
2
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 */
11namespace Carbon\Traits;
12
13use BadMethodCallException;
14use Carbon\CarbonInterface;
15use Carbon\Exceptions\BadComparisonUnitException;
16use InvalidArgumentException;
17
18/**
19 * Trait Comparison.
20 *
21 * Comparison utils and testers. All the following methods return booleans.
22 * nowWithSameTz
23 *
24 * Depends on the following methods:
25 *
26 * @method static resolveCarbon($date)
27 * @method static copy()
28 * @method static nowWithSameTz()
29 * @method static static yesterday($timezone = null)
30 * @method static static tomorrow($timezone = null)
31 */
32trait Comparison
33{
34 /** @var bool */
35 protected $endOfTime = false;
36
37 /** @var bool */
38 protected $startOfTime = false;
39
40 /**
41 * Determines if the instance is equal to another
42 *
43 * @example
44 * ```
45 * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:16'); // true
46 * Carbon::parse('2018-07-25 12:45:16')->eq(Carbon::parse('2018-07-25 12:45:16')); // true
47 * Carbon::parse('2018-07-25 12:45:16')->eq('2018-07-25 12:45:17'); // false
48 * ```
49 *
50 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
51 *
52 * @see equalTo()
53 *
54 * @return bool
55 */
56 public function eq($date): bool
57 {
58 return $this->equalTo($date);
59 }
60
61 /**
62 * Determines if the instance is equal to another
63 *
64 * @example
65 * ```
66 * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:16'); // true
67 * Carbon::parse('2018-07-25 12:45:16')->equalTo(Carbon::parse('2018-07-25 12:45:16')); // true
68 * Carbon::parse('2018-07-25 12:45:16')->equalTo('2018-07-25 12:45:17'); // false
69 * ```
70 *
71 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
72 *
73 * @return bool
74 */
75 public function equalTo($date): bool
76 {
77 return $this == $date;
78 }
79
80 /**
81 * Determines if the instance is not equal to another
82 *
83 * @example
84 * ```
85 * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:16'); // false
86 * Carbon::parse('2018-07-25 12:45:16')->ne(Carbon::parse('2018-07-25 12:45:16')); // false
87 * Carbon::parse('2018-07-25 12:45:16')->ne('2018-07-25 12:45:17'); // true
88 * ```
89 *
90 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
91 *
92 * @see notEqualTo()
93 *
94 * @return bool
95 */
96 public function ne($date): bool
97 {
98 return $this->notEqualTo($date);
99 }
100
101 /**
102 * Determines if the instance is not equal to another
103 *
104 * @example
105 * ```
106 * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:16'); // false
107 * Carbon::parse('2018-07-25 12:45:16')->notEqualTo(Carbon::parse('2018-07-25 12:45:16')); // false
108 * Carbon::parse('2018-07-25 12:45:16')->notEqualTo('2018-07-25 12:45:17'); // true
109 * ```
110 *
111 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
112 *
113 * @return bool
114 */
115 public function notEqualTo($date): bool
116 {
117 return !$this->equalTo($date);
118 }
119
120 /**
121 * Determines if the instance is greater (after) than another
122 *
123 * @example
124 * ```
125 * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:15'); // true
126 * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:16'); // false
127 * Carbon::parse('2018-07-25 12:45:16')->gt('2018-07-25 12:45:17'); // false
128 * ```
129 *
130 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
131 *
132 * @see greaterThan()
133 *
134 * @return bool
135 */
136 public function gt($date): bool
137 {
138 return $this->greaterThan($date);
139 }
140
141 /**
142 * Determines if the instance is greater (after) than another
143 *
144 * @example
145 * ```
146 * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:15'); // true
147 * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:16'); // false
148 * Carbon::parse('2018-07-25 12:45:16')->greaterThan('2018-07-25 12:45:17'); // false
149 * ```
150 *
151 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
152 *
153 * @return bool
154 */
155 public function greaterThan($date): bool
156 {
157 return $this > $date;
158 }
159
160 /**
161 * Determines if the instance is greater (after) than another
162 *
163 * @example
164 * ```
165 * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:15'); // true
166 * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:16'); // false
167 * Carbon::parse('2018-07-25 12:45:16')->isAfter('2018-07-25 12:45:17'); // false
168 * ```
169 *
170 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
171 *
172 * @see greaterThan()
173 *
174 * @return bool
175 */
176 public function isAfter($date): bool
177 {
178 return $this->greaterThan($date);
179 }
180
181 /**
182 * Determines if the instance is greater (after) than or equal to another
183 *
184 * @example
185 * ```
186 * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:15'); // true
187 * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:16'); // true
188 * Carbon::parse('2018-07-25 12:45:16')->gte('2018-07-25 12:45:17'); // false
189 * ```
190 *
191 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
192 *
193 * @see greaterThanOrEqualTo()
194 *
195 * @return bool
196 */
197 public function gte($date): bool
198 {
199 return $this->greaterThanOrEqualTo($date);
200 }
201
202 /**
203 * Determines if the instance is greater (after) than or equal to another
204 *
205 * @example
206 * ```
207 * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:15'); // true
208 * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:16'); // true
209 * Carbon::parse('2018-07-25 12:45:16')->greaterThanOrEqualTo('2018-07-25 12:45:17'); // false
210 * ```
211 *
212 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
213 *
214 * @return bool
215 */
216 public function greaterThanOrEqualTo($date): bool
217 {
218 return $this >= $date;
219 }
220
221 /**
222 * Determines if the instance is less (before) than another
223 *
224 * @example
225 * ```
226 * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:15'); // false
227 * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:16'); // false
228 * Carbon::parse('2018-07-25 12:45:16')->lt('2018-07-25 12:45:17'); // true
229 * ```
230 *
231 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
232 *
233 * @see lessThan()
234 *
235 * @return bool
236 */
237 public function lt($date): bool
238 {
239 return $this->lessThan($date);
240 }
241
242 /**
243 * Determines if the instance is less (before) than another
244 *
245 * @example
246 * ```
247 * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:15'); // false
248 * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:16'); // false
249 * Carbon::parse('2018-07-25 12:45:16')->lessThan('2018-07-25 12:45:17'); // true
250 * ```
251 *
252 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
253 *
254 * @return bool
255 */
256 public function lessThan($date): bool
257 {
258 return $this < $date;
259 }
260
261 /**
262 * Determines if the instance is less (before) than another
263 *
264 * @example
265 * ```
266 * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:15'); // false
267 * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:16'); // false
268 * Carbon::parse('2018-07-25 12:45:16')->isBefore('2018-07-25 12:45:17'); // true
269 * ```
270 *
271 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
272 *
273 * @see lessThan()
274 *
275 * @return bool
276 */
277 public function isBefore($date): bool
278 {
279 return $this->lessThan($date);
280 }
281
282 /**
283 * Determines if the instance is less (before) or equal to another
284 *
285 * @example
286 * ```
287 * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:15'); // false
288 * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:16'); // true
289 * Carbon::parse('2018-07-25 12:45:16')->lte('2018-07-25 12:45:17'); // true
290 * ```
291 *
292 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
293 *
294 * @see lessThanOrEqualTo()
295 *
296 * @return bool
297 */
298 public function lte($date): bool
299 {
300 return $this->lessThanOrEqualTo($date);
301 }
302
303 /**
304 * Determines if the instance is less (before) or equal to another
305 *
306 * @example
307 * ```
308 * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:15'); // false
309 * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:16'); // true
310 * Carbon::parse('2018-07-25 12:45:16')->lessThanOrEqualTo('2018-07-25 12:45:17'); // true
311 * ```
312 *
313 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date
314 *
315 * @return bool
316 */
317 public function lessThanOrEqualTo($date): bool
318 {
319 return $this <= $date;
320 }
321
322 /**
323 * Determines if the instance is between two others.
324 *
325 * The third argument allow you to specify if bounds are included or not (true by default)
326 * but for when you including/excluding bounds may produce different results in your application,
327 * we recommend to use the explicit methods ->betweenIncluded() or ->betweenExcluded() instead.
328 *
329 * @example
330 * ```
331 * Carbon::parse('2018-07-25')->between('2018-07-14', '2018-08-01'); // true
332 * Carbon::parse('2018-07-25')->between('2018-08-01', '2018-08-20'); // false
333 * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01'); // true
334 * Carbon::parse('2018-07-25')->between('2018-07-25', '2018-08-01', false); // false
335 * ```
336 *
337 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
338 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
339 * @param bool $equal Indicates if an equal to comparison should be done
340 *
341 * @return bool
342 */
343 public function between($date1, $date2, $equal = true): bool
344 {
345 $date1 = $this->resolveCarbon($date1);
346 $date2 = $this->resolveCarbon($date2);
347
348 if ($date1->greaterThan($date2)) {
349 [$date1, $date2] = [$date2, $date1];
350 }
351
352 if ($equal) {
353 return $this->greaterThanOrEqualTo($date1) && $this->lessThanOrEqualTo($date2);
354 }
355
356 return $this->greaterThan($date1) && $this->lessThan($date2);
357 }
358
359 /**
360 * Determines if the instance is between two others, bounds included.
361 *
362 * @example
363 * ```
364 * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-14', '2018-08-01'); // true
365 * Carbon::parse('2018-07-25')->betweenIncluded('2018-08-01', '2018-08-20'); // false
366 * Carbon::parse('2018-07-25')->betweenIncluded('2018-07-25', '2018-08-01'); // true
367 * ```
368 *
369 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
370 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
371 *
372 * @return bool
373 */
374 public function betweenIncluded($date1, $date2): bool
375 {
376 return $this->between($date1, $date2, true);
377 }
378
379 /**
380 * Determines if the instance is between two others, bounds excluded.
381 *
382 * @example
383 * ```
384 * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-14', '2018-08-01'); // true
385 * Carbon::parse('2018-07-25')->betweenExcluded('2018-08-01', '2018-08-20'); // false
386 * Carbon::parse('2018-07-25')->betweenExcluded('2018-07-25', '2018-08-01'); // false
387 * ```
388 *
389 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
390 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
391 *
392 * @return bool
393 */
394 public function betweenExcluded($date1, $date2): bool
395 {
396 return $this->between($date1, $date2, false);
397 }
398
399 /**
400 * Determines if the instance is between two others
401 *
402 * @example
403 * ```
404 * Carbon::parse('2018-07-25')->isBetween('2018-07-14', '2018-08-01'); // true
405 * Carbon::parse('2018-07-25')->isBetween('2018-08-01', '2018-08-20'); // false
406 * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01'); // true
407 * Carbon::parse('2018-07-25')->isBetween('2018-07-25', '2018-08-01', false); // false
408 * ```
409 *
410 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date1
411 * @param \Carbon\Carbon|\DateTimeInterface|mixed $date2
412 * @param bool $equal Indicates if an equal to comparison should be done
413 *
414 * @return bool
415 */
416 public function isBetween($date1, $date2, $equal = true): bool
417 {
418 return $this->between($date1, $date2, $equal);
419 }
420
421 /**
422 * Determines if the instance is a weekday.
423 *
424 * @example
425 * ```
426 * Carbon::parse('2019-07-14')->isWeekday(); // false
427 * Carbon::parse('2019-07-15')->isWeekday(); // true
428 * ```
429 *
430 * @return bool
431 */
432 public function isWeekday()
433 {
434 return !$this->isWeekend();
435 }
436
437 /**
438 * Determines if the instance is a weekend day.
439 *
440 * @example
441 * ```
442 * Carbon::parse('2019-07-14')->isWeekend(); // true
443 * Carbon::parse('2019-07-15')->isWeekend(); // false
444 * ```
445 *
446 * @return bool
447 */
448 public function isWeekend()
449 {
450 return \in_array($this->dayOfWeek, static::$weekendDays);
451 }
452
453 /**
454 * Determines if the instance is yesterday.
455 *
456 * @example
457 * ```
458 * Carbon::yesterday()->isYesterday(); // true
459 * Carbon::tomorrow()->isYesterday(); // false
460 * ```
461 *
462 * @return bool
463 */
464 public function isYesterday()
465 {
466 return $this->toDateString() === static::yesterday($this->getTimezone())->toDateString();
467 }
468
469 /**
470 * Determines if the instance is today.
471 *
472 * @example
473 * ```
474 * Carbon::today()->isToday(); // true
475 * Carbon::tomorrow()->isToday(); // false
476 * ```
477 *
478 * @return bool
479 */
480 public function isToday()
481 {
482 return $this->toDateString() === $this->nowWithSameTz()->toDateString();
483 }
484
485 /**
486 * Determines if the instance is tomorrow.
487 *
488 * @example
489 * ```
490 * Carbon::tomorrow()->isTomorrow(); // true
491 * Carbon::yesterday()->isTomorrow(); // false
492 * ```
493 *
494 * @return bool
495 */
496 public function isTomorrow()
497 {
498 return $this->toDateString() === static::tomorrow($this->getTimezone())->toDateString();
499 }
500
501 /**
502 * Determines if the instance is in the future, ie. greater (after) than now.
503 *
504 * @example
505 * ```
506 * Carbon::now()->addHours(5)->isFuture(); // true
507 * Carbon::now()->subHours(5)->isFuture(); // false
508 * ```
509 *
510 * @return bool
511 */
512 public function isFuture()
513 {
514 return $this->greaterThan($this->nowWithSameTz());
515 }
516
517 /**
518 * Determines if the instance is in the past, ie. less (before) than now.
519 *
520 * @example
521 * ```
522 * Carbon::now()->subHours(5)->isPast(); // true
523 * Carbon::now()->addHours(5)->isPast(); // false
524 * ```
525 *
526 * @return bool
527 */
528 public function isPast()
529 {
530 return $this->lessThan($this->nowWithSameTz());
531 }
532
533 /**
534 * Determines if the instance is a leap year.
535 *
536 * @example
537 * ```
538 * Carbon::parse('2020-01-01')->isLeapYear(); // true
539 * Carbon::parse('2019-01-01')->isLeapYear(); // false
540 * ```
541 *
542 * @return bool
543 */
544 public function isLeapYear()
545 {
546 return $this->rawFormat('L') === '1';
547 }
548
549 /**
550 * Determines if the instance is a long year
551 *
552 * @example
553 * ```
554 * Carbon::parse('2015-01-01')->isLongYear(); // true
555 * Carbon::parse('2016-01-01')->isLongYear(); // false
556 * ```
557 *
558 * @see https://en.wikipedia.org/wiki/ISO_8601#Week_dates
559 *
560 * @return bool
561 */
562 public function isLongYear()
563 {
564 return static::create($this->year, 12, 28, 0, 0, 0, $this->tz)->weekOfYear === 53;
565 }
566
567 /**
568 * Compares the formatted values of the two dates.
569 *
570 * @example
571 * ```
572 * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-12-13')); // true
573 * Carbon::parse('2019-06-13')->isSameAs('Y-d', Carbon::parse('2019-06-14')); // false
574 * ```
575 *
576 * @param string $format date formats to compare.
577 * @param \Carbon\Carbon|\DateTimeInterface|string|null $date instance to compare with or null to use current day.
578 *
579 * @return bool
580 */
581 public function isSameAs($format, $date = null)
582 {
583 return $this->rawFormat($format) === $this->resolveCarbon($date)->rawFormat($format);
584 }
585
586 /**
587 * Determines if the instance is in the current unit given.
588 *
589 * @example
590 * ```
591 * Carbon::parse('2019-01-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // true
592 * Carbon::parse('2018-12-13')->isSameUnit('year', Carbon::parse('2019-12-25')); // false
593 * ```
594 *
595 * @param string $unit singular unit string
596 * @param \Carbon\Carbon|\DateTimeInterface|null $date instance to compare with or null to use current day.
597 *
598 * @throws BadComparisonUnitException
599 *
600 * @return bool
601 */
602 public function isSameUnit($unit, $date = null)
603 {
604 $units = [
605 // @call isSameUnit
606 'year' => 'Y',
607 // @call isSameUnit
608 'week' => 'o-W',
609 // @call isSameUnit
610 'day' => 'Y-m-d',
611 // @call isSameUnit
612 'hour' => 'Y-m-d H',
613 // @call isSameUnit
614 'minute' => 'Y-m-d H:i',
615 // @call isSameUnit
616 'second' => 'Y-m-d H:i:s',
617 // @call isSameUnit
618 'micro' => 'Y-m-d H:i:s.u',
619 // @call isSameUnit
620 'microsecond' => 'Y-m-d H:i:s.u',
621 ];
622
623 if (!isset($units[$unit])) {
624 if (isset($this->$unit)) {
625 return $this->resolveCarbon($date)->$unit === $this->$unit;
626 }
627
628 if ($this->localStrictModeEnabled ?? static::isStrictModeEnabled()) {
629 throw new BadComparisonUnitException($unit);
630 }
631
632 return false;
633 }
634
635 return $this->isSameAs($units[$unit], $date);
636 }
637
638 /**
639 * Determines if the instance is in the current unit given.
640 *
641 * @example
642 * ```
643 * Carbon::now()->isCurrentUnit('hour'); // true
644 * Carbon::now()->subHours(2)->isCurrentUnit('hour'); // false
645 * ```
646 *
647 * @param string $unit The unit to test.
648 *
649 * @throws BadMethodCallException
650 *
651 * @return bool
652 */
653 public function isCurrentUnit($unit)
654 {
655 return $this->{'isSame'.ucfirst($unit)}();
656 }
657
658 /**
659 * Checks if the passed in date is in the same quarter as the instance quarter (and year if needed).
660 *
661 * @example
662 * ```
663 * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2019-03-01')); // true
664 * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2019-04-01')); // false
665 * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01')); // false
666 * Carbon::parse('2019-01-12')->isSameQuarter(Carbon::parse('2018-03-01'), false); // true
667 * ```
668 *
669 * @param \Carbon\Carbon|\DateTimeInterface|string|null $date The instance to compare with or null to use current day.
670 * @param bool $ofSameYear Check if it is the same month in the same year.
671 *
672 * @return bool
673 */
674 public function isSameQuarter($date = null, $ofSameYear = true)
675 {
676 $date = $this->resolveCarbon($date);
677
678 return $this->quarter === $date->quarter && (!$ofSameYear || $this->isSameYear($date));
679 }
680
681 /**
682 * Checks if the passed in date is in the same month as the instance´s month.
683 *
684 * @example
685 * ```
686 * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2019-01-01')); // true
687 * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2019-02-01')); // false
688 * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01')); // false
689 * Carbon::parse('2019-01-12')->isSameMonth(Carbon::parse('2018-01-01'), false); // true
690 * ```
691 *
692 * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use the current date.
693 * @param bool $ofSameYear Check if it is the same month in the same year.
694 *
695 * @return bool
696 */
697 public function isSameMonth($date = null, $ofSameYear = true)
698 {
699 return $this->isSameAs($ofSameYear ? 'Y-m' : 'm', $date);
700 }
701
702 /**
703 * Checks if this day is a specific day of the week.
704 *
705 * @example
706 * ```
707 * Carbon::parse('2019-07-17')->isDayOfWeek(Carbon::WEDNESDAY); // true
708 * Carbon::parse('2019-07-17')->isDayOfWeek(Carbon::FRIDAY); // false
709 * Carbon::parse('2019-07-17')->isDayOfWeek('Wednesday'); // true
710 * Carbon::parse('2019-07-17')->isDayOfWeek('Friday'); // false
711 * ```
712 *
713 * @param int $dayOfWeek
714 *
715 * @return bool
716 */
717 public function isDayOfWeek($dayOfWeek)
718 {
719 if (\is_string($dayOfWeek) && \defined($constant = static::class.'::'.strtoupper($dayOfWeek))) {
720 $dayOfWeek = \constant($constant);
721 }
722
723 return $this->dayOfWeek === $dayOfWeek;
724 }
725
726 /**
727 * Check if its the birthday. Compares the date/month values of the two dates.
728 *
729 * @example
730 * ```
731 * Carbon::now()->subYears(5)->isBirthday(); // true
732 * Carbon::now()->subYears(5)->subDay()->isBirthday(); // false
733 * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-05')); // true
734 * Carbon::parse('2019-06-05')->isBirthday(Carbon::parse('2001-06-06')); // false
735 * ```
736 *
737 * @param \Carbon\Carbon|\DateTimeInterface|null $date The instance to compare with or null to use current day.
738 *
739 * @return bool
740 */
741 public function isBirthday($date = null)
742 {
743 return $this->isSameAs('md', $date);
744 }
745
746 /**
747 * Check if today is the last day of the Month
748 *
749 * @example
750 * ```
751 * Carbon::parse('2019-02-28')->isLastOfMonth(); // true
752 * Carbon::parse('2019-03-28')->isLastOfMonth(); // false
753 * Carbon::parse('2019-03-30')->isLastOfMonth(); // false
754 * Carbon::parse('2019-03-31')->isLastOfMonth(); // true
755 * Carbon::parse('2019-04-30')->isLastOfMonth(); // true
756 * ```
757 *
758 * @return bool
759 */
760 public function isLastOfMonth()
761 {
762 return $this->day === $this->daysInMonth;
763 }
764
765 /**
766 * Check if the instance is start of day / midnight.
767 *
768 * @example
769 * ```
770 * Carbon::parse('2019-02-28 00:00:00')->isStartOfDay(); // true
771 * Carbon::parse('2019-02-28 00:00:00.999999')->isStartOfDay(); // true
772 * Carbon::parse('2019-02-28 00:00:01')->isStartOfDay(); // false
773 * Carbon::parse('2019-02-28 00:00:00.000000')->isStartOfDay(true); // true
774 * Carbon::parse('2019-02-28 00:00:00.000012')->isStartOfDay(true); // false
775 * ```
776 *
777 * @param bool $checkMicroseconds check time at microseconds precision
778 *
779 * @return bool
780 */
781 public function isStartOfDay($checkMicroseconds = false)
782 {
783 /* @var CarbonInterface $this */
784 return $checkMicroseconds
785 ? $this->rawFormat('H:i:s.u') === '00:00:00.000000'
786 : $this->rawFormat('H:i:s') === '00:00:00';
787 }
788
789 /**
790 * Check if the instance is end of day.
791 *
792 * @example
793 * ```
794 * Carbon::parse('2019-02-28 23:59:59.999999')->isEndOfDay(); // true
795 * Carbon::parse('2019-02-28 23:59:59.123456')->isEndOfDay(); // true
796 * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(); // true
797 * Carbon::parse('2019-02-28 23:59:58.999999')->isEndOfDay(); // false
798 * Carbon::parse('2019-02-28 23:59:59.999999')->isEndOfDay(true); // true
799 * Carbon::parse('2019-02-28 23:59:59.123456')->isEndOfDay(true); // false
800 * Carbon::parse('2019-02-28 23:59:59')->isEndOfDay(true); // false
801 * ```
802 *
803 * @param bool $checkMicroseconds check time at microseconds precision
804 *
805 * @return bool
806 */
807 public function isEndOfDay($checkMicroseconds = false)
808 {
809 /* @var CarbonInterface $this */
810 return $checkMicroseconds
811 ? $this->rawFormat('H:i:s.u') === '23:59:59.999999'
812 : $this->rawFormat('H:i:s') === '23:59:59';
813 }
814
815 /**
816 * Check if the instance is start of day / midnight.
817 *
818 * @example
819 * ```
820 * Carbon::parse('2019-02-28 00:00:00')->isMidnight(); // true
821 * Carbon::parse('2019-02-28 00:00:00.999999')->isMidnight(); // true
822 * Carbon::parse('2019-02-28 00:00:01')->isMidnight(); // false
823 * ```
824 *
825 * @return bool
826 */
827 public function isMidnight()
828 {
829 return $this->isStartOfDay();
830 }
831
832 /**
833 * Check if the instance is midday.
834 *
835 * @example
836 * ```
837 * Carbon::parse('2019-02-28 11:59:59.999999')->isMidday(); // false
838 * Carbon::parse('2019-02-28 12:00:00')->isMidday(); // true
839 * Carbon::parse('2019-02-28 12:00:00.999999')->isMidday(); // true
840 * Carbon::parse('2019-02-28 12:00:01')->isMidday(); // false
841 * ```
842 *
843 * @return bool
844 */
845 public function isMidday()
846 {
847 /* @var CarbonInterface $this */
848 return $this->rawFormat('G:i:s') === static::$midDayAt.':00:00';
849 }
850
851 /**
852 * Checks if the (date)time string is in a given format.
853 *
854 * @example
855 * ```
856 * Carbon::hasFormat('11:12:45', 'h:i:s'); // true
857 * Carbon::hasFormat('13:12:45', 'h:i:s'); // false
858 * ```
859 *
860 * @param string $date
861 * @param string $format
862 *
863 * @return bool
864 */
865 public static function hasFormat($date, $format)
866 {
867 // createFromFormat() is known to handle edge cases silently.
868 // E.g. "1975-5-1" (Y-n-j) will still be parsed correctly when "Y-m-d" is supplied as the format.
869 // To ensure we're really testing against our desired format, perform an additional regex validation.
870
871 return self::matchFormatPattern((string) $date, preg_quote((string) $format, '/'), static::$regexFormats);
872 }
873
874 /**
875 * Checks if the (date)time string is in a given format.
876 *
877 * @example
878 * ```
879 * Carbon::hasFormatWithModifiers('31/08/2015', 'd#m#Y'); // true
880 * Carbon::hasFormatWithModifiers('31/08/2015', 'm#d#Y'); // false
881 * ```
882 *
883 * @param string $date
884 * @param string $format
885 *
886 * @return bool
887 */
888 public static function hasFormatWithModifiers($date, $format): bool
889 {
890 return self::matchFormatPattern((string) $date, (string) $format, array_merge(static::$regexFormats, static::$regexFormatModifiers));
891 }
892
893 /**
894 * Checks if the (date)time string is in a given format and valid to create a
895 * new instance.
896 *
897 * @example
898 * ```
899 * Carbon::canBeCreatedFromFormat('11:12:45', 'h:i:s'); // true
900 * Carbon::canBeCreatedFromFormat('13:12:45', 'h:i:s'); // false
901 * ```
902 *
903 * @param string $date
904 * @param string $format
905 *
906 * @return bool
907 */
908 public static function canBeCreatedFromFormat($date, $format)
909 {
910 try {
911 // Try to create a DateTime object. Throws an InvalidArgumentException if the provided time string
912 // doesn't match the format in any way.
913 if (!static::rawCreateFromFormat($format, $date)) {
914 return false;
915 }
916 } catch (InvalidArgumentException $e) {
917 return false;
918 }
919
920 return static::hasFormatWithModifiers($date, $format);
921 }
922
923 /**
924 * Returns true if the current date matches the given string.
925 *
926 * @example
927 * ```
928 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019')); // true
929 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2018')); // false
930 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019-06')); // true
931 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('06-02')); // true
932 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('2019-06-02')); // true
933 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('Sunday')); // true
934 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('June')); // true
935 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23')); // true
936 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23:45')); // true
937 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12:23:00')); // false
938 * var_dump(Carbon::parse('2019-06-02 12:23:45')->is('12h')); // true
939 * var_dump(Carbon::parse('2019-06-02 15:23:45')->is('3pm')); // true
940 * var_dump(Carbon::parse('2019-06-02 15:23:45')->is('3am')); // false
941 * ```
942 *
943 * @param string $tester day name, month name, hour, date, etc. as string
944 *
945 * @return bool
946 */
947 public function is(string $tester)
948 {
949 $tester = trim($tester);
950
951 if (preg_match('/^\d+$/', $tester)) {
952 return $this->year === (int) $tester;
953 }
954
955 if (preg_match('/^\d{3,}-\d{1,2}$/', $tester)) {
956 return $this->isSameMonth(static::parse($tester));
957 }
958
959 if (preg_match('/^\d{1,2}-\d{1,2}$/', $tester)) {
960 return $this->isSameDay(static::parse($this->year.'-'.$tester));
961 }
962
963 $modifier = preg_replace('/(\d)h$/i', '$1:00', $tester);
964
965 /* @var CarbonInterface $max */
966 $median = static::parse('5555-06-15 12:30:30.555555')->modify($modifier);
967 $current = $this->avoidMutation();
968 /* @var CarbonInterface $other */
969 $other = $this->avoidMutation()->modify($modifier);
970
971 if ($current->eq($other)) {
972 return true;
973 }
974
975 if (preg_match('/\d:\d{1,2}:\d{1,2}$/', $tester)) {
976 return $current->startOfSecond()->eq($other);
977 }
978
979 if (preg_match('/\d:\d{1,2}$/', $tester)) {
980 return $current->startOfMinute()->eq($other);
981 }
982
983 if (preg_match('/\d(h|am|pm)$/', $tester)) {
984 return $current->startOfHour()->eq($other);
985 }
986
987 if (preg_match(
988 '/^(january|february|march|april|may|june|july|august|september|october|november|december)\s+\d+$/i',
989 $tester
990 )) {
991 return $current->startOfMonth()->eq($other->startOfMonth());
992 }
993
994 $units = [
995 'month' => [1, 'year'],
996 'day' => [1, 'month'],
997 'hour' => [0, 'day'],
998 'minute' => [0, 'hour'],
999 'second' => [0, 'minute'],
1000 'microsecond' => [0, 'second'],
1001 ];
1002
1003 foreach ($units as $unit => [$minimum, $startUnit]) {
1004 if ($minimum === $median->$unit) {
1005 $current = $current->startOf($startUnit);
1006
1007 break;
1008 }
1009 }
1010
1011 return $current->eq($other);
1012 }
1013
1014 /**
1015 * Checks if the (date)time string is in a given format with
1016 * given list of pattern replacements.
1017 *
1018 * @example
1019 * ```
1020 * Carbon::hasFormat('11:12:45', 'h:i:s'); // true
1021 * Carbon::hasFormat('13:12:45', 'h:i:s'); // false
1022 * ```
1023 *
1024 * @param string $date
1025 * @param string $format
1026 * @param array $replacements
1027 *
1028 * @return bool
1029 */
1030 private static function matchFormatPattern(string $date, string $format, array $replacements): bool
1031 {
1032 // Preg quote, but remove escaped backslashes since we'll deal with escaped characters in the format string.
1033 $regex = str_replace('\\\\', '\\', $format);
1034 // Replace not-escaped letters
1035 $regex = preg_replace_callback(
1036 '/(?<!\\\\)((?:\\\\{2})*)(['.implode('', array_keys($replacements)).'])/',
1037 function ($match) use ($replacements) {
1038 return $match[1].strtr($match[2], $replacements);
1039 },
1040 $regex
1041 );
1042 // Replace escaped letters by the letter itself
1043 $regex = preg_replace('/(?<!\\\\)((?:\\\\{2})*)\\\\(\w)/', '$1$2', $regex);
1044 // Escape not escaped slashes
1045 $regex = preg_replace('#(?<!\\\\)((?:\\\\{2})*)/#', '$1\\/', $regex);
1046
1047 return (bool) @preg_match('/^'.$regex.'$/', $date);
1048 }
1049
1050 /**
1051 * Returns true if the date was created using CarbonImmutable::startOfTime()
1052 *
1053 * @return bool
1054 */
1055 public function isStartOfTime(): bool
1056 {
1057 return $this->startOfTime ?? false;
1058 }
1059
1060 /**
1061 * Returns true if the date was created using CarbonImmutable::endOfTime()
1062 *
1063 * @return bool
1064 */
1065 public function isEndOfTime(): bool
1066 {
1067 return $this->endOfTime ?? false;
1068 }
1069}