Passed
Push — master ( 5b74be...84d61d )
by Eric
18:51 queued 06:03
created

Dates   A

Complexity

Total Complexity 13

Size/Duplication

Total Lines 132
Duplicated Lines 0 %

Test Coverage

Coverage 100%

Importance

Changes 0
Metric Value
eloc 40
dl 0
loc 132
ccs 45
cts 45
cp 1
rs 10
c 0
b 0
f 0
wmc 13
1
<?php
2
3
declare(strict_types=1);
4
5
/**
6
 * Utility - Collection of various PHP utility functions.
7
 *
8
 * @author    Eric Sizemore <[email protected]>
9
 * @version   2.0.0
10
 * @copyright (C) 2017 - 2024 Eric Sizemore
11
 * @license   The MIT License (MIT)
12
 *
13
 * Copyright (C) 2017 - 2024 Eric Sizemore <https://www.secondversion.com>.
14
 *
15
 * Permission is hereby granted, free of charge, to any person obtaining a copy
16
 * of this software and associated documentation files (the "Software"), to
17
 * deal in the Software without restriction, including without limitation the
18
 * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
19
 * sell copies of the Software, and to permit persons to whom the Software is
20
 * furnished to do so, subject to the following conditions:
21
 *
22
 * The above copyright notice and this permission notice shall be included in
23
 * all copies or substantial portions of the Software.
24
 *
25
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
26
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
27
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
28
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
29
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
30
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
31
 * THE SOFTWARE.
32
 */
33
34
namespace Esi\Utility;
35
36
// Exceptions
37
use Exception;
38
use InvalidArgumentException;
39
use RuntimeException;
40
41
// Classes
42
use DateTimeZone;
43
use DateTime;
44
45
// Functions
46
use function ceil;
47
use function time;
48
49
/**
50
 * Date utilities.
51
 */
52
final class Dates
53
{
54
    /**
55
     * Regex used to validate a given timestamp.
56
     *
57
     * @var string VALIDATE_TIMESTAMP_REGEX
58
     */
59
    public const VALIDATE_TIMESTAMP_REGEX = '/^\d{8,11}$/';
60
61
    /**
62
     * timeDifference()
63
     *
64
     * Formats the difference between two timestamps to be human-readable.
65
     *
66
     * @param   int     $timestampFrom  Starting unix timestamp.
67
     * @param   int     $timestampTo    Ending unix timestamp.
68
     * @param   string  $timezone       The timezone to use. Must be a valid timezone:
69
     *                                  {@see http://www.php.net/manual/en/timezones.php}
70
     * @param   string  $append         The string to append to the difference.
71
     *
72
     * @throws  InvalidArgumentException|RuntimeException|Exception
73
     */
74 3
    public static function timeDifference(int $timestampFrom, int $timestampTo = 0, string $timezone = 'UTC', string $append = ' old'): string
75
    {
76 3
        if ($timezone === '') {
77 1
            $timezone = 'UTC';
78
        }
79
80 3
        if (!Dates::validTimezone($timezone)) {
81 1
            throw new RuntimeException('$timezone appears to be invalid.');
82
        }
83
84
        // Normalize timestamps
85 2
        $timestampTo   = (Dates::validateTimestamp($timestampTo)) ? $timestampTo : time();
86 2
        $timestampFrom = (Dates::validateTimestamp($timestampFrom)) ? $timestampFrom : time();
87
88
        // This will generally only happen if the $timestampFrom was 0, or if it's invalid, as it is set to time();
89
        // as is $timestampTo if left at 0
90 2
        if ($timestampFrom >= $timestampTo) {
91 1
            throw new InvalidArgumentException('$timestampFrom needs to be less than $timestampTo.');
92
        }
93
94
        // Create DateTime objects and set timezone
95 1
        $timestampFrom = (new DateTime('@' . $timestampFrom))->setTimezone(new DateTimeZone($timezone));
96 1
        $timestampTo   = (new DateTime('@' . $timestampTo))->setTimezone(new DateTimeZone($timezone));
97
98
        // Calculate difference
99 1
        $difference = $timestampFrom->diff($timestampTo);
100
101
        // Format the difference
102 1
        $string = match (true) {
103 1
            $difference->y > 0  => $difference->y . ' year(s)',
104 1
            $difference->m > 0  => $difference->m . ' month(s)',
105 1
            $difference->d >= 7 => ceil($difference->d / 7) . ' week(s)',
106 1
            $difference->d > 0  => $difference->d . ' day(s)',
107 1
            $difference->h > 0  => $difference->h . ' hour(s)',
108 1
            $difference->i > 0  => $difference->i . ' minute(s)',
109 1
            $difference->s > 0  => $difference->s . ' second(s)',
110 1
            default             => ''
111 1
        };
112
113 1
        return $string . $append;
114
    }
115
116
    /**
117
     * timezoneInfo()
118
     *
119
     * Retrieves information about a timezone.
120
     *
121
     * Note: Must be a valid timezone recognized by PHP.
122
     *
123
     * @see http://www.php.net/manual/en/timezones.php
124
     *
125
     * @param   string  $timezone  The timezone to return information for.
126
     * @return  array<string, bool|float|int|string|null>
127
     *
128
     * @throws  InvalidArgumentException|RuntimeException|Exception
129
     */
130 1
    public static function timezoneInfo(string $timezone = 'UTC'): array
131
    {
132 1
        if ($timezone === '') {
133 1
            $timezone = 'UTC';
134
        }
135
136 1
        if (!Dates::validTimezone($timezone)) {
137 1
            throw new RuntimeException('$timezone appears to be invalid.');
138
        }
139
140 1
        $dateTimeZone = new DateTimeZone($timezone);
141
142 1
        $location = $dateTimeZone->getLocation();
143
144 1
        return [
145 1
            'offset'    => $dateTimeZone->getOffset(new DateTime('now', new DateTimeZone('GMT'))) / 3_600,
1 ignored issue
show
Bug introduced by
A parse error occurred: Syntax error, unexpected T_STRING, expecting ',' or ']' on line 145 at column 101
Loading history...
146 1
            'country'   => $location['country_code'] ?? 'N/A',
147 1
            'latitude'  => $location['latitude'] ?? 'N/A',
148 1
            'longitude' => $location['longitude'] ?? 'N/A',
149 1
            'dst'       => $dateTimeZone->getTransitions(time(), time())[0]['isdst'] ?? null,
150 1
        ];
151
    }
152
153
    /**
154
     * Determines if a given timezone is valid, according to
155
     * {@link http://www.php.net/manual/en/timezones.php}.
156
     *
157
     * @param   string  $timezone  The timezone to validate.
158
     */
159 5
    public static function validTimezone(string $timezone): bool
160
    {
161 5
        static $validTimezones;
162
163 5
        $validTimezones ??= DateTimeZone::listIdentifiers();
164 5
        return Arrays::exists($validTimezones, $timezone);
165
    }
166
167
    /**
168
     * Determines if a given timestamp matches the valid range that is typically
169
     * found in a unix timestamp (at least in PHP).
170
     *
171
     * Typically, a timestamp for PHP can be valid if it is either 0 or between 8 and 11 digits in length.
172
     *
173
     * @param   int   $timestamp  The timestamp to validate.
174
     */
175 2
    public static function validateTimestamp(int $timestamp): bool
176
    {
177 2
        if ($timestamp === 0 || $timestamp < 0) {
178 2
            return false;
179
        }
180 1
        return (preg_match(self::VALIDATE_TIMESTAMP_REGEX, (string) $timestamp) === 1);
181
    }
182
}
183