Passed
Push — master ( f8e3f7...025a78 )
by Sebastian
03:51
created

Microtime::createNow()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 8
Code Lines 3

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 3
c 1
b 0
f 0
dl 0
loc 8
rs 10
cc 2
nc 2
nop 1
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 DateTime;
15
use DateTimeZone;
16
use Exception;
17
18
/**
19
 * Microtime class that extends the vanilla `DateTime` object
20
 * with the capability to handle microseconds, as well as to
21
 * add a number of utility methods.
22
 *
23
 * @package Application Utils
24
 * @subpackage Microtime
25
 * @see https://www.php.net/manual/en/datetime.format.php
26
 */
27
class Microtime extends DateTime implements Interface_Stringable
28
{
29
    const ERROR_FAILED_CREATING_DATE_OBJECT = 88601;
30
    const ERROR_FAILED_CONVERTING_STRING = 88602;
31
    const ERROR_INVALID_DATE_VALUE = 88603;
32
33
    const DATETIME_NOW = 'now';
34
    const FORMAT_ISO = 'Y-m-d H:i:s.u';
35
36
    // region: Format character constants
37
38
    /**
39
     * Day of the month without leading zeros
40
     */
41
    const CHAR_DAY_OF_MONTH = 'j';
42
43
    /**
44
     * Day of the month with leading zeros
45
     */
46
    const CHAR_DAY_OF_MONTH_LZ = 'd';
47
48
    /**
49
     * Day of the month as name, `Mon` through `Sun`
50
     */
51
    const CHAR_DAY_NAME_SHORT = 'D';
52
53
    /**
54
     * `Monday` through `Saturday`
55
     */
56
    const CHAR_DAY_NAME_LONG = 'l';
57
58
    /**
59
     * One-based day of the week (1=Monday, 7=Sunday)
60
     */
61
    const CHAR_DAY_OF_WEEK = 'N';
62
63
    /**
64
     * Zero-Based day of the week (0=Sunday, 6=Saturday)
65
     */
66
    const CHAR_DAY_OF_WEEK_ZB = 'w';
67
68
    /**
69
     * English ordinal suffix for the day of the month (th, nd...)
70
     */
71
    const CHAR_DAY_ORDINAL_SUFFIX = 'S';
72
73
    /**
74
     * Month number with leading zeros
75
     */
76
    const CHAR_MONTH_LZ = 'm';
77
78
    /**
79
     * Month number without leading zeros
80
     */
81
    const CHAR_MONTH = 'n';
82
83
    /**
84
     * Month name, three-letter short variant.
85
     */
86
    const CHAR_MONTH_NAME_SHORT = 'M';
87
88
    /**
89
     * Month name, full length.
90
     */
91
    const CHAR_MONTH_NAME_LONG = 'F';
92
93
    /**
94
     * 28 through 31
95
     */
96
    const CHAR_AMOUNT_DAYS_IN_MONTH = 't';
97
98
    // endregion
99
100
    /**
101
     * Attempts to determine the kind of date to create dynamically.
102
     * If you already know what type of date to create, use the `createXXX()`
103
     * methods instead, which perform slightly better.
104
     *
105
     * @param string|DateTime|Microtime|Microtime_ParseResult|mixed $datetime
106
     * @param DateTimeZone|null $timeZone
107
     * @throws Microtime_Exception
108
     *
109
     * @see Microtime::ERROR_FAILED_CREATING_DATE_OBJECT
110
     * @see Microtime::ERROR_FAILED_CONVERTING_STRING
111
     *
112
     * @see Microtime::createFromDate()
113
     * @see Microtime::createFromMicrotime()
114
     * @see Microtime::createFromString()
115
     * @see Microtime::createNow()
116
     */
117
    public function __construct($datetime=self::DATETIME_NOW, ?DateTimeZone $timeZone=null)
118
    {
119
        if($datetime instanceof Microtime_ParseResult)
120
        {
121
            $parsed = $datetime;
122
        }
123
        else
124
        {
125
            $parsed = $this->parseDate($datetime, $timeZone);
126
        }
127
128
        try
129
        {
130
            parent::__construct($parsed->getDateTime(), $parsed->getTimeZone());
131
        }
132
        catch (Exception $e)
133
        {
134
            throw new Microtime_Exception(
135
                'Failed to create date from string.',
136
                sprintf(
137
                    'Source date string: [%s].',
138
                    strval($datetime)
139
                ),
140
                self::ERROR_FAILED_CONVERTING_STRING
141
            );
142
        }
143
    }
144
145
    /**
146
     * @param string|DateTime|Microtime|mixed $datetime
147
     * @param DateTimeZone|null $timeZone
148
     * @return Microtime_ParseResult
149
     * @throws Microtime_Exception
150
     */
151
    private function parseDate($datetime, ?DateTimeZone $timeZone=null) : Microtime_ParseResult
152
    {
153
        if($datetime instanceof Microtime)
154
        {
155
            return new Microtime_ParseResult(
156
                $datetime->getISODate(),
157
                $datetime->getTimezone()
158
            );
159
        }
160
161
        if($datetime instanceof DateTime)
162
        {
163
            return new Microtime_ParseResult(
164
                $datetime->format(self::FORMAT_ISO),
165
                $datetime->getTimezone()
166
            );
167
        }
168
169
        if($timeZone === null)
170
        {
171
            $timeZone = new DateTimeZone(date_default_timezone_get());
172
        }
173
174
        if(empty($datetime) || $datetime === self::DATETIME_NOW)
175
        {
176
            return self::parseNow($timeZone);
177
        }
178
179
        if(is_string($datetime))
180
        {
181
            return new Microtime_ParseResult(
182
                $datetime,
183
                $timeZone
184
            );
185
        }
186
187
        throw new Microtime_Exception(
188
            'Invalid date time value',
189
            sprintf(
190
                'The specified value is not a supported date value: [%s].',
191
                parseVariable($datetime)->enableType()->toString()
192
            ),
193
            self::ERROR_INVALID_DATE_VALUE
194
        );
195
    }
196
197
    /**
198
     * @param DateTimeZone $timeZone
199
     * @return Microtime_ParseResult
200
     * @throws Microtime_Exception
201
     */
202
    private static function parseNow(DateTimeZone $timeZone) : Microtime_ParseResult
203
    {
204
        $dateObj = DateTime::createFromFormat('0.u00 U', microtime(), new DateTimeZone('America/Denver'));
205
206
        if($dateObj !== false)
207
        {
208
            $dateObj->setTimezone($timeZone);
209
210
            return new Microtime_ParseResult(
211
                $dateObj->format(self::FORMAT_ISO),
212
                $timeZone
213
            );
214
        }
215
216
        throw new Microtime_Exception(
217
            'Failed to create microseconds date.',
218
            '',
219
            self::ERROR_FAILED_CREATING_DATE_OBJECT
220
        );
221
    }
222
223
    /**
224
     * Creates a new Microtime for the current time.
225
     *
226
     * @param DateTimeZone|null $timeZone
227
     * @return Microtime
228
     * @throws Microtime_Exception
229
     */
230
    public static function createNow(?DateTimeZone $timeZone=null) : Microtime
231
    {
232
        if($timeZone === null)
233
        {
234
            $timeZone = new DateTimeZone(date_default_timezone_get());
235
        }
236
237
        return new Microtime(self::parseNow($timeZone));
238
    }
239
240
    /**
241
     * Creates a microtime from a date string. For the microseconds
242
     * to be used, the string must be in a supported format.
243
     *
244
     * @param string $date
245
     * @param DateTimeZone|null $timeZone
246
     * @return Microtime
247
     * @throws Microtime_Exception
248
     */
249
    public static function createFromString(string $date, ?DateTimeZone $timeZone=null) : Microtime
250
    {
251
        if($timeZone === null)
252
        {
253
            $timeZone = new DateTimeZone(date_default_timezone_get());
254
        }
255
256
        return new Microtime(new Microtime_ParseResult($date, $timeZone));
257
    }
258
259
    /**
260
     * Creates a new Microtime instance given an existing Microtime instance.
261
     * The time zone is inherited.
262
     *
263
     * @param Microtime $date
264
     * @return Microtime
265
     * @throws Microtime_Exception
266
     */
267
    public static function createFromMicrotime(Microtime $date) : Microtime
268
    {
269
        return new Microtime(new Microtime_ParseResult($date->getISODate(), $date->getTimezone()));
270
    }
271
272
    /**
273
     * Creates a microtime instance from an existing DateTime instance.
274
     * The Microtime inherits the time zone.
275
     *
276
     * @param DateTime $date
277
     * @return Microtime
278
     * @throws Microtime_Exception
279
     */
280
    public static function createFromDate(DateTime $date) : Microtime
281
    {
282
        return new Microtime(new Microtime_ParseResult($date->format(self::FORMAT_ISO), $date->getTimezone()));
283
    }
284
285
    /**
286
     * Gets the microseconds part of the date.
287
     * @return int Six-digit microseconds value.
288
     */
289
    public function getMicroseconds() : int
290
    {
291
        return intval($this->format('u'));
292
    }
293
294
    /**
295
     * ISO formatted date with microseconds, in the
296
     * format `Y-m-d H:i:s.u`.
297
     *
298
     * @return string
299
     */
300
    public function getISODate() : string
301
    {
302
        return $this->format(self::FORMAT_ISO);
303
    }
304
305
    /**
306
     * Date formatted for storing in a MySQL database column.
307
     *
308
     * NOTE: To store microseconds in MySQL, a DateTime column
309
     * needs to be used, with a length of 6 (3 for the milliseconds,
310
     * +3 for the microseconds). Without the length specified,
311
     * the milliseconds information will be stripped out.
312
     *
313
     * @return string
314
     */
315
    public function getMySQLDate() : string
316
    {
317
        return $this->getISODate();
318
    }
319
320
    public function __toString()
321
    {
322
        return $this->getISODate();
323
    }
324
325
    public function getYear() : int
326
    {
327
        return intval($this->format('Y'));
328
    }
329
330
    public function getMonth() : int
331
    {
332
        return intval($this->format('m'));
333
    }
334
335
    public function getDay() : int
336
    {
337
        return intval($this->format('d'));
338
    }
339
340
    public function getHour24() : int
341
    {
342
        return intval($this->format('H'));
343
    }
344
345
    public function getHour12() : int
346
    {
347
        return intval($this->format('h'));
348
    }
349
350
    public function isAM() : bool
351
    {
352
        return $this->getMeridiem() === 'am';
353
    }
354
355
    public function isPM() : bool
356
    {
357
        return $this->getMeridiem() === 'pm';
358
    }
359
360
    /**
361
     * String identifying whether the time is ante meridiem (AM) or post meridiem (PM).
362
     * @return string `am` or `pm`
363
     */
364
    public function getMeridiem() : string
365
    {
366
        return $this->format('a');
367
    }
368
369
    public function getMinutes() : int
370
    {
371
        return intval($this->format('i'));
372
    }
373
374
    public function getSeconds() : int
375
    {
376
        return intval($this->format('s'));
377
    }
378
}
379