Dates::unwrapWeek()   A
last analyzed

Complexity

Conditions 3
Paths 3

Size

Total Lines 21

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 0
CRAP Score 12

Importance

Changes 0
Metric Value
dl 0
loc 21
ccs 0
cts 18
cp 0
rs 9.584
c 0
b 0
f 0
cc 3
nc 3
nop 4
crap 12
1
<?php
2
3
namespace DavisPeixoto\ReportDates;
4
5
use DateInterval;
6
use DateTime;
7
use Exception;
8
9
class Dates
10
{
11
    /**
12
     * @var DatesConfig $config
13
     */
14
    private $config;
15
16
    /**
17
     * @var DateTime $startDate
18
     */
19
    private $startDate;
20
21
    /**
22
     * @var DateTime $endDate
23
     */
24
    private $endDate;
25
26
    /**
27
     * Dates constructor.
28
     *
29
     * @param DatesConfig|null $config
30
     */
31
    public function __construct(DatesConfig $config = null)
32
    {
33
        $this->config = $config ?? new DatesConfig();
34
    }
35
36
    /**
37
     * Returns an array of week intervals
38
     *
39
     * @param DateTime $startDate
40
     * @param DateTime $endDate
41
     * @param bool $full include non-business days if true
42
     * @param bool $inclusive decide whether we should expand the days until the week start or not
43
     *
44
     * @return WeekInterval[]
45
     * @throws Exception
46
     */
47
    public function getWeeksBreak(DateTime $startDate, DateTime $endDate, $full = false, $inclusive = false)
48
    {
49
        $this->startDate = $startDate;
50
        $this->endDate = $endDate;
51
        $output = [];
52
53
        if ($inclusive) {
54
            $this->addPaddingDays();
55
        }
56
57
        foreach ($this->makeIntervals($full) as $key => $value) {
58
            $output[$key] = new WeekInterval($value['start'], $value['end']);
0 ignored issues
show
Bug introduced by
The call to WeekInterval::__construct() misses a required argument $config.

This check looks for function calls that miss required arguments.

Loading history...
59
        }
60
61
        return $output;
62
    }
63
64
    /**
65
     * Returns a week interval, based on year week and year month
66
     * Both parameters are necessary because a given week can have
67
     * days from two different months, and in this case, we might
68
     * want to pull days from one month, another month, or even
69
     * the entire week, regardless the month
70
     *
71
     * @param string $yearWeek
72
     * @param string $yearMonth
73
     * @param bool $full
74
     * @param bool $inclusive
75
     * @return WeekInterval
76
     * @throws Exception
77
     */
78
    public function unwrapWeek(
79
        string $yearWeek,
80
        string $yearMonth,
81
        bool $full = false,
82
        bool $inclusive = false
83
    ): WeekInterval {
84
        $aux = str_split($yearMonth, 4);
85
        $aux[] = '01';
86
        $objStart = new DateTime(implode('-', $aux));
87
88
        $startDate = new DateTime(date('Y-m-01', $objStart->getTimestamp()));
89
        $endDate = new DateTime(date('Y-m-t', $objStart->getTimestamp()));
90
91
        $weeks = $this->getWeeksBreak($startDate, $endDate, $full, $inclusive);
92
93
        foreach ($weeks as $week) {
94
            if ($week->getYearWeek() === $yearWeek) {
95
                return $week;
96
            }
97
        }
98
    }
99
100
    /**
101
     * @throws Exception
102
     */
103
    private function adjustDayLightSavingsTime() :void
104
    {
105
        $oneHour = new DateInterval('PT1H');
106
107
        if ($this->startDate->format('h') === '01') {
108
            $this->startDate->sub($oneHour);
109
        }
110
111
        if ($this->startDate->format('h') === '23') {
112
            $this->startDate->add($oneHour);
113
        }
114
    }
115
116
    private function addPaddingDays()
117
    {
118 View Code Duplication
        if ($this->startDate->format('w') !== $this->config->getWeeksStartsOn()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
119
            $this->startDate->setISODate(
120
                (int) $this->startDate->format('o'),
121
                (int) $this->startDate->format('W'),
122
                (int) $this->config->getWeeksStartsOn()->getValue()
123
            );
124
        }
125
126 View Code Duplication
        if ($this->endDate->format('w') !== $this->config->getWeeksEndsOn()) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
127
            $this->endDate->setISODate(
128
                (int) $this->endDate->format('o'),
129
                (int) $this->endDate->format('W'),
130
                (int) $this->config->getWeeksEndsOn()->getValue()
131
            );
132
        }
133
    }
