Passed
Push — master ( 75ba92...aea3e4 )
by Dmitry
14:02
created

UsageInterval::hours()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 1
c 0
b 0
f 0
dl 0
loc 3
rs 10
cc 1
nc 1
nop 0
1
<?php
2
declare(strict_types=1);
3
4
namespace hiqdev\php\billing\action;
5
use DateInterval;
6
use DateTimeImmutable;
7
use InvalidArgumentException;
8
9
/** @readonly */
10
final class UsageInterval
11
{
12
    /** @readonly */
13
    private DateTimeImmutable $start;
14
    /** @readonly */
15
    private DateTimeImmutable $end;
16
    /** @readonly */
17
    private DateTimeImmutable $month;
18
19
    private function __construct(
20
        DateTimeImmutable $start,
21
        DateTimeImmutable $end
22
    ) {
23
        $this->start = $start;
24
        $this->end = $end;
25
        $this->month = self::toMonth($start);
26
    }
27
28
    private static function toMonth(DateTimeImmutable $date): DateTimeImmutable
29
    {
30
        return $date->modify('first day of this month midnight');
31
    }
32
33
    public static function wholeMonth(DateTimeImmutable $time): self
34
    {
35
        $start = self::toMonth($time);
36
37
        return new self(
38
            $start,
39
            $start->modify('+1 month'),
40
        );
41
    }
42
43
    /**
44
     * Calculates the usage interval for the given month for the given start and end sale dates.
45
     *
46
     * @param DateTimeImmutable $month the month to calculate the usage interval for
47
     * @param DateTimeImmutable $start the start date of the sale
48
     * @param DateTimeImmutable|null $end the end date of the sale or null if the sale is active
49
     * @return static
50
     */
51
    public static function withinMonth(
52
        DateTimeImmutable $month,
53
        DateTimeImmutable $start,
54
        ?DateTimeImmutable $end
55
    ): self {
56
        $month = self::toMonth($month);
57
        $nextMonth = $month->modify('+1 month');
58
59
        if ($end !== null && $start > $end) {
60
            throw new InvalidArgumentException('Start date must be less than end date');
61
        }
62
63
        if ($start >= $nextMonth) {
64
            $start = $month;
65
            $end = $month;
66
        }
67
68
        if ($end !== null && $end < $month) {
69
            $start = $month;
70
            $end = $month;
71
        }
72
73
        $effectiveSince = max($start, $month);
74
        $effectiveTill = min(
75
            $end ?? new DateTimeImmutable('2999-01-01'),
76
            $month->modify('+1 month')
77
        );
78
79
        return new self(
80
            $effectiveSince,
81
            $effectiveTill,
82
        );
83
    }
84
85
    public function start(): DateTimeImmutable
86
    {
87
        return $this->start;
88
    }
89
90
    public function end(): DateTimeImmutable
91
    {
92
        return $this->end;
93
    }
94
95
    public function dateTimeInterval(): DateInterval
96
    {
97
        return $this->start->diff($this->end);
98
    }
99
100
    public function seconds(): int
101
    {
102
        $interval = $this->dateTimeInterval();
103
104
        return $interval->s
105
                + $interval->i * 60
106
                + $interval->h * 3600
107
                + $interval->days * 86400;
108
    }
109
110
    public function minutes(): float
111
    {
112
        return $this->seconds() / 60;
113
    }
114
115
    public function hours(): float
116
    {
117
        return $this->seconds() / 60 / 60;
118
    }
119
120
    public function secondsInMonth(): int
121
    {
122
        return $this->month->format('t') * 86400;
123
    }
124
125
    public function ratioOfMonth(): float
126
    {
127
        $usageSeconds = $this->seconds();
128
        $secondsInCurrentMonth = $this->secondsInMonth();
129
130
        return $usageSeconds / $secondsInCurrentMonth;
131
    }
132
}
133