Passed
Push — master ( 38eb84...e982ac )
by Mars
01:43
created

TimePeriodHelper   F

Complexity

Total Complexity 75

Size/Duplication

Total Lines 511
Duplicated Lines 0 %

Importance

Changes 2
Bugs 1 Features 0
Metric Value
wmc 75
eloc 135
c 2
b 1
f 0
dl 0
loc 511
rs 2.4

18 Methods

Rating   Name   Duplication   Size   Complexity  
A sort() 0 3 1
A cut() 0 3 1
C intersect() 0 44 12
A filter() 0 3 1
A greaterThan() 0 27 6
A fill() 0 3 1
A extend() 0 3 1
A shorten() 0 3 1
A format() 0 3 1
A time() 0 3 1
A union() 0 26 5
A dataSortOut() 0 8 4
A gap() 0 3 1
C isOverlap() 0 36 12
A validate() 0 3 1
A lessThan() 0 27 6
B contact() 0 32 7
C diff() 0 48 13

How to fix   Complexity   

Complex Class

Complex classes like TimePeriodHelper often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use TimePeriodHelper, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
namespace marsapp\helper\timeperiod;
4
5
use marsapp\helper\timeperiod\classes\Base;
6
use marsapp\helper\timeperiod\classes\DataProcess;
7
8
/**
9
 * Time Period Helper
10
 * 
11
 * Note:
12
 * 1. Format: $timePeriods = [[$startDatetime1, $endDatetime1], [$startDatetime2, $endDatetime2], ...];
13
 * - $Datetime = Y-m-d H:i:s ; Y-m-d H:i:00 ; Y-m-d H:00:00 ;
14
 * 2. If it is hour/minute/second, the end point is usually not included, for example, 8 o'clock to 9 o'clock is 1 hour.
15
 * - ●=====○
16
 * 3. If it is a day/month/year, it usually includes an end point, for example, January to March is 3 months.
17
 * - ●=====●
18
 * 4. When processing, assume that the $timePeriods format is correct. If necessary, you need to call the verification function to verify the data.
19
 * 5. Ensure performance by keeping the $timePeriods format correct:
20
 * - a. When getting the raw $timePeriods, sort out it by format(), filter(), union().
21
 * - b. Handle $timePeriods using only the functions provided by TimePeriodHelper (Will not break the format, sort)
22
 * - c. When you achieve the two operations described above, you can turn off auto sort out (TimePeriodHelper::setSortOut(false)) to improve performance.
23
 * 
24
 * @version 0.5.3
25
 * @author Mars Hung <[email protected]>
26
 * @see https://github.com/marshung24/TimePeriodHelper
27
 */
