Completed
Branch master (fd93d1)
by Bushlanov
05:04 queued 01:22
created

WorkingTime::modify()   A

Complexity

Conditions 3
Paths 4

Size

Total Lines 15
Code Lines 10

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
c 0
b 0
f 0
dl 0
loc 15
rs 9.4285
cc 3
eloc 10
nc 4
nop 2
1
<?php
2
/**
3
 * @copyright 2016, Aleksandr Bushlanov
4
 * @package gtvolk\WorkingTime
5
 * @link https://github.com/GT-Volk/
6
 * @license https://github.com/GT-Volk/working-time/blob/master/LICENSE.md
7
 */
8
9
namespace gtvolk\WorkingTime;
10
11
/**
12
 * Класс рассчитывает временнЫе интервалы, учитывая рабочие часы и выходные дни.
13
 *
14
 * @property \DateTime $dateTime
15
 * @property array $workingDays
16
 * @property array $weekends
17
 * @property array $holidays
18
 *
19
 * @since  1.0.0
20
 *
21
 * @author Aleksandr Bushlanov <[email protected]>
22
 */
23
class WorkingTime
24
{
25
    /**
26
     * @var \DateTime
27
     */
28
    public $dateTime;
29
30
    /**
31
     * @var array
32
     */
33
    public $workingDays;
34
35
    /**
36
     * @var array
37
     */
38
    public $weekends;
39
40
    /**
41
     * @var array
42
     */
43
    public $holidays;
44
45
    /**
46
     * WorkingTime constructor.
47
     *
48
     * @param array $workTimeConfig
49
     * @param string $dateTime
50
     */
51
    public function __construct(array $workTimeConfig, string $dateTime = 'now')
52
    {
53
        $this->dateTime = new \DateTime($dateTime);
54
        foreach ($workTimeConfig as $name => $value) {
55
            $this->$name = $value;
56
        }
57
    }
58
59
    /**
60
     * Фурмирует строку дня.
61
     *
62
     * @param string $date
63
     * @param string $format
64
     * @return string
65
     */
66
    private function buildDataPartString(string $date, string $format) : string
67
    {
68
        if ($date === null) {
69
            $day = date($format, $this->dateTime->getTimestamp());
70
        } else {
71
            $day = date($format, strtotime($date));
72
        }
73
74
        return $day;
75
    }
76
77
    /**
78
     * Формирует дату из строки.
79
     *
80
     * @param string $date
81
     * @return \DateTime
82
     */
83
    private function buildDate(string $date = null) : \DateTime
84
    {
85
        if ($date === null) {
86
            return new \DateTime($this->dateTime->format('Y-m-d H:i'));
87
        } else {
88
            return new \DateTime($date);
89
        }
90
    }
91
92
    /**
93
     * Проверяет является ли дата праздничным днём.
94
     *
95
     * @param string $date
96
     * @return bool
97
     */
98
    public function isHoliday(string $date = null) : bool
99
    {
100
        $day = $this->buildDataPartString($date, 'm-d');
101
        return in_array($day, $this->holidays, false);
102
    }
103
104
    /**
105
     * Проверяет является ли дата выходным днём.
106
     *
107
     * @param string $date
108
     * @return bool
109
     */
110
    public function isWeekend(string $date = null) : bool
111
    {
112
        $day = $this->buildDataPartString($date, 'w');
113
        return in_array($day, $this->weekends, false);
114
    }
115
116
    /**
117
     * Проверяет евляется ли дата рабочим днём.
118
     *
119
     * Формат даты - "Y-m-d"
120
     * @param string $date
121
     * @return bool
122
     */
123
    public function isWorkingDate(string $date = null) : bool
124
    {
125
        if ($date === null) {
126
            $unixTime = $this->dateTime->getTimestamp();
127
        } else {
128
            $unixTime = strtotime($date);
129
        }
130
131
        $dayNumber = date('w', $unixTime);
132
        if (in_array($dayNumber, $this->weekends, false)) {
133
            // Это выходной день
134
            return false;
135
        }
136
137
        $dayAndMonth = date('m-d', $unixTime);
138
        if (in_array($dayAndMonth, $this->holidays, false)) {
139
            // Это праздничный день
140
            return false;
141
        }
142
143
        // Рабочий день
144
        return true;
145
    }
146
147
    /**
148
     * Проверяет евляется ли время рабочим.
149
     *
150
     * @param string|null $time Формат времени - "H:i" или полный "Y-m-d H:i"
151
     * @return bool
152
     * @throws \Exception
153
     */
154
    public function isWorkingTime(string $time = null) : bool
155
    {
156
        if ($time === null) {
157
            $dateTime = $this->dateTime;
158
        } else {
159
            if (self::validateDate($time, 'H:i')) {
160
                $dateTime = new \DateTime($this->dateTime->format('Y-m-d') . ' ' . $time);
161
            } elseif (self::validateDate($time, 'Y-m-d H:i')) {
162
                $dateTime = new \DateTime($time);
163
            } else {
164
                throw new \InvalidArgumentException("Date `{$time}` isn't a valid date. Dates should be formatted as Y-m-d or H:i, e.g. `2016-10-27` or `17:30`.");
165
            }
166
        }
167
168
        list($jobStart, $jobSEnd) = explode('-', $this->workingDays[$dateTime->format('w')]);
169
170
        $dtStart = new \DateTime($dateTime->format('Y-m-d') . ' ' . $jobStart);
171
        $dtEnd = new \DateTime($dateTime->format('Y-m-d') . ' ' . $jobSEnd);
172
173
        if ($dateTime > $dtStart && $dateTime < $dtEnd) {
174
            return true;
175
        } else {
176
            return false;
177
        }
178
    }
179
180
    /**
181
     * Возвращает следующий рабочий день.
182
     *
183
     * @param string|null $date Формат даты - "Y-m-d"
184
     * @return string
185
     */
186
    public function nextWorkingDay(string $date = null) : string
187
    {
188
        if ($date === null) {
189
            $dateTime = new \DateTime($this->dateTime->format('Y-m-d'));
190
        } else {
191
            $dateTime = new \DateTime($date);
192
        }
193
194
        do {
195
            $dateTime->modify('+1 day');
196
        } while (!$this->isWorkingDate($dateTime->format('Y-m-d')));
197
198
        return $dateTime->format('Y-m-d');
199
    }
200
201
    /**
202
     * Возвращает ближайшее рабочее время. Либо пустую строку если текущее время уже рабочее
203
     *
204
     * @param string|null $date
205
     * @return string
206
     */
207
    public function nextWorkingTime(string $date = null) : string
208
    {
209
        $dateTime = $this->buildDate($date);
210
211
        $nextWorkingTime = '';
212
213
        // Если дня нет в конфиге считаем его выходным
214
        if (!isset($this->workingDays[$dateTime->format('w')])) {
215
            $nextWorkingDay = $this->nextWorkingDay($dateTime->format('Y-m-d'));
216
            $nWDateTime = new \DateTime($nextWorkingDay);
217
            $workTime = explode('-', $this->workingDays[$nWDateTime->format('w')]);
218
            return $nextWorkingDay . ' ' . $workTime[0];
219
        }
220
221
        list($jobStart, $jobSEnd) = explode('-', $this->workingDays[$dateTime->format('w')]);
222
223
        if ($this->isWorkingDate($dateTime->format('Y-m-d'))) { // Если день рабочий проверяем время
224
225
            $dtStart = new \DateTime($dateTime->format('Y-m-d') . ' ' . $jobStart);
226
            $dtEnd = new \DateTime($dateTime->format('Y-m-d') . ' ' . $jobSEnd);
227
228
            // Если начало дня еще не наступило (утро) возвращаем указанную дату + время
229
            if ($dateTime < $dtStart) {
230
                $nextWorkingTime = $dateTime->format('Y-m-d') . ' ' . $jobStart;
231
            } elseif ($dateTime >= $dtEnd) { // Если рабочий день уже закончился
232
                // Ищем следующий рабочий день и выводим его + время начало дня
233
                $nextWorkingTime = $this->nextWorkingDay($dateTime->format('Y-m-d')) . ' ' . $jobStart;
234
            }
235
        } else { // Если день не рабочий
236
237
            // Ищем следующий рабочий день и выводим его + время начало дня
238
            $nextWorkingTime = $this->nextWorkingDay($dateTime->format('Y-m-d')) . ' ' . $jobStart;
239
        }
240
241
        return $nextWorkingTime;
242
    }
243
244
    /**
245
     * Возвращает длинну рабочего дня в минутах.
246
     *
247
     * @param string|null $date Формат даты - "Y-m-d"
248
     * @return int
249
     */
250
    public function getJobMinutesInDay(string $date = null) : int
251
    {
252
        $day = $this->buildDataPartString($date, 'w');
253
254
        $nextWorkingTime = $this->nextWorkingTime($date ?? $this->dateTime->format('Y-m-d H:i'));
255
256
        list($jobStart, $jobSEnd) = explode('-', $this->workingDays[(int)$day]);
257
        // Считаем остаток рабочего времени
258
        if (empty($nextWorkingTime)) {
259
            $jobStart = ($date === null ? date('H:i', $this->dateTime->getTimestamp()) : date('H:i', strtotime($date)));
260
        }
261
262
        $dtStart = new \DateTime($jobStart);
263
        $dtEnd = new \DateTime($jobSEnd);
264
        $diff = $dtEnd->diff($dtStart);
265
266
        return ($diff->h * 60 + $diff->i);
267
    }
268
269
    /**
270
     * Прибавляет заданное количество минут к дате с учетом рабочего времени.
271
     *
272
     * @param int $minutes
273
     * @param string|null $date
274
     * @return \DateTime
275
     */
276
    private function modifyDate(int $minutes, string $date = null) : \DateTime
277
    {
278
        $dateTime = new \DateTime($date);
279
        $jobMinutesInDay = $this->getJobMinutesInDay($date);
280
281
        // Если длинна дня больше чем время модификации
282
        if ($jobMinutesInDay > $minutes) {
283
            $dateTime->modify("+$minutes minutes");
284
        } else { // Если длинна дня меньше чем время модификации
285
            do {
286
                $dateTime->modify("+$jobMinutesInDay minutes");
287
                $minutes -= $jobMinutesInDay;
288
                $nextWorkingTime = $this->nextWorkingTime($dateTime->format('Y-m-d H:i'));
289
                $dateTime = new \DateTime($nextWorkingTime);
290
                $jobMinutesInNextDay = $this->getJobMinutesInDay($dateTime->format('Y-m-d H:i'));
291
                if ($jobMinutesInNextDay > $minutes) {
292
                    $dateTime->modify("+$minutes minutes");
293
                    $minutes = 0;
294
                }
295
            } while ($minutes > 0);
296
        }
297
298
        return $dateTime;
299
    }
300
301
    /**
302
     * Прибавляет заданное количество минут к дате с учетом рабочего времени.
303
     *
304
     * @param int $minutes
305
     * @param string $date
306
     * @return string
307
     */
308
    public function modify(int $minutes, string $date = null) : string
309
    {
310
        $nextWorkingTime = $this->nextWorkingTime($date);
311
        $dateTime = null;
0 ignored issues
show
Unused Code introduced by
$dateTime is not used, you could remove the assignment.

This check looks for variable assignements that are either overwritten by other assignments or where the variable is not used subsequently.

$myVar = 'Value';
$higher = false;

if (rand(1, 6) > 3) {
    $higher = true;
} else {
    $higher = false;
}

Both the $myVar assignment in line 1 and the $higher assignment in line 2 are dead. The first because $myVar is never used and the second because $higher is always overwritten for every possible time line.

Loading history...
312
        // Если дата вне рабочего времени
313
        if (!empty($nextWorkingTime)) {
314
            $dateTime = $this->modifyDate($minutes, $nextWorkingTime);
315
        } else { // если дата в пределах рабочего времени
316
            $dateTime = $this->modifyDate($minutes, $date);
317
        }
318
        if ($date === null) {
319
            $this->dateTime->setTimestamp($dateTime->getTimestamp());
320
        }
321
        return $dateTime->format('Y-m-d H:i');
322
    }
323
324
    /**
325
     * Проверяет является ли строка корректной датой.
326
     *
327
     * @param $date
328
     * @param string $format
329
     * @return bool
330
     */
331
    public static function validateDate(string $date, string $format = 'Y-m-d H:i:s') : bool
332
    {
333
        $vDate = \DateTime::createFromFormat($format, $date);
334
        return $vDate && $vDate->format($format) === $date;
335
    }
336
}
337