Completed
Push — master ( 173749...bef969 )
by ignace nyamagana
14:41
created

Period::toArray()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 4
Code Lines 2

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
c 1
b 0
f 0
dl 0
loc 4
rs 10
cc 1
eloc 2
nc 1
nop 0
1
<?php
2
/**
3
 * League.Period (http://period.thephpleague.com)
4
 *
5
 * @package   League.period
6
 * @author    Ignace Nyamagana Butera <[email protected]>
7
 * @copyright 2014-2015 Ignace Nyamagana Butera
8
 * @license   https://github.com/thephpleague/period/blob/master/LICENSE (MIT License)
9
 * @version   3.1.0
10
 * @link      https://github.com/thephpleague/period/
11
 */
12
namespace League\Period;
13
14
use DateInterval;
15
use DatePeriod;
16
use DateTime;
17
use DateTimeImmutable;
18
use DateTimeInterface;
19
use DateTimeZone;
20
use Generator;
21
use InvalidArgumentException;
22
use JsonSerializable;
23
use LogicException;
24
use OutOfRangeException;
25
26
/**
27
 * A immutable value object class to manipulate Time Range.
28
 *
29
 * @package League.period
30
 * @author  Ignace Nyamagana Butera <[email protected]>
31
 * @since   1.0.0
32
 */