28
class TimePeriodHelper extends Base
29
{
30
31
    /**
32
     * ************************************************
33
     * ************** Operation Function **************
34
     * ************************************************
35
     */
36
37
    /**
38
     * Sort time periods (Order by ASC)
39
     * 
40
     * 1. When sorting, sort the start time first, if the start time is the same, then sort the end time
41
     * 2. Sort Priority: Start Time => End Time
42
     * 
43
     * @param array $timePeriods
44
     * @return array
45
     */
46
    public static function sort(array $timePeriods)
47
    {
48
        return DataProcess::sort($timePeriods);
49
    }
50
51
    /**
52
     * Union one or more time periods
53
     * 
54
     * 1. Sort and merge one or more time periods with contacts
55
     * 2. TimePeriodHelper::union($timePeriods1, $timePeriods2, $timePeriods3, ......);
56
     * 
57
     * @param array $timePeriods
58
     * @return array
59
     */
60
    public static function union()
61
    {
62
        $opt = [];
63
64
        // Combine and sort
65
        $merge = call_user_func_array('array_merge', func_get_args());
66
        $merge = DataProcess::sort($merge);
67
68
        if (empty($merge)) {
69
            return $opt;
70
        }
71
72
        $tmp = array_shift($merge);
73
        foreach ($merge as $k => $tp) {
74
            if ($tp[0] > $tmp[1]) {
75
                // Got it, and set next.
76
                $opt[] = $tmp;
77
                $tmp = $tp;
78
            } elseif ($tp[1] > $tmp[1]) {
79
                // Extend end time
80
                $tmp[1] = $tp[1];
81
            }
82
        }
83
        $opt[] = $tmp;
84
85
        return $opt;
86
    }
87
88
    /**
89
     * Computes the difference of time periods
90
     * 
91
     * 1. Compares $timePeriods1 against $timePeriods2 and returns the values in $timePeriods1 that are not present in $timePeriods2.
92
     * 2. e.g. TimePeriodHelper::diff($timePeriods1, $timePeriods2);
93
     * 3. Whether $timePeriods is sorted out will affect the correctness of the results. Please refer to Note 5. Ensure performance by keeping the $timePeriods format correct.
94
     * 
95
     * @param array $timePeriods1
96
     * @param array $timePeriods2
97
     * @param bool|string $sortOut Whether the input needs to be rearranged. Value: true, false, 'default'. If it is 'default', see getSortOut()
98
     * @return array
99
     */
100
    public static function diff(array $timePeriods1, array $timePeriods2, $sortOut = 'default')
101
    {
102
        /*** Arguments prepare ***/
103
        // Subject or pattern is empty, do nothing
104
        if (empty($timePeriods1) || empty($timePeriods2)) {
105
            return $timePeriods1;
106
        }
107
108
        // Data sorting out
109
        self::dataSortOut($sortOut, $timePeriods1, $timePeriods2);
110
111
        $opt = [];
112
        foreach ($timePeriods1 as $k1 => $ori) {
113
            foreach ($timePeriods2 as $ko => $sub) {
114
                if ($sub[1] <= $ori[0]) {
115
                    // No overlap && Passed: --sub0--sub1--ori0--ori1--
116
                    unset($timePeriods2[$ko]);
117
                    continue;
118
                } elseif ($ori[1] <= $sub[0]) {
119
                    // No overlap: --ori0--ori1--sub0--sub1--
120
                    continue;
121
                } elseif ($sub[0] <= $ori[0] && $ori[1] <= $sub[1]) {
122
                    // Subtract all: --sub0--ori0--ori1--sub1--
123
                    $ori = [];
124
                    break;
125
                } elseif ($ori[0] < $sub[0] && $sub[1] < $ori[1]) {
126
                    // Delete internal: --ori0--sub0--sub1--ori1--
127
                    $opt[] = [$ori[0], $sub[0]];
128
                    $ori = [$sub[1], $ori[1]];
129
                    //} elseif ($sub[0] <= $ori[0] && $sub[1] <= $ori[1]) { // Complete condition
130
                } elseif ($sub[0] <= $ori[0]) { // Equivalent condition
131
                    // Delete overlap: --sub0--ori0--sub1--ori1--
132
                    $ori = [$sub[1], $ori[1]];
133
                    //} elseif ($ori[0] <= $sub[0] && $ori[1] <= $sub[1]) { // Complete condition
134
                    //} elseif ($ori[1] <= $sub[1]) { // Equivalent condition
135
                } else { // Equivalent condition
136
                    // Delete overlap: --ori0--sub0--ori1--sub1--
137
                    $ori = [$ori[0], $sub[0]];
138
                }
139
            }
140
141
            // All No overlap
142
            if (!empty($ori)) {
143
                $opt[] = $ori;
144
            }
145
        }
146
147
        return $opt;
148
    }
149
150
    /**
151
     * Computes the intersection of time periods
152
     * 
153
     * 1. e.g. TimePeriodHelper::intersect($timePeriods1, $timePeriods2);
154
     * 2. Whether $timePeriods is sorted out will affect the correctness of the results. Please refer to Note 5. Ensure performance by keeping the $timePeriods format correct.
155
     * 
156
     * @param array $timePeriods1
157
     * @param array $timePeriods2
158
     * @param bool|string $sortOut Whether the input needs to be rearranged. Value: true, false, 'default'. If it is 'default', see getSortOut()
159
     * @return array
160
     */
161
    public static function intersect(array $timePeriods1, array $timePeriods2, $sortOut = 'default')
162
    {
163
        // Subject or pattern is empty, do nothing
164
        if (empty($timePeriods1) || empty($timePeriods2)) {
165
            return [];
166
        }
167
168
        // Data sorting out
169
        self::dataSortOut($sortOut, $timePeriods1, $timePeriods2);
170
171
        $opt = [];
172
        foreach ($timePeriods1 as $k1 => $ori) {
173
            foreach ($timePeriods2 as $ko => $sub) {
174
                if ($sub[1] <= $ori[0]) {
175
                    // No overlap && Passed: --sub0--sub1--ori0--ori1--
176
                    unset($timePeriods2[$ko]);
177
                    continue;
178
                } elseif ($ori[1] <= $sub[0]) {
179
                    // No overlap: --ori0--ori1--sub0--sub1--
180
                    continue;
181
                } elseif ($sub[0] <= $ori[0] && $ori[1] <= $sub[1]) {
182
                    // Subtract all: --sub0--ori0--ori1--sub1--
183
                    $opt[] = [$ori[0], $ori[1]];
184
                    break;
185
                } elseif ($ori[0] < $sub[0] && $sub[1] < $ori[1]) {
186
                    // Delete internal: --ori0--sub0--sub1--ori1--
187
                    $opt[] = [$sub[0], $sub[1]];
188
                    $ori = [$sub[1], $ori[1]];
189
                    //} elseif ($sub[0] <= $ori[0] && $sub[1] <= $ori[1]) { // Complete condition
190
                } elseif ($sub[0] <= $ori[0]) { // Equivalent condition
191
                    // Delete overlap: --sub0--ori0--sub1--ori1--
192
                    $opt[] = [$ori[0], $sub[1]];
193
                    $ori = [$sub[1], $ori[1]];
194
                    //} elseif ($ori[0] <= $sub[0] && $ori[1] <= $sub[1]) { // Complete condition
195
                    //} elseif ($ori[1] <= $sub[1]) { // Equivalent condition
196
                } else { // Equivalent condition
197
                    // Delete overlap: --ori0--sub0--ori1--sub1--
198
                    $opt[] = [$sub[0], $ori[1]];
199
                    break;
200
                }
201
            }
202
        }
203
204
        return $opt;
205
    }
206
207
    /**
208
     * Time period is overlap
209
     * 
210
     * 1. Determine if there is overlap between the two time periods
211
     * 2. Only when there is no intersection, no data is needed.
212
     * 3. Logic is similar to intersect.
213
     *  
214
     * @param array $timePeriods1
215
     * @param array $timePeriods2
216
     * @return bool
217
     */
218
    public static function isOverlap(array $timePeriods1, array $timePeriods2)
219
    {
220
        // Subject or pattern is empty, do nothing
221
        if (empty($timePeriods1) || empty($timePeriods2)) {
222
            return false;
223
        }
224
225
        foreach ($timePeriods1 as $k1 => $ori) {
226
            foreach ($timePeriods2 as $ko => $sub) {
227
                if ($sub[1] <= $ori[0]) {
228
                    // No overlap && Passed: --sub0--sub1--ori0--ori1--
229
                    unset($timePeriods2[$ko]);
230
                    continue;
231
                } elseif ($ori[1] <= $sub[0]) {
232
                    // No overlap: --ori0--ori1--sub0--sub1--
233
                    continue;
234
                } elseif ($sub[0] <= $ori[0] && $ori[1] <= $sub[1]) {
235
                    // Subtract all: --sub0--ori0--ori1--sub1--
236
                    return true;
237
                } elseif ($ori[0] < $sub[0] && $sub[1] < $ori[1]) {
238
                    // Delete internal: --ori0--sub0--sub1--ori1--
239
                    return true;
240
                    //} elseif ($sub[0] <= $ori[0] && $sub[1] <= $ori[1]) { // Complete condition
241
                } elseif ($sub[0] <= $ori[0]) { // Equivalent condition
242
                    // Delete overlap: --sub0--ori0--sub1--ori1--
243
                    return true;
244
                    //} elseif ($ori[0] <= $sub[0] && $ori[1] <= $sub[1]) { // Complete condition
245
                    //} elseif ($ori[1] <= $sub[1]) { // Equivalent condition
246
                } else { // Equivalent condition
247
                    // Delete overlap: --ori0--sub0--ori1--sub1--
248
                    return true;
249
                }
250
            }
251
        }
252
253
        return false;
254
    }
255
256
    /**
257
     * The time period is in contact with the specified time (time period)
258
     * 
259
     * @param array $timePeriods
260
     * @param string $sDateTime
261
     * @param string $eDateTime
262
     * @param string $sortOut
263
     * @return array
264
     */
265
    public static function contact(array $timePeriods, $sDateTime, $eDateTime = null, $sortOut = 'default')
266
    {
267
        // Subject is empty, do nothing
268
        if (empty($timePeriods)) {
269
            return [];
270
        }
271
272
        // Data sorting out
273
        self::dataSortOut($sortOut, $timePeriods);
274
        // Set $eDateTime
275
        $eDateTime = $eDateTime ?: $sDateTime;
276
        $sTime = min($sDateTime, $eDateTime);
277
        $eTime = max($sDateTime, $eDateTime);
278
279
        // Get Contact time periods
280
        $opt = [];
281
        foreach ($timePeriods as $k => $tp) {
282
            if ($eTime <= $tp[0]) {
283
                // No overlap && Passed: --$sTime--$eTime--$tp0--$tp1--
284
                if ($sTime == $tp[0]) {
285
                    // But 
286
                    $opt[] = $tp;
287
                }
288
            } elseif ($tp[1] <= $sTime) {
289
                // No overlap: --$tp0--$tp1--$sTime--$eTime--
290
            } else {
291
                // Overlap
292
                $opt[] = $tp;
293
            }
294
        }
295
296
        return $opt;
297
    }
298
299
    /**
300
     * Time period greater than the specified time
301
     * 
302
     * @param array $timePeriods
303
     * @param string $refDatetime
304
     * @param string $intactTime
305
     * @param string $sortOut
306
     * @return array
307
     */
308
    public static function greaterThan(array $timePeriods, $refDatetime, $intactTime = true, $sortOut = 'default')
309
    {
310
        // Subject is empty, do nothing
311
        if (empty($timePeriods)) {
312
            return [];
313
        }
314
315
        // Data sorting out
316
        self::dataSortOut($sortOut, $timePeriods);
317
318
        // Get Contact time periods
319
        $opt = [];
320
        foreach ($timePeriods as $k => $tp) {
321
            if ($intactTime) {
322
                // Time period is intact
323
                if ($tp[0] >= $refDatetime) {
324
                    $opt[] = $tp;
325
                }
326
            } else {
327
                // Time period not intact
328
                if ($tp[1] > $refDatetime) {
329
                    $opt[] = $tp;
330
                }
331
            }
332
        }
333
334
        return $opt;
335
    }
336
337
    /**
338
     * Time period less than the specified time
339
     * 
340
     * @param array $timePeriods
341
     * @param string $refDatetime
342
     * @param string $intactTime
343
     * @param string $sortOut
344
     * @return array
345
     */
346
    public static function lessThan(array $timePeriods, $refDatetime, $intactTime = true, $sortOut = 'default')
347
    {
348
        // Subject is empty, do nothing
349
        if (empty($timePeriods)) {
350
            return [];
351
        }
352
353
        // Data sorting out
354
        self::dataSortOut($sortOut, $timePeriods);
355
356
        // Get Contact time periods
357
        $opt = [];
358
        foreach ($timePeriods as $k => $tp) {
359
            if ($intactTime) {
360
                // Time period is intact
361
                if ($tp[1] <= $refDatetime) {
362
                    $opt[] = $tp;
363
                }
364
            } else {
365
                // Time period not intact
366
                if ($tp[0] < $refDatetime) {
367
                    $opt[] = $tp;
368
                }
369
            }
370
        }
371
372
        return $opt;
373
    }
374
375
    /**
376
     * Fill time periods
377
     * 
378
     * Leaving only the first start time and the last end time
379
     * 
380
     * @param array $timePeriods
381
     * @return array
382
     */
383
    public static function fill(array $timePeriods)
384
    {
385
        return DataProcess::fill($timePeriods);
386
    }
387
388
    /**
389
     * Get gap time periods of multiple sets of time periods
390
     * 
391
     * 1. Whether $timePeriods is sorted out will affect the correctness of the results. Please refer to Note 5. Ensure performance by keeping the $timePeriods format correct.
392
     * 
393
     * @param array $timePeriods
394
     * @param bool|string $sortOut Whether the input needs to be rearranged. Value: true, false, 'default'. If it is 'default', see getSortOut()
395
     * @return array
396
     */
397
    public static function gap(array $timePeriods, $sortOut = 'default')
398
    {
399
        return DataProcess::gap($timePeriods, $sortOut);
400
    }
401
402
    /**
403
     * Calculation period total time
404
     *
405
     * 1. You can specify the smallest unit (from setUnit())
406
     * 2. Whether $timePeriods is sorted out will affect the correctness of the results. Please refer to Note 5. Ensure performance by keeping the $timePeriods format correct.
407
     * 3. approximation: chop off
408
     *
409
     * @param array $timePeriods            
410
     * @param int $precision
411
     *            Optional decimal places for the decimal point
412
     * @param bool|string $sortOut Whether the input needs to be rearranged. Value: true, false, 'default'. If it is 'default', see getSortOut()
413
     * @return number
414
     */
415
    public static function time(array $timePeriods, $precision = 0, $sortOut = 'default')
416
    {
417
        return DataProcess::time($timePeriods, $precision, $sortOut);
418
    }
419
420
    /**
421
     * Cut the time period of the specified length of time
422
     *
423
     * 1. You can specify the smallest unit (from setUnit())
424
     * 2. Whether $timePeriods is sorted out will affect the correctness of the results. Please refer to Note 5. Ensure performance by keeping the $timePeriods format correct.
425
     * 
426
     * @param array $timePeriods            
427
     * @param number $time
428
     *            Specified length of time
429
     * @param bool|string $sortOut Whether the input needs to be rearranged. Value: true, false, 'default'. If it is 'default', see getSortOut()
430
     * @return array
431
     */
432
    public static function cut(array $timePeriods, $time, $sortOut = 'default')
433
    {
434
        return DataProcess::cut($timePeriods, $time, $sortOut);
435
    }
436
437
    /**
438
     * Increase the time period of the specified length of time after the last time period
439
     *
440
     * 1. You can specify the smallest unit (from setUnit())
441
     * 2. Whether $timePeriods is sorted out will affect the correctness of the results. Please refer to Note 5. Ensure performance by keeping the $timePeriods format correct.
442
     * 
443
     * @param array $timePeriods            
444
     * @param number $time
445
     *            Specified length of time (default uint:second)
446
     * @param number $interval
447
     *            Interval with existing time period
448
     * @param bool|string $sortOut Whether the input needs to be rearranged. Value: true, false, 'default'. If it is 'default', see getSortOut()
449
     * @return array
450
     */
451
    public static function extend(array $timePeriods, $time, $interval = 0, $sortOut = 'default')
452
    {
453
        return DataProcess::extend($timePeriods, $time, $interval, $sortOut);
454
    }
455
456
    /**
457
     * Shorten the specified length of time from behind
458
     *
459
     * 1. You can specify the smallest unit (from setUnit())
460
     * 2. Whether $timePeriods is sorted out will affect the correctness of the results. Please refer to Note 5. Ensure performance by keeping the $timePeriods format correct.
461
     * 
462
     * @param array $timePeriods            
463
     * @param number $time
464
     *            Specified length of time (default uint:second)
465
     * @param bool $crossperiod
466
     *            Whether to shorten across time
467
     * @param bool|string $sortOut Whether the input needs to be rearranged. Value: true, false, 'default'. If it is 'default', see getSortOut()
468
     * @return array
469
     */
470
    public static function shorten(array $timePeriods, $time, $crossperiod = true, $sortOut = 'default')
471
    {
472
        return DataProcess::shorten($timePeriods, $time, $crossperiod, $sortOut);
473
    }
474
475
    /**
476
     * Transform format
477
     * 
478
     * @param array $timePeriods
479
     * @param string $unit Time unit, if default,use class options setting
480
     * @return array
481
     */
482
    public static function format(array $timePeriods, $unit = 'default')
483
    {
484
        return DataProcess::format($timePeriods, $unit);
485
    }
486
487
    /**
488
     * Validate time period
489
     * 
490
     * Verify format, size, start/end time
491
     * 
492
     * @param mixed|array $timePeriods
493
     * @throws \Exception
494
     * @return bool
495
     */
496
    public static function validate($timePeriods)
497
    {
498
        return DataProcess::validate($timePeriods);
499
    }
500
501
    /**
502
     * Remove invalid time period
503
     * 
504
     * 1. Verify format, size, start/end time, and remove invalid.
505
     * 2. time carry problem processing, e.g. 2019-01-01 24:00:00 => 2019-01-02 00:00:00
506
     * 
507
     * @param mixed|array $timePeriods
508
     * @throws \Exception
509
     * @return array
510
     */
511
    public static function filter($timePeriods)
512
    {
513
        return DataProcess::filter($timePeriods);
514
    }
515
516
517
    /**
518
     * ********************************************
519
     * ************** Tools Function **************
520
     * ********************************************
521
     */
522
523
    /**
524
     * Data sorting out
525
     *
526
     * @param bool|string $sortOut
527
     * @param array $timePeriods1
528
     * @param array|null $timePeriods2
529
     * @return void
530
     */
531
    public static function dataSortOut(&$sortOut, &$timePeriods1, &$timePeriods2 = null)
532
    {
533
        // Data sorting out
534
        $sortOut = $sortOut === 'default' ? self::getSortOut() : !!$sortOut;
535
        if ($sortOut) {
536
            $timePeriods1 = self::union($timePeriods1);
537
            if (!is_null($timePeriods2)) {
538
                $timePeriods2 = self::union($timePeriods2);
539
            }
540
        }
541
    }
542
}
543