Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
121 / 121
100.00% covered (success)
100.00%
12 / 12
CRAP
100.00% covered (success)
100.00%
1 / 1
DateTime
100.00% covered (success)
100.00%
121 / 121
100.00% covered (success)
100.00%
12 / 12
39
100.00% covered (success)
100.00%
1 / 1
 create
100.00% covered (success)
100.00%
6 / 6
100.00% covered (success)
100.00%
1 / 1
3
 setDefaultDateFormat
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getDefaultDateFormat
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasDefaultDateFormat
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 setDefaultTimeFormat
100.00% covered (success)
100.00%
2 / 2
100.00% covered (success)
100.00%
1 / 1
1
 getDefaultTimeFormat
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 hasDefaultTimeFormat
100.00% covered (success)
100.00%
1 / 1
100.00% covered (success)
100.00%
1 / 1
1
 isDst
100.00% covered (success)
100.00%
11 / 11
100.00% covered (success)
100.00%
1 / 1
7
 getTotal
100.00% covered (success)
100.00%
28 / 28
100.00% covered (success)
100.00%
1 / 1
8
 getAverage
100.00% covered (success)
100.00%
29 / 29
100.00% covered (success)
100.00%
1 / 1
8
 getWeekDates
100.00% covered (success)
100.00%
32 / 32
100.00% covered (success)
100.00%
1 / 1
4
 __toString