33
class Period implements JsonSerializable
34
{
35
    /**
36
     * DateTime Format to create ISO8601 Interval format
37
     */
38
    const DATE_ISO8601 = 'Y-m-d\TH:i:s\Z';
39
40
    /**
41
     * Date Format for timezoneless DateTimeInterface
42
     */
43
    const DATE_LOCALE = 'Y-m-d H:i:s';
44
45
    /**
46
     * Period starting included date point.
47
     *
48
     * @var DateTimeImmutable
49
     */
50
    protected $startDate;
51
52
    /**
53
     * Period ending excluded date point.
54
     *
55
     * @var DateTimeImmutable
56
     */
57
    protected $endDate;
58
59
    /**
60
     * Create a new instance.
61
     *
62
     * @param DateTimeInterface|string $startDate starting date point
63
     * @param DateTimeInterface|string $endDate   ending date point
64
     *
65
     * @throws LogicException If $startDate is greater than $endDate
66
     */
67
    public function __construct($startDate, $endDate)
68
    {
69
        $startDate = static::filterDatePoint($startDate);
70
        $endDate   = static::filterDatePoint($endDate);
71
        if ($startDate > $endDate) {
72
            throw new LogicException('the ending endpoint must be greater or equal to the starting endpoint');
73
        }
74
        $this->startDate = $startDate;
75
        $this->endDate   = $endDate;
76
    }
77
78
    /**
79
     * Validate a DateTime.
80
     *
81
     * @param DateTimeInterface|string $datetime
82
     *
83
     * @return DateTimeImmutable
84
     */
85
    protected static function filterDatePoint($datetime)
86
    {
87
        if ($datetime instanceof DateTimeImmutable) {
88
            return $datetime;
89
        }
90
91
        if ($datetime instanceof DateTime) {
92
            return static::convertDateTime($datetime);
93
        }
94
95
        return new DateTimeImmutable($datetime);
96
    }
97
98
    /**
99
     * Convert a DateTime object into a DateTimeImmutable object
100
     *
101
     * @param DateTime $datetime
102
     *
103
     * @return DateTimeImmutable
104
     */
105
    protected static function convertDateTime(DateTime $datetime)
106
    {
107
        static $useFromMutable;
108
109
        if (null === $useFromMutable) {
110
            $useFromMutable = method_exists(new DateTimeImmutable(), 'createFromMutable');
111
        }
112
113
        if ($useFromMutable) {
114
            return DateTimeImmutable::createFromMutable($datetime);
115
        }
116
117
        return new DateTimeImmutable($datetime->format(static::DATE_LOCALE), $datetime->getTimeZone());
118
    }
119
120
    /**
121
     * Create a Period object for a specific day
122
     *
123
     * The date is truncated so that the Time range starts at midnight according to the date timezone.
124
     * The duration is equivalent to one full day.
125
     *
126
     * @param DateTimeInterface|string $day
127
     *
128
     * @return static
129
     */
130
    public static function createFromDay($day)
131
    {
132
        $startDate = static::filterDatePoint($day)->setTime(0, 0, 0);
133
134
        return new static($startDate, $startDate->add(new DateInterval('P1D')));
135
    }
136
137
    /**
138
     * Create a Period object from a starting point and an interval.
139
     *
140
     * The interval can be
141
     * <ul>
142
     * <li>a DateInterval object</li>
143
     * <li>an int interpreted as the duration expressed in seconds.</li>
144
     * <li>a string in a format supported by DateInterval::createFromDateString</li>
145
     * </ul>
146
     *
147
     * @param DateTimeInterface|string $startDate The start date point
148
     * @param mixed                    $interval  The interval
149
     *
150
     * @return static
151
     */
152
    public static function createFromDuration($startDate, $interval)
153
    {
154
        $startDate = static::filterDatePoint($startDate);
155
156
        return new static($startDate, $startDate->add(static::filterDateInterval($interval)));
157
    }
158
159
    /**
160
     * Validate a DateInterval.
161
     *
162
     * The interval can be
163
     * <ul>
164
     * <li>a DateInterval object</li>
165
     * <li>an int interpreted as the duration expressed in seconds.</li>
166
     * <li>a string in a format supported by DateInterval::createFromDateString</li>
167
     * </ul>
168
     *
169
     * @param mixed $interval The interval
170
     *
171
     * @return DateInterval
172
     */
173
    protected static function filterDateInterval($interval)
174
    {
175
        if ($interval instanceof DateInterval) {
176
            return $interval;
177
        }
178
179
        if (false !== ($res = filter_var($interval, FILTER_VALIDATE_INT))) {
180
            return new DateInterval('PT'.$res.'S');
181
        }
182
183
        return DateInterval::createFromDateString($interval);
184
    }
185
186
    /**
187
     * Create a Period object from a ending endpoint and an interval.
188
     *
189
     * The interval can be
190
     * <ul>
191
     * <li>a DateInterval object</li>
192
     * <li>an int interpreted as the duration expressed in seconds.</li>
193
     * <li>a string in a format supported by DateInterval::createFromDateString</li>
194
     * </ul>
195
     *
196
     * @param DateTimeInterface|string $endDate  The start date point
197
     * @param mixed                    $interval The interval
198
     *
199
     * @return static
200
     */
201
    public static function createFromDurationBeforeEnd($endDate, $interval)
202
    {
203
        $endDate = static::filterDatePoint($endDate);
204
205
        return new static($endDate->sub(static::filterDateInterval($interval)), $endDate);
206
    }
207
208
    /**
209
     * Create a Period object for a specific week
210
     *
211
     * @param int $year
212
     * @param int $week index from 1 to 53
213
     *
214
     * @return static
215
     */
216
    public static function createFromWeek($year, $week)
217
    {
218
        $week = static::validateYear($year).'W'.sprintf('%02d', static::validateRange($week, 1, 53));
219
        $startDate = new DateTimeImmutable($week);
220
221
        return new static($startDate, $startDate->add(new DateInterval('P1W')));
222
    }
223
224
    /**
225
     * Validate a year.
226
     *
227
     * @param int $year
228
     *
229
     * @throws InvalidArgumentException If year is not a valid int
230
     *
231
     * @return int
232
     */
233
    protected static function validateYear($year)
234
    {
235
        $year = filter_var($year, FILTER_VALIDATE_INT);
236
        if (false === $year) {
237
            throw new InvalidArgumentException('A Year must be a valid int');
238
        }
239
240
        return $year;
241
    }
242
243
    /**
244
     * Validate a int according to a range.
245
     *
246
     * @param int $value the value to validate
247
     * @param int $min   the minimum value
248
     * @param int $max   the maximal value
249
     *
250
     * @throws OutOfRangeException If the value is not in the range
251
     *
252
     * @return int
253
     */
254
    protected static function validateRange($value, $min, $max)
255
    {
256
        $res = filter_var($value, FILTER_VALIDATE_INT, ['options' => ['min_range' => $min, 'max_range' => $max]]);
257
        if (false === $res) {
258
            throw new OutOfRangeException('the submitted value is not contained within the valid range');
259
        }
260
261
        return $res;
262
    }
263
264
    /**
265
     * Create a Period object for a specific month
266
     *
267
     * @param int $year
268
     * @param int $month Month index from 1 to 12
269
     *
270
     * @return static
271
     */
272
    public static function createFromMonth($year, $month)
273
    {
274
        return static::createFromYearInterval(1, $year, $month);
275
    }
276
277
    /**
278
     * Create a Period object for a specific interval in a given year
279
     *
280
     * @param int $duration
281
     * @param int $year
282
     * @param int $index
283
     *
284
     * @return static
285
     */
286
    protected static function createFromYearInterval($duration, $year, $index)
287
    {
288
        $month = sprintf('%02s', ((static::validateRange($index, 1, 12 / $duration) - 1) * $duration) + 1);
289
        $startDate = new DateTimeImmutable(static::validateYear($year).'-'.$month.'-01');
290
291
        return new static($startDate, $startDate->add(new DateInterval('P'.$duration.'M')));
292
    }
293
294
    /**
295
     * Create a Period object for a specific quarter
296
     *
297
     * @param int $year
298
     * @param int $quarter Quarter Index from 1 to 4
299
     *
300
     * @return static
301
     */
302
    public static function createFromQuarter($year, $quarter)
303
    {
304
        return static::createFromYearInterval(3, $year, $quarter);
305
    }
306
307
    /**
308
     * Create a Period object for a specific semester
309
     *
310
     * @param int $year
311
     * @param int $semester Semester Index from 1 to 2
312
     *
313
     * @return static
314
     */
315
    public static function createFromSemester($year, $semester)
316
    {
317
        return static::createFromYearInterval(6, $year, $semester);
318
    }
319
320
    /**
321
     * Create a Period object for a specific Year
322
     *
323
     * @param int $year
324
     *
325
     * @return static
326
     */
327
    public static function createFromYear($year)
328
    {
329
        $startDate = new DateTimeImmutable(static::validateYear($year).'-01-01');
330
331
        return new static($startDate, $startDate->add(new DateInterval('P1Y')));
332
    }
333
334
    /**
335
     * String representation of a Period using ISO8601 Time interval format
336
     *
337
     * @return string
338
     */
339
    public function __toString()
340
    {
341
        $utc = new DateTimeZone('UTC');
342
343
        return $this->startDate->setTimeZone($utc)->format(static::DATE_ISO8601)
344
            .'/'.$this->endDate->setTimeZone($utc)->format(static::DATE_ISO8601);
345
    }
346
347
    /**
348
     * implement JsonSerializable interface
349
     *
350
     * @return array
351
     */
352
    public function jsonSerialize()
353
    {
354
        return [
355
            'startDate' => new DateTime($this->startDate->format(static::DATE_LOCALE), $this->startDate->getTimeZone()),
356
            'endDate' => new DateTime($this->endDate->format(static::DATE_LOCALE), $this->endDate->getTimeZone()),
357
        ];
358
    }
359
360
    /**
361
     * Returns the Array representation of a Period object
362
     *
363
     * @return array
364
     */
365
    public function toArray()
366
    {
367
        return ['startDate' => $this->startDate, 'endDate' => $this->endDate];
368
    }
369
370
    /**
371
     * Returns the starting date point.
372
     *
373
     * @return DateTimeImmutable
374
     */
375
    public function getStartDate()
376
    {
377
        return $this->startDate;
378
    }
379
380
    /**
381
     * Returns the ending endpoint.
382
     *
383
     * @return DateTimeImmutable
384
     */
385
    public function getEndDate()
386
    {
387
        return $this->endDate;
388
    }
389
390
    /**
391
     * Returns the Period duration as expressed in seconds
392
     *
393
     * @return int
394
     */
395
    public function getTimestampInterval()
396
    {
397
        return $this->endDate->getTimestamp() - $this->startDate->getTimestamp();
398
    }
399
400
    /**
401
     * Returns the Period duration as a DateInterval object.
402
     *
403
     * @return DateInterval
404
     */
405
    public function getDateInterval()
406
    {
407
        return $this->startDate->diff($this->endDate);
408
    }
409
410
    /**
411
     * Allows iteration over a set of dates and times,
412
     * recurring at regular intervals, over the Period object.
413
     *
414
     * The interval can be
415
     * <ul>
416
     * <li>a DateInterval object</li>
417
     * <li>an int interpreted as the duration expressed in seconds.</li>
418
     * <li>a string in a format supported by DateInterval::createFromDateString</li>
419
     * </ul>
420
     *
421
     * @param DateInterval|int|string $interval The interval
422
     *
423
     * @param int $option can be set to DatePeriod::EXCLUDE_START_DATE
424
     *                    to exclude the start date from the set of
425
     *                    recurring dates within the period.
426
     *
427
     * @return DatePeriod
428
     */
429
    public function getDatePeriod($interval, $option = 0)
430
    {
431
        return new DatePeriod($this->startDate, static::filterDateInterval($interval), $this->endDate, $option);
432
    }
433
434
    /**
435
     * Tells whether two Period share the same datepoints.
436
     *
437
     * @param Period $period
438
     *
439
     * @return bool
440
     */
441
    public function sameValueAs(Period $period)
442
    {
443
        return $this->startDate == $period->getStartDate() && $this->endDate == $period->getEndDate();
444
    }
445
446
    /**
447
     * Tells whether two Period object abuts
448
     *
449
     * @param Period $period
450
     *
451
     * @return bool
452
     */
453
    public function abuts(Period $period)
454
    {
455
        return $this->startDate == $period->getEndDate() || $this->endDate == $period->getStartDate();
456
    }
457
458
    /**
459
     * Tells whether two Period objects overlaps.
460
     *
461
     * @param Period $period
462
     *
463
     * @return bool
464
     */
465
    public function overlaps(Period $period)
466
    {
467
        if ($this->abuts($period)) {
468
            return false;
469
        }
470
471
        return $this->startDate < $period->getEndDate() && $this->endDate > $period->getStartDate();
472
    }
473
474
    /**
475
     * Tells whether a Period is entirely after the specified index
476
     *
477
     * @param Period|DateTimeInterface|string $index
478
     *
479
     * @return bool
480
     */
481
    public function isAfter($index)
482
    {
483
        if ($index instanceof Period) {
484
            return $this->startDate >= $index->getEndDate();
485
        }
486
487
        return $this->startDate > static::filterDatePoint($index);
488
    }
489
490
    /**
491
     * Tells whether a Period is entirely before the specified index
492
     *
493
     * @param Period|DateTimeInterface|string $index
494
     *
495
     * @return bool
496
     */
497
    public function isBefore($index)
498
    {
499
        if ($index instanceof Period) {
500
            return $this->endDate <= $index->getStartDate();
501
        }
502
503
        return $this->endDate <= static::filterDatePoint($index);
504
    }
505
506
    /**
507
     * Tells whether the specified index is fully contained within
508
     * the current Period object.
509
     *
510
     * @param Period|DateTimeInterface|string $index
511
     *
512
     * @return bool
513
     */
514
    public function contains($index)
515
    {
516
        if ($index instanceof Period) {
517
            return $this->containsPeriod($index);
518
        }
519
520
        $datetime = static::filterDatePoint($index);
521
522
        return $datetime >= $this->startDate && $datetime < $this->endDate;
523
    }
524
525
    /**
526
     * Tells whether a Period object is fully contained within
527
     * the current Period object.
528
     *
529
     * @param Period $period
530
     *
531
     * @return bool
532
     */
533
    protected function containsPeriod(Period $period)
534
    {
535
        $endDate = $period->getEndDate();
536
537
        return $this->contains($period->getStartDate())
538
            && ($endDate >= $this->startDate && $endDate <= $this->endDate);
539
    }
540
541
    /**
542
     * Compares two Period objects according to their duration.
543
     *
544
     * @param Period $period
545
     *
546
     * @return int
547
     */
548
    public function compareDuration(Period $period)
549
    {
550
        $datetime = $this->startDate->add($period->getDateInterval());
551
        if ($this->endDate > $datetime) {
552
            return 1;
553
        }
554
555
        if ($this->endDate < $datetime) {
556
            return -1;
557
        }
558
559
        return 0;
560
    }
561
562
    /**
563
     * Tells whether the current Period object duration
564
     * is greater than the submitted one.
565
     *
566
     * @param Period $period
567
     *
568
     * @return bool
569
     */
570
    public function durationGreaterThan(Period $period)
571
    {
572
        return 1 === $this->compareDuration($period);
573
    }
574
575
    /**
576
     * Tells whether the current Period object duration
577
     * is less than the submitted one.
578
     *
579
     * @param Period $period
580
     *
581
     * @return bool
582
     */
583
    public function durationLessThan(Period $period)
584
    {
585
        return -1 === $this->compareDuration($period);
586
    }
587
588
    /**
589
     * Tells whether the current Period object duration
590
     * is equal to the submitted one
591
     *
592
     * @param Period $period
593
     *
594
     * @return bool
595
     */
596
    public function sameDurationAs(Period $period)
597
    {
598
        return 0 === $this->compareDuration($period);
599
    }
600
601
    /**
602
     * Returns a new Period object with a new included starting date point.
603
     *
604
     * @param DateTimeInterface|string $startDate date point
605
     *
606
     * @return static
607
     */
608
    public function startingOn($startDate)
609
    {
610
        return new static(static::filterDatePoint($startDate), $this->endDate);
611
    }
612
613
    /**
614
     * Returns a new Period object with a new ending date point.
615
     *
616
     * @param DateTimeInterface|string $endDate date point
617
     *
618
     * @return static
619
     */
620
    public function endingOn($endDate)
621
    {
622
        return new static($this->startDate, static::filterDatePoint($endDate));
623
    }
624
625
    /**
626
     * Returns a new Period object with a new ending date point.
627
     *
628
     * The interval can be
629
     * <ul>
630
     * <li>a DateInterval object</li>
631
     * <li>an int interpreted as the duration expressed in seconds.</li>
632
     * <li>a string in a format supported by DateInterval::createFromDateString</li>
633
     * </ul>
634
     *
635
     * @param DateInterval|int|string $interval The interval
636
     *
637
     * @return static
638
     */
639
    public function withDuration($interval)
640
    {
641
        return new static($this->startDate, $this->startDate->add(static::filterDateInterval($interval)));
642
    }
643
644
    /**
645
     * Returns a new Period object with an added interval
646
     *
647
     * The interval can be
648
     * <ul>
649
     * <li>a DateInterval object</li>
650
     * <li>an int interpreted as the duration expressed in seconds.</li>
651
     * <li>a string in a format supported by DateInterval::createFromDateString</li>
652
     * </ul>
653
     *
654
     * @param DateInterval|int|string $interval The interval
655
     *
656
     * @return static
657
     */
658
    public function add($interval)
659
    {
660
        return new static($this->startDate, $this->endDate->add(static::filterDateInterval($interval)));
661
    }
662
663
    /**
664
     * Returns a new Period object with a Removed interval
665
     *
666
     * The interval can be
667
     * <ul>
668
     * <li>a DateInterval object</li>
669
     * <li>an int interpreted as the duration expressed in seconds.</li>
670
     * <li>a string in a format supported by DateInterval::createFromDateString</li>
671
     * </ul>
672
     *
673
     * @param DateInterval|int|string $interval The interval
674
     *
675
     * @return static
676
     */
677
    public function sub($interval)
678
    {
679
        return new static($this->startDate, $this->endDate->sub(static::filterDateInterval($interval)));
680
    }
681
682
    /**
683
     * Returns a new Period object adjacent to the current Period
684
     * and starting with its ending endpoint.
685
     * If no duration is provided the new Period will be created
686
     * using the current object duration
687
     *
688
     * The interval can be
689
     * <ul>
690
     * <li>a DateInterval object</li>
691
     * <li>an int interpreted as the duration expressed in seconds.</li>
692
     * <li>a string in a format supported by DateInterval::createFromDateString</li>
693
     * </ul>
694
     *
695
     * @param DateInterval|int|string $interval The interval
696
     *
697
     * @return static
698
     */
699
    public function next($interval = null)
700
    {
701
        if (is_null($interval)) {
702
            $interval = $this->getDateInterval();
703
        }
704
705
        return new static($this->endDate, $this->endDate->add(static::filterDateInterval($interval)));
706
    }
707
708
    /**
709
     * Returns a new Period object adjacent to the current Period
710
     * and ending with its starting endpoint.
711
     * If no duration is provided the new Period will have the
712
     * same duration as the current one
713
     *
714
     * The interval can be
715
     * <ul>
716
     * <li>a DateInterval object</li>
717
     * <li>an int interpreted as the duration expressed in seconds.</li>
718
     * <li>a string in a format supported by DateInterval::createFromDateString</li>
719
     * </ul>
720
     *
721
     * @param DateInterval|int|string $interval The interval
722
     *
723
     * @return static
724
     */
725
    public function previous($interval = null)
726
    {
727
        if (is_null($interval)) {
728
            $interval = $this->getDateInterval();
729
        }
730
731
        return new static($this->startDate->sub(static::filterDateInterval($interval)), $this->startDate);
732
    }
733
734
    /**
735
     * Merges one or more Period objects to return a new Period object.
736
     *
737
     * The resultant object represents the largest duration possible.
738
     *
739
     * @param Period ...$arg one or more Period objects
740
     *
741
     * @return static
742
     */
743
    public function merge(Period $arg)
0 ignored issues
show
Unused Code introduced by
The parameter $arg is not used and could be removed.

This check looks from parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
744
    {
745
        $reducer = function (Period $carry, Period $period) {
746
            if ($carry->getStartDate() > $period->getStartDate()) {
747
                $carry = $carry->startingOn($period->getStartDate());
748
            }
749
750
            if ($carry->getEndDate() < $period->getEndDate()) {
751
                $carry = $carry->endingOn($period->getEndDate());
752
            }
753
754
            return $carry;
755
        };
756
757
        return array_reduce(func_get_args(), $reducer, $this);
758
    }
759
760
    /**
761
     * Split a Period by a given interval
762
     *
763
     * The interval can be
764
     * <ul>
765
     * <li>a DateInterval object</li>
766
     * <li>an int interpreted as the duration expressed in seconds.</li>
767
     * <li>a string in a format supported by DateInterval::createFromDateString</li>
768
     * </ul>
769
     *
770
     * @param DateInterval|int|string $interval The interval
771
     *
772
     * @return Generator
773
     */
774
    public function split($interval)
775
    {
776
        $startDate = $this->startDate;
777
        $interval = static::filterDateInterval($interval);
778
        do {
779
            $endDate = $startDate->add($interval);
780
            if ($endDate > $this->endDate) {
781
                $endDate = $this->endDate;
782
            }
783
            yield new static($startDate, $endDate);
784
785
            $startDate = $endDate;
786
        } while ($startDate < $this->endDate);
787
    }
788
789
    /**
790
     * Computes the intersection between two Period objects.
791
     *
792
     * @param Period $period
793
     *
794
     * @throws LogicException If Both objects do not overlaps
795
     *
796
     * @return static
797
     */
798
    public function intersect(Period $period)
799
    {
800
        if ($this->abuts($period)) {
801
            throw new LogicException('Both object should not abuts');
802
        }
803
804
        return new static(
805
            ($period->getStartDate() > $this->startDate) ? $period->getStartDate() : $this->startDate,
806
            ($period->getEndDate() < $this->endDate) ? $period->getEndDate() : $this->endDate
807
        );
808
    }
809
810
    /**
811
     * Computes the gap between two Period objects.
812
     *
813
     * @param Period $period
814
     *
815
     * @return static
816
     */
817
    public function gap(Period $period)
818
    {
819
        if ($period->getStartDate() > $this->startDate) {
820
            return new static($this->endDate, $period->getStartDate());
821
        }
822
823
        return new static($period->getEndDate(), $this->startDate);
824
    }
825
826
    /**
827
     * Returns the difference between two Period objects expressed in seconds
828
     *
829
     * @param Period $period
830
     *
831
     * @return float
832
     */
833
    public function timestampIntervalDiff(Period $period)
834
    {
835
        return $this->getTimestampInterval() - $period->getTimestampInterval();
836
    }
837
838
    /**
839
     * Returns the difference between two Period objects expressed in \DateInterval
840
     *
841
     * @param Period $period
842
     *
843
     * @return DateInterval
844
     */
845
    public function dateIntervalDiff(Period $period)
846
    {
847
        return $this->endDate->diff($this->withDuration($period->getDateInterval())->endDate);
848
    }
849
850
    /**
851
     * Computes the difference between two overlapsing Period objects
852
     *
853
     * Returns an array containing the difference expressed as Period objects
854
     * The array will:
855
     *
856
     * <ul>
857
     * <li>be empty if both objects have the same datepoints</li>
858
     * <li>contain one Period object if both objects share one endpoint</li>
859
     * <li>contain two Period objects if both objects share no endpoint</li>
860
     * </ul>
861
     *
862
     * @param Period $period
863
     *
864
     * @throws LogicException if both object do not overlaps
865
     *
866
     * @return Period[]
867
     */
868
    public function diff(Period $period)
869
    {
870
        if (! $this->overlaps($period)) {
871
            throw new LogicException('Both Period objects should overlaps');
872
        }
873
874
        $res = [
875
            static::createFromDatepoints($this->startDate, $period->getStartDate()),
876
            static::createFromDatepoints($this->endDate, $period->getEndDate()),
877
        ];
878
879
        return array_values(array_filter($res, function (Period $period) {
880
            return $period->getStartDate() != $period->getEndDate();
881
        }));
882
    }
883
884
    /**
885
     * Create a new instance given two datepoints
886
     *
887
     * The datepoints will be used as to allow the creation of
888
     * a Period object
889
     *
890
     * @param DateTimeInterface|string $datePoint1 endpoint
891
     * @param DateTimeInterface|string $datePoint2 endpoint
892
     *
893
     * @return Period
894
     */
895
    protected static function createFromDatepoints($datePoint1, $datePoint2)
896
    {
897
        $startDate = static::filterDatePoint($datePoint1);
898
        $endDate   = static::filterDatePoint($datePoint2);
899
        if ($startDate > $endDate) {
900
            return new static($endDate, $startDate);
901
        }
902
903
        return new static($startDate, $endDate);
904
    }
905
}
906