Issues (84)

src/Microtime.php (1 issue)

Labels
Severity
1
<?php
2
/**
3
 * File containing the class {@see \AppUtils\Microtime}.
4
 *
5
 * @package Application Utils
6
 * @subpackage Microtime
7
 * @see \AppUtils\Microtime
8
 */
9
10
declare(strict_types=1);
11
12
namespace AppUtils;
13
14
use AppUtils\Microtime\DateFormatChars;
15
use DateTime;
16
use DateTimeZone;
17
use Exception;
18
19
/**
20
 * Microtime class that extends the vanilla `DateTime` object
21
 * with the capability to handle microseconds, as well as to
22
 * add a number of utility methods.
23
 *
24
 * @package Application Utils
25
 * @subpackage Microtime
26
 * @see https://www.php.net/manual/en/datetime.format.php
27
 */
28
class Microtime extends DateTime implements Interface_Stringable
29
{
30
    public const ERROR_FAILED_CREATING_DATE_OBJECT = 88601;
31
    public const ERROR_FAILED_CONVERTING_STRING = 88602;
32
    public const ERROR_INVALID_DATE_VALUE = 88603;
33
34
    public const DATETIME_NOW = 'now';
35
    public const FORMAT_ISO = 'Y-m-d H:i:s.u';
36
37
    /**
38
     * Attempts to determine the kind of date to create dynamically.
39
     * If you already know what type of date to create, use the `createXXX()`
40
     * methods instead, which perform slightly better.
41
     *
42
     * @param string|DateTime|Microtime|Microtime_ParseResult|mixed $datetime
43
     * @param DateTimeZone|null $timeZone
44
     * @throws Microtime_Exception
45
     *
46
     * @see Microtime::ERROR_FAILED_CREATING_DATE_OBJECT
47
     * @see Microtime::ERROR_FAILED_CONVERTING_STRING
48
     *
49
     * @see Microtime::createFromDate()
50
     * @see Microtime::createFromMicrotime()
51
     * @see Microtime::createFromString()
52
     * @see Microtime::createNow()
53
     */
54
    public function __construct($datetime=self::DATETIME_NOW, ?DateTimeZone $timeZone=null)
55
    {
56
        if($datetime instanceof Microtime_ParseResult)
57
        {
58
            $parsed = $datetime;
59
        }
60
        else
61
        {
62
            $parsed = $this->parseDate($datetime, $timeZone);
63
        }
64
65
        try
66
        {
67
            parent::__construct($parsed->getDateTime(), $parsed->getTimeZone());
68
        }
69
        catch (Exception $e)
70
        {
71
            throw new Microtime_Exception(
72
                'Failed to create date from string.',
73
                sprintf(
74
                    'Source date string: [%s].',
75
                    $datetime
0 ignored issues
show
It seems like $datetime can also be of type DateTime; however, parameter $values of sprintf() does only seem to accept double|integer|string, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

75
                    /** @scrutinizer ignore-type */ $datetime
Loading history...
76
                ),
77
                self::ERROR_FAILED_CONVERTING_STRING,
78
                $e
79
            );
80
        }
81
    }
82
83
    /**
84
     * @param string|DateTime|Microtime|mixed $datetime
85
     * @param DateTimeZone|null $timeZone
86
     * @return Microtime_ParseResult
87
     * @throws Microtime_Exception
88
     */
89
    private function parseDate($datetime, ?DateTimeZone $timeZone=null) : Microtime_ParseResult
90
    {
91
        if($datetime instanceof self)
92
        {
93
            return new Microtime_ParseResult(
94
                $datetime->getISODate(),
95
                $datetime->getTimezone()
96
            );
97
        }
98
99
        if($datetime instanceof DateTime)
100
        {
101
            return new Microtime_ParseResult(
102
                $datetime->format(self::FORMAT_ISO),
103
                $datetime->getTimezone()
104
            );
105
        }
106
107
        if($timeZone === null)
108
        {
109
            $timeZone = new DateTimeZone(date_default_timezone_get());
110
        }
111
112
        if(empty($datetime) || $datetime === self::DATETIME_NOW)
113
        {
114
            return self::parseNow($timeZone);
115
        }
116
117
        if(is_string($datetime))
118
        {
119
            return new Microtime_ParseResult(
120
                $datetime,
121
                $timeZone
122
            );
123
        }
124
125
        throw new Microtime_Exception(
126
            'Invalid date time value',
127
            sprintf(
128
                'The specified value is not a supported date value: [%s].',
129
                parseVariable($datetime)->enableType()->toString()
130
            ),
131
            self::ERROR_INVALID_DATE_VALUE
132
        );
133
    }
134
135
    /**
136
     * @param DateTimeZone $timeZone
137
     * @return Microtime_ParseResult
138
     * @throws Microtime_Exception
139
     */
140
    private static function parseNow(DateTimeZone $timeZone) : Microtime_ParseResult
141
    {
142
        $dateObj = DateTime::createFromFormat('0.u00 U', microtime(), new DateTimeZone('America/Denver'));
143
144
        if($dateObj !== false)
145
        {
146
            $dateObj->setTimezone($timeZone);
147
148
            return new Microtime_ParseResult(
149
                $dateObj->format(self::FORMAT_ISO),
150
                $timeZone
151
            );
152
        }
153
154
        throw new Microtime_Exception(
155
            'Failed to create microseconds date.',
156
            '',
157
            self::ERROR_FAILED_CREATING_DATE_OBJECT
158
        );
159
    }
160
161
    /**
162
     * Creates a new Microtime for the current time.
163
     *
164
     * @param DateTimeZone|null $timeZone
165
     * @return Microtime
166
     * @throws Microtime_Exception
167
     */
168
    public static function createNow(?DateTimeZone $timeZone=null) : Microtime
169
    {
170
        if($timeZone === null)
171
        {
172
            $timeZone = new DateTimeZone(date_default_timezone_get());
173
        }
174
175
        return new Microtime(self::parseNow($timeZone));
176
    }
177
178
    /**
179
     * Creates a microtime from a date string. For the microseconds
180
     * to be used, the string must be in a supported format.
181
     *
182
     * @param string $date
183
     * @param DateTimeZone|null $timeZone
184
     * @return Microtime
185
     * @throws Microtime_Exception
186
     */
187
    public static function createFromString(string $date, ?DateTimeZone $timeZone=null) : Microtime
188
    {
189
        if($timeZone === null)
190
        {
191
            $timeZone = new DateTimeZone(date_default_timezone_get());
192
        }
193
194
        return new Microtime(new Microtime_ParseResult($date, $timeZone));
195
    }
196
197
    /**
198
     * Creates a new Microtime instance given an existing Microtime instance.
199
     * The time zone is inherited.
200
     *
201
     * @param Microtime $date
202
     * @return Microtime
203
     * @throws Microtime_Exception
204
     */
205
    public static function createFromMicrotime(Microtime $date) : Microtime
206
    {
207
        return new Microtime(new Microtime_ParseResult($date->getISODate(), $date->getTimezone()));
208
    }
209
210
    /**
211
     * Creates a microtime instance from an existing DateTime instance.
212
     * The Microtime inherits the time zone.
213
     *
214
     * @param DateTime $date
215
     * @return Microtime
216
     * @throws Microtime_Exception
217
     */
218
    public static function createFromDate(DateTime $date) : Microtime
219
    {
220
        return new Microtime(new Microtime_ParseResult($date->format(self::FORMAT_ISO), $date->getTimezone()));
221
    }
222
223
    /**
224
     * Gets the microseconds part of the date.
225
     * @return int Six-digit microseconds value.
226
     */
227
    public function getMicroseconds() : int
228
    {
229
        return (int)$this->format(DateFormatChars::TIME_MICROSECONDS);
230
    }
231
232
    /**
233
     * ISO formatted date with microseconds, in the
234
     * format `Y-m-d H:i:s.u`.
235
     *
236
     * @return string
237
     */
238
    public function getISODate() : string
239
    {
240
        return $this->format(self::FORMAT_ISO);
241
    }
242
243
    /**
244
     * Date formatted for storing in a MySQL database column.
245
     *
246
     * NOTE: To store microseconds in MySQL, a DateTime column
247
     * needs to be used, with a length of 6 (3 for the milliseconds,
248
     * +3 for the microseconds). Without the length specified,
249
     * the milliseconds information will be stripped out.
250
     *
251
     * @return string
252
     */
253
    public function getMySQLDate() : string
254
    {
255
        return $this->getISODate();
256
    }
257
258
    public function __toString()
259
    {
260
        return $this->getISODate();
261
    }
262
263
    public function getYear() : int
264
    {
265
        return (int)$this->format(DateFormatChars::YEAR);
266
    }
267
268
    public function getMonth() : int
269
    {
270
        return (int)$this->format(DateFormatChars::MONTH);
271
    }
272
273
    public function getDay() : int
274
    {
275
        return (int)$this->format(DateFormatChars::DAY_OF_MONTH);
276
    }
277
278
    public function getHour24() : int
279
    {
280
        return (int)$this->format(DateFormatChars::TIME_24_ZB);
281
    }
282
283
    public function getHour12() : int
284
    {
285
        return (int)$this->format(DateFormatChars::TIME_12);
286
    }
287
288
    public function isAM() : bool
289
    {
290
        return $this->getMeridiem() === 'am';
291
    }
292
293
    public function isPM() : bool
294
    {
295
        return $this->getMeridiem() === 'pm';
296
    }
297
298
    /**
299
     * String identifying whether the time is ante meridiem (AM) or post meridiem (PM).
300
     * @return string `am` or `pm`
301
     */
302
    public function getMeridiem() : string
303
    {
304
        return $this->format(DateFormatChars::TIME_MERIDIEM_LOWER);
305
    }
306
307
    public function getMinutes() : int
308
    {
309
        return (int)$this->format(DateFormatChars::TIME_MINUTES_LZ);
310
    }
311
312
    public function getSeconds() : int
313
    {
314
        return (int)$this->format(DateFormatChars::TIME_SECONDS_LZ);
315
    }
316
}
317