Code Coverage
 
Lines
Functions and Methods
Classes and Traits
Total
100.00% covered (success)
100.00%
120 / 120
100.00% covered (success)
100.00%
12 / 12
CRAP
100.00% covered (success)
100.00%
1 / 1
DateTime
100.00% covered (success)
100.00%
120 / 120
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%
10 / 10
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        // Default to U.S.-based DST
149        if (($dstStart === null) || ($dstEnd === null)) {
150            $dstStart = strtotime('Second Sunday of March 2AM');
151            $dstEnd   = strtotime('First Sunday of November 2AM');
152        }
153
154        if ($dateTime === null) {
155            $dateTime = time();
156        } else if (!is_numeric($dateTime)) {
157            $dateTime = strtotime($dateTime);
158            if ($dateTime === false) {
159                throw new \InvalidArgumentException('Error: Invalid date-time parameter.');
160            }
161        }
162
163        return (($dateTime > $dstStart) && ($dateTime < $dstEnd));
164    }
165
166    /**
167     * Method to get total time from array of multiple time values in HH:MM:SS format
168     *
169     * Standard hh:mm:ss format string is '%H:%I:%S'
170     *
171     * @param  array $times
172     * @param  ?string $format
173     * @param  bool $secondsOnly
174     * @throws \Exception
175     * @return DateInterval|string
176     */
177    public static function getTotal(array $times, ?string $format = null, bool $secondsOnly = false): DateInterval|string
178    {
179        $totalHours   = 0;
180        $totalMinutes = 0;
181        $totalSeconds = 0;
182
183        foreach ($times as $time) {
184            if ($time instanceof \DateInterval) {
185                $hours   = $time->format('%h');
186                $minutes = $time->format('%i');
187                $seconds = $time->format('%s');
188            } else {
189                if (substr_count($time, ':') == 2) {
190                    [$hours, $minutes, $seconds] = explode(':', $time);
191                } else {
192                    $hours = 0;
193                    [$minutes, $seconds] = explode(':', $time);
194                }
195            }
196            $totalHours   += (int)$hours;
197            $totalMinutes += (int)$minutes;
198            $totalSeconds += (int)$seconds;
199        }
200
201        if ($secondsOnly) {
202            $totalSeconds  += (int)$totalHours * 3600;
203            $totalSeconds  += (int)$totalMinutes * 60;
204            $intervalFormat = 'PT' . $totalSeconds . 'S';
205        } else {
206            if ($totalSeconds > 60) {
207                $totalMinutes += floor($totalSeconds / 60);
208                $totalSeconds  = $totalSeconds % 60;
209            }
210            if ($totalMinutes > 60) {
211                $totalHours  += floor($totalMinutes / 60);
212                $totalMinutes = $totalMinutes % 60;
213            }
214            $intervalFormat = 'PT' . (int)$totalHours . 'H' . (int)$totalMinutes . 'M' . (int)$totalSeconds . 'S';
215        }
216
217        $dateInterval = new DateInterval($intervalFormat);
218
219        return ($format !== null) ? $dateInterval->format($format) : $dateInterval;
220    }
221
222    /**
223     * Method to get average time from array of multiple time values in HH:MM:SS format
224     *
225     * Standard hh:mm:ss format string is '%H:%I:%S'
226     *
227     * @param  array   $times
228     * @param  ?string $format
229     * @param  bool    $secondsOnly
230     * @throws \Exception
231     * @return DateInterval|string
232     */
233    public static function getAverage(array $times, ?string $format = null, bool $secondsOnly = false): DateInterval|string
234    {
235        $total       = static::getTotal($times, null, true);
236        $totalTime   = $total->s;
237        $averageTime = round(($totalTime / count($times)), 2);
238        $hh          = 0;
239        $mm          = 0;
240        $ss          = 0;
241
242        if ($averageTime >= 3600) {
243            $hh   = floor($averageTime / 3600);
244            $mins = $averageTime - ($hh * 3600);
245            $mm   = floor($mins / 60);
246            $ss   = (int)($mins - ($mm * 60)) % 60;
247        } else if ($averageTime >= 60) {
248            $mm = floor($averageTime / 60);
249            $ss = ((int)$averageTime % 60);
250        } else {
251            $ss = $averageTime;
252        }
253
254        if ($secondsOnly) {
255            $totalSeconds   = 0;
256            $totalSeconds  += (int)$hh * 3600;
257            $totalSeconds  += (int)$mm * 60;
258            $intervalFormat = 'PT' . $totalSeconds . 'S';
259        } else {
260            $intervalFormat = 'PT';
261            if ($hh != 0) {
262                $intervalFormat .= (int)$hh . 'H';
263            }
264            if ($mm != 0) {
265                $intervalFormat .= (int)$mm . 'M';
266            }
267            if ($ss != 0) {
268                $intervalFormat .= (int)$ss . 'S';
269            }
270        }
271
272        $dateInterval = new DateInterval($intervalFormat);
273
274        return ($format !== null) ? $dateInterval->format($format) : $dateInterval;
275    }
276
277    /**
278     * Method to get dates of a week
279     *
280     * @param  ?int    $week
281     * @param  ?int    $year
282     * @param  ?string $format
283     * @return array
284     */
285    public static function getWeekDates(?int $week = null, ?int $year = null, ?string $format = null): array
286    {
287        if ($week === null) {
288            $week = date('W');
289        }
290        if ($year === null) {
291            $year = date('Y');
292        }
293
294        $today     = new static('today');
295        $sunday    = clone $today->setISODate($year, $week, 0);
296        $monday    = clone $today->setISODate($year, $week, 1);
297        $tuesday   = clone $today->setISODate($year, $week, 2);
298        $wednesday = clone $today->setISODate($year, $week, 3);
299        $thursday  = clone $today->setISODate($year, $week, 4);
300        $friday    = clone $today->setISODate($year, $week, 5);
301        $saturday  = clone $today->setISODate($year, $week, 6);
302
303        if ($format !== null) {
304            $weekDates = [
305                0 => $sunday->format($format),
306                1 => $monday->format($format),
307                2 => $tuesday->format($format),
308                3 => $wednesday->format($format),
309                4 => $thursday->format($format),
310                5 => $friday->format($format),
311                6 => $saturday->format($format),
312            ];
313        } else {
314            $weekDates = [
315                0 => $sunday,
316                1 => $monday,
317                2 => $tuesday,
318                3 => $wednesday,
319                4 => $thursday,
320                5 => $friday,
321                6 => $saturday,
322            ];
323        }
324
325        return $weekDates;
326    }
327
328    /**
329     * __toString method
330     *
331     * @return string
332     */
333    public function __toString(): string
334    {
335        $string = '';
336
337        if (!empty($this->defaultDateFormat)) {
338            $format = $this->defaultDateFormat;
339
340            if (!empty($this->defaultTimeFormat)) {
341                $format .= ' ' . $this->defaultTimeFormat;
342            }
343
344            $string = $this->format($format);
345        }
346
347        return $string;
348    }
349
350}
351