TimeSpan   A
last analyzed

Complexity

Total Complexity 27

Size/Duplication

Total Lines 263
Duplicated Lines 0 %

Coupling/Cohesion

Components 1
Dependencies 3

Importance

Changes 2
Bugs 0 Features 1
Metric Value
wmc 27
c 2
b 0
f 1
lcom 1
cbo 3
dl 0
loc 263
rs 10

21 Methods

Rating   Name   Duplication   Size   Complexity  
A asDurationWithStartDate() 0 11 1
A asInterval() 0 11 1
A __construct() 0 4 1
A asDuration() 0 14 2
A setStartDate() 0 11 2
A setEndDate() 0 14 2
A getStartDate() 0 4 1
A getEndDate() 0 4 1
A getYearsComponent() 0 4 1
A getMonthsComponent() 0 4 1
A getDaysComponent() 0 4 1
A toYears() 0 4 1
A toMonths() 0 4 1
A toDays() 0 7 1
A clearStartEndDate() 0 4 1
A setDateInterval() 0 4 1
A newDateIntervalAbsolute() 0 11 1
A newDateIntervalDifference() 0 8 1
A checkStartEndDateAndSetInterval() 0 8 2
A roundDateInterval() 0 15 3
A __toString() 0 4 1
1
<?php
2
3
4
namespace FinanCalc\Utils\Time {
5
6
    use DateInterval;
7
    use DateTime;
8
    use Exception;
9
    use FinanCalc\Constants\ErrorMessages;
10
    use FinanCalc\Utils\Helpers;
11
    use FinanCalc\Utils\MathFuncs;
12
    use InvalidArgumentException;
13
14
    /**
15
     * Class TimeSpan
16
     * @package FinanCalc\Utils\Time
17
     */
18
    class TimeSpan
19
    {
20
        /** @var  DateTime */
21
        private $startDate;
22
        /** @var  DateTime */
23
        private $endDate;
24
        /** @var  DateInterval */
25
        private $dateInterval;
26
27
        /**
28
         * PRIVATE constructor
29
         */
30
        private function __construct()
31
        {
32
33
        }
34
35
        /**
36
         * @param integer $years
37
         * @param integer $months
38
         * @param integer $days
39
         * @return TimeSpan
40
         */
41
        public static function asDuration(
42
            $years,
43
            $months = 0,
44
            $days = 0
45
        ) {
46
            foreach (func_get_args() as $arg) {
47
                Helpers::checkIfNotNegativeNumberOrThrowAnException($arg);
48
            }
49
50
            $newThis = new TimeSpan();
51
            $newThis->newDateIntervalAbsolute($years, $months, $days);
52
53
            return $newThis;
54
        }
55
56
        /**
57
         * @param DateTime $startDate
58
         * @param $years
59
         * @param $months
60
         * @param int $days
61
         * @return TimeSpan
62
         */
63
        public static function asDurationWithStartDate(
64
            DateTime $startDate,
65
            $years,
66
            $months = 0,
67
            $days = 0
68
        ) {
69
            $newThis = TimeSpan::asDuration($years, $months, $days);
70
            $newThis->setStartDate($startDate);
71
72
            return $newThis;
73
        }
74
75
        /**
76
         * @param DateTime $startDate
77
         * @param DateTime $endDate
78
         * @return TimeSpan
79
         */
80
        public static function asInterval(
81
            DateTime $startDate,
82
            DateTime $endDate
83
        ) {
84
            $newThis = new TimeSpan();
85
            $newThis->checkStartEndDateAndSetInterval($startDate, $endDate);
86
            $newThis->setStartDate($startDate);
87
            $newThis->setEndDate($endDate);
88
89
            return $newThis;
90
        }
91
92
        /**
93
         * @param DateTime $startDate
94
         */
95
        public function setStartDate(DateTime $startDate)
96
        {
97
            if ($this->endDate !== null) {
98
                $this->checkStartEndDateAndSetInterval($startDate, $this->endDate);
99
            } else {
100
                $endDate = clone $startDate;
101
                $this->endDate = $endDate->add($this->dateInterval);
102
            }
103
104
            $this->startDate = $startDate;
105
        }
106
107
        /**
108
         * @param DateTime $endDate
109
         */
110
        public function setEndDate(DateTime $endDate)
111
        {
112
            if ($this->startDate !== null) {
113
                $this->checkStartEndDateAndSetInterval($this->startDate, $endDate);
114
            } else {
115
                $dateInterval = $this->dateInterval;
116
                $dateInterval->invert = 1;
117
                $startDate = clone $endDate;
118
                $this->startDate = $startDate->add($dateInterval);
119
                $dateInterval->invert = 0;
120
            }
121
122
            $this->endDate = $endDate;
123
        }
124
125
        /**
126
         * @return DateTime
127
         */
128
        public function getStartDate()
129
        {
130
            return $this->startDate;
131
        }
132
133
        /**
134
         * @return DateTime
135
         */
136
        public function getEndDate()
137
        {
138
            return $this->endDate;
139
        }
140
141
        /**
142
         * @return string
143
         */
144
        public function getYearsComponent()
145
        {
146
            return (string)$this->dateInterval->y;
147
        }
148
149
        /**
150
         * @return string
151
         */
152
        public function getMonthsComponent()
153
        {
154
            return (string)$this->dateInterval->m;
155
        }
156
157
        /**
158
         * @return string
159
         */
160
        public function getDaysComponent()
161
        {
162
            return (string)$this->dateInterval->d;
163
        }
164
165
        /**
166
         * @return string
167
         * @throws Exception
168
         */
169
        public function toYears()
170
        {
171
            return MathFuncs::div($this->toDays(), TimeUtils::getCurrentDayCountConvention()['days_in_a_year']);
172
        }
173
174
        /**
175
         * @return string
176
         * @throws Exception
177
         */
178
        public function toMonths()
179
        {
180
            return MathFuncs::div($this->toDays(), TimeUtils::getCurrentDayCountConvention()['days_in_a_month']);
181
        }
182
183
        /**
184
         * @return string
185
         * @throws Exception
186
         */
187
        public function toDays()
188
        {
189
            $yearsComponent = TimeUtils::getDaysFromYears($this->getYearsComponent());
190
            $monthsComponent = TimeUtils::getDaysFromMonths($this->getMonthsComponent());
191
192
            return MathFuncs::add(MathFuncs::add($yearsComponent, $monthsComponent), $this->getDaysComponent());
193
        }
194
195
        public function clearStartEndDate()
196
        {
197
            $this->startDate = $this->endDate = null;
198
        }
199
200
        /** PRIVATE methods */
201
202
        /**
203
         * @param DateInterval $dateInterval
204
         */
205
        private function setDateInterval(DateInterval $dateInterval)
206
        {
207
            $this->dateInterval = $dateInterval;
208
        }
209
210
        /**
211
         * @param $years
212
         * @param $months
213
         * @param $days
214
         */
215
        private function newDateIntervalAbsolute($years, $months, $days)
216
        {
217
            $this->setDateInterval(
218
                new DateInterval(
219
                    "P" .
220
                    (string)$years . "Y" .
221
                    (string)$months . "M" .
222
                    (string)$days . "D"
223
                )
224
            );
225
        }
226
227
        /**
228
         * @param DateTime $startDate
229
         * @param DateTime $endDate
230
         */
231
        private function newDateIntervalDifference(DateTime $startDate, DateTime $endDate)
232
        {
233
            $this->setDateInterval(
234
                $this->roundDateInterval(
235
                    $startDate->diff($endDate)
236
                )
237
            );
238
        }
239
240
        /**
241
         * @param DateTime $startDate
242
         * @param DateTime $endDate
243
         */
244
        private function checkStartEndDateAndSetInterval(DateTime $startDate, DateTime $endDate)
245
        {
246
            if ($startDate < $endDate) {
247
                $this->newDateIntervalDifference($startDate, $endDate);
248
            } else {
249
                throw new InvalidArgumentException(ErrorMessages::getStartDateMustBeBeforeEndDateMessage());
250
            }
251
        }
252
253
        /**
254
         * @param DateInterval $dateInterval
255
         * @return DateInterval
256
         */
257
        private function roundDateInterval(DateInterval $dateInterval)
258
        {
259
            // TODO: make more intelligent rounding based on the start and end date
260
            if (in_array($dateInterval->d, [30, 31])) {
261
                $dateInterval->d = 0;
262
                $dateInterval->m += 1;
263
            }
264
265
            if ($dateInterval->m == 12) {
266
                $dateInterval->y += 1;
267
                $dateInterval->m = 0;
268
            }
269
270
            return $dateInterval;
271
        }
272
273
        /**
274
         * @return string
275
         */
276
        public function __toString()
277
        {
278
            return $this->toDays();
279
        }
280
    }
281
}
282