100.00% covered (success)
100.00%
7 / 7
100.00% covered (success)
100.00%
1 / 1
3
1<?php
2/**
3 * Pop PHP Framework (https://www.popphp.org/)
4 *
5 * @link       https://github.com/popphp/popphp-framework
6 * @author     Nick Sagona, III <dev@noladev.com>
7 * @copyright  Copyright (c) 2009-2026 NOLA Interactive, LLC.
8 * @license    https://www.popphp.org/license     New BSD License
9 */
10
11/**
12 * @namespace
13 */
14namespace Pop\Utils;
15
16use DateTimeZone;
17use DateInterval;
18
19/**
20 * Pop utils date-time helper class
21 *
22 * @category   Pop
23 * @package    Pop\Utils
24 * @author     Nick Sagona, III <dev@noladev.com>
25 * @copyright  Copyright (c) 2009-2026 NOLA Interactive, LLC.
26 * @license    https://www.popphp.org/license     New BSD License
27 * @version    2.3.0
28 */
29class DateTime extends \DateTime
30{
31
32    use DateTimeTrait;
33
34    /**
35     * Default date format
36     * @var ?string
37     */
38    protected ?string $defaultDateFormat = null;
39
40    /**
41     * Default time format
42     * @var ?string
43     */
44    protected ?string $defaultTimeFormat = null;
45
46    /**
47     * Create a new DateTime object
48     *
49     * @param  string        $dateTime
50     * @param  ?DateTimeZone $timeZone
51     * @param  ?string       $defaultDateFormat
52     * @param  ?string       $defaultTimeFormat
53     * @throws \Exception
54     * @return static
55     */
56    public static function create(
57        string $dateTime = 'now', ?DateTimeZone $timeZone = null,
58        ?string $defaultDateFormat = null, ?string $defaultTimeFormat = null): static
59    {
60        $dt = new static($dateTime, $timeZone);
61        if ($defaultDateFormat !== null) {
62            $dt->setDefaultDateFormat($defaultDateFormat);
63        }
64        if ($defaultTimeFormat !== null) {
65            $dt->setDefaultTimeFormat($defaultTimeFormat);
66        }
67
68        return $dt;
69    }
70
71    /**
72     * Method to set the default date format
73     *
74     * @param  string $defaultDateFormat
75     * @return static
76     */
77    public function setDefaultDateFormat(string $defaultDateFormat): static
78    {
79        $this->defaultDateFormat = $defaultDateFormat;
80        return $this;
81    }
82
83    /**
84     * Method to get the default date format
85     *
86     * @return string
87     */
88    public function getDefaultDateFormat(): string
89    {
90        return $this->defaultDateFormat;
91    }
92
93    /**
94     * Method to see if the object as a default date format
95     *
96     * @return bool
97     */
98    public function hasDefaultDateFormat(): bool
99    {
100        return !empty($this->defaultDateFormat);
101    }
102
103    /**
104     * Method to set the default time format
105     *
106     * @param  string $defaultTimeFormat
107     * @return static
108     */
109    public function setDefaultTimeFormat(string $defaultTimeFormat): static
110    {
111        $this->defaultTimeFormat = $defaultTimeFormat;
112        return $this;
113    }
114
115    /**
116     * Method to get the default time format
117     *
118     * @return string
119     */
120    public function getDefaultTimeFormat(): string
121    {
122        return $this->defaultTimeFormat;
123    }
124
125    /**
126     * Method to see if the object as a default time format
127     *
128     * @return bool
129     */
130    public function hasDefaultTimeFormat(): bool
131    {
132        return !empty($this->defaultTimeFormat);
133    }
134
135    /**
136     * Method to determine if time is currently DST
137     *
138     * Standard hh:mm:ss format string is '%H:%I:%S'
139     *
140     * @param  ?string $dateTime
141     * @param  ?string $dstStart
142     * @param  ?string $dstEnd
143     * @throws \InvalidArgumentException
144     * @return bool
145     */
146    public static function isDst(?string $dateTime = null, ?string $dstStart = null, ?string $dstEnd = null): bool
147    {
148        if ($dateTime === null) {
149            $dateTime = time();
150        } else if (!is_numeric($dateTime)) {
151            $dateTime = strtotime($dateTime);
152            if ($dateTime === false) {
153                throw new \InvalidArgumentException('Error: Invalid date-time parameter.');
154            }
155        }
156
157        // Default to U.S.-based DST
158        if (($dstStart === null) || ($dstEnd === null)) {
159            $year     = date('Y', $dateTime);
160            $dstStart = strtotime('Second Sunday of March ' . $year . ' 2AM');
161            $dstEnd   = strtotime('First Sunday of November ' . $year . ' 2AM');
162        }
163
164        return (($dateTime > $dstStart) && ($dateTime < $dstEnd));
165    }
166
167    /**
168     * Method to get total time from array of multiple time values in HH:MM:SS format
169     *
170     * Standard hh:mm:ss format string is '%H:%I:%S'
171     *
172     * @param  array $times
173     * @param  ?string $format
174     * @param  bool $secondsOnly
175     * @throws \Exception
176     * @return DateInterval|string
177     */
178    public static function getTotal(array $times, ?string $format = null, bool $secondsOnly = false): DateInterval|string
179    {
180        $totalHours   = 0;
181        $totalMinutes = 0;
182        $totalSeconds = 0;
183
184        foreach ($times as $time) {
185            if ($time instanceof \DateInterval) {
186                $hours   = $time->format('%h');
187                $minutes = $time->format('%i');
188                $seconds = $time->format('%s');
189            } else {
190                if (substr_count($time, ':') == 2) {
191                    [$hours, $minutes, $seconds] = explode(':', $time);
192                } else {
193                    $hours = 0;
194                    [$minutes, $seconds] = explode(':', $time);
195                }
196            }
197            $totalHours   += (int)$hours;
198            $totalMinutes += (int)$minutes;
199            $totalSeconds += (int)$seconds;
200        }
201
202        if ($secondsOnly) {
203            $totalSeconds  += (int)$totalHours * 3600;
204            $totalSeconds  += (int)$totalMinutes * 60;
205            $intervalFormat = 'PT' . $totalSeconds . 'S';
206        } else {
207            if ($totalSeconds > 60) {
208                $totalMinutes += floor($totalSeconds / 60);
209                $totalSeconds  = $totalSeconds % 60;
210            }
211            if ($totalMinutes > 60) {
212                $totalHours  += floor($totalMinutes / 60);
213                $totalMinutes = $totalMinutes % 60;
214            }
215            $intervalFormat = 'PT' . (int)$totalHours . 'H' . (int)$totalMinutes . 'M' . (int)$totalSeconds . 'S';
216        }
217
218        $dateInterval = new DateInterval($intervalFormat);
219
220        return ($format !== null) ? $dateInterval->format($format) : $dateInterval;
221    }
222
223    /**
224     * Method to get average time from array of multiple time values in HH:MM:SS format
225     *
226     * Standard hh:mm:ss format string is '%H:%I:%S'
227     *
228     * @param  array   $times
229     * @param  ?string $format
230     * @param  bool    $secondsOnly
231     * @throws \Exception
232     * @return DateInterval|string
233     */
234    public static function getAverage(array $times, ?string $format = null, bool $secondsOnly = false): DateInterval|string
235    {
236        $total       = static::getTotal($times, null, true);
237        $totalTime   = $total->s;
238        $averageTime = round(($totalTime / count($times)), 2);
239        $hh          = 0;
240        $mm          = 0;
241        $ss          = 0;
242
243        if ($averageTime >= 3600) {
244            $hh   = floor($averageTime / 3600);
245            $mins = $averageTime - ($hh * 3600);
246            $mm   = floor($mins / 60);
247            $ss   = (int)($mins - ($mm * 60)) % 60;
248        } else if ($averageTime >= 60) {
249            $mm = floor($averageTime / 60);
250            $ss = ((int)$averageTime % 60);
251        } else {
252            $ss = $averageTime;
253        }
254
255        if ($secondsOnly) {
256            $totalSeconds   = 0;
257            $totalSeconds  += (int)$hh * 3600;
258            $totalSeconds  += (int)$mm * 60;
259            $intervalFormat = 'PT' . $totalSeconds . 'S';
260        } else {
261            $intervalFormat = 'PT';
262            if ($hh != 0) {
263                $intervalFormat .= (int)$hh . 'H';
264            }
265            if ($mm != 0) {
266                $intervalFormat .= (int)$mm . 'M';
267            }
268            if ($ss != 0) {
269                $intervalFormat .= (int)$ss . 'S';
270            }
271        }
272
273        $dateInterval = new DateInterval($intervalFormat);
274
275        return ($format !== null) ? $dateInterval->format($format) : $dateInterval;
276    }
277
278    /**
279     * Method to get dates of a week
280     *
281     * @param  ?int    $week
282     * @param  ?int    $year
283     * @param  ?string $format
284     * @return array
285     */
286    public static function getWeekDates(?int $week = null, ?int $year = null, ?string $format = null): array
287    {
288        if ($week === null) {
289            $week = date('W');
290        }
291        if ($year === null) {
292            $year = date('Y');
293        }
294
295        $today     = new static('today');
296        $sunday    = clone $today->setISODate($year, $week, 0);
297        $monday    = clone $today->setISODate($year, $week, 1);
298        $tuesday   = clone $today->setISODate($year, $week, 2);
299        $wednesday = clone $today->setISODate($year, $week, 3);
300        $thursday  = clone $today->setISODate($year, $week, 4);
301        $friday    = clone $today->setISODate($year, $week, 5);
302        $saturday  = clone $today->setISODate($year, $week, 6);
303
304        if ($format !== null) {
305            $weekDates = [
306                0 => $sunday->format($format),
307                1 => $monday->format($format),
308                2 => $tuesday->format($format),
309                3 => $wednesday->format($format),
310                4 => $thursday->format($format),
311                5 => $friday->format($format),
312                6 => $saturday->format($format),
313            ];
314        } else {
315            $weekDates = [
316                0 => $sunday,
317                1 => $monday,
318                2 => $tuesday,
319                3 => $wednesday,
320                4 => $thursday,
321                5 => $friday,
322                6 => $saturday,
323            ];
324        }
325
326        return $weekDates;
327    }
328
329    /**
330     * __toString method
331     *
332     * @return string
333     */
334    public function __toString(): string
335    {
336        $string = '';
337
338        if (!empty($this->defaultDateFormat)) {
339            $format = $this->defaultDateFormat;
340
341            if (!empty($this->defaultTimeFormat)) {
342                $format .= ' ' . $this->defaultTimeFormat;
343            }
344
345            $string = $this->format($format);
346        }
347
348        return $string;
349    }
350
351}
352