134
135
    /**
136
     * @param bool $full
137
     * @return array
138
     *
139
     * @throws Exception
140
     */
141
    private function makeIntervals(bool $full): array
142
    {
143
        if ($full) {
144
            return $this->makeFull();
145
        }
146
147
        return $this->makeBusinessOnly();
148
    }
149
150
    /**
151
     * @return array
152
     * @throws Exception
153
     */
154
    private function makeFull() :array
155
    {
156
        $output = [];
157
        $index = 0;
158
        $isOpen = false;
159
        $oneDay = new DateInterval('P1D');
160
161
        while ($this->startDate <= $this->endDate) {
162
            $str = $this->startDate->format('w');
163
164 View Code Duplication
            if (!$isOpen && ($str !== $this->config->getWeeksEndsOn()->getValue())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
165
                $output[$index]['start'] = clone $this->startDate;
166
                $isOpen = true;
167
            }
168
169 View Code Duplication
            if ($isOpen && ($str === $this->config->getWeeksEndsOn()->getValue())) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
170
                $output[$index]['end'] = clone $this->startDate;
171
                $isOpen = false;
172
                $index++;
173
            }
174
175 View Code Duplication
            if ($isOpen && ($this->startDate == $this->endDate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
176
                $output[$index]['end'] = clone $this->startDate;
177
                $isOpen = false;
178
            }
179
180
            $this->startDate->add($oneDay);
181
            $this->adjustDayLightSavingsTime();
182
        }
183
184
        return $output;
185
    }
186
187
    /**
188
     * @return array
189
     * @throws Exception
190
     */
191
    private function makeBusinessOnly() : array
192
    {
193
        $output = [];
194
        $index = 0;
195
        $isOpen = false;
196
        $oneDay = new DateInterval('P1D');
197
198
        while ($this->startDate <= $this->endDate) {
199
            $str = $this->startDate->format('w');
200
201
            if (!$isOpen && $this->isInRange($str)) {
202
                $output[$index]['start'] = clone $this->startDate;
203
                $isOpen = true;
204
            }
205
206
            if ($isOpen && !$this->isInRange($str)) {
207
                $aux = clone $this->startDate;
208
                $aux->sub($oneDay);
209
                $output[$index]['end'] = clone $aux;
210
                $isOpen = false;
211
                $index++;
212
            }
213
214 View Code Duplication
            if ($isOpen && ($this->startDate == $this->endDate)) {
0 ignored issues
show
Duplication introduced by
This code seems to be duplicated across your project.

Duplicated code is one of the most pungent code smells. If you need to duplicate the same code in three or more different places, we strongly encourage you to look into extracting the code into a single class or operation.

You can also find more detailed suggestions in the “Code” section of your repository.

Loading history...
215
                $output[$index]['end'] = clone $this->startDate;
216
                $isOpen = false;
217
            }
218
219
            $this->startDate->add($oneDay);
220
            $this->adjustDayLightSavingsTime();
221
        }
222
223
        return $output;
224
    }
225
226
    /**
227
     * @param string $day
228
     * @return bool
229
     */
230
    private function isInRange($day) :bool
231
    {
232
        foreach ($this->config->getBusinessDays() as $businessDay) {
233
            if ($businessDay->getValue() === $day) {
234
                return true;
235
            }
236
        }
237
238
        return false;
239
    }
240
}
241