]> git.mxchange.org Git - friendica.git/blob - src/Util/DateTimeFormat.php
bump version 2023.12
[friendica.git] / src / Util / DateTimeFormat.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2023, the Friendica project
4  *
5  * @license GNU AGPL version 3 or any later version
6  *
7  * This program is free software: you can redistribute it and/or modify
8  * it under the terms of the GNU Affero General Public License as
9  * published by the Free Software Foundation, either version 3 of the
10  * License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU Affero General Public License for more details.
16  *
17  * You should have received a copy of the GNU Affero General Public License
18  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
19  *
20  */
21
22 namespace Friendica\Util;
23
24 use Friendica\Core\Logger;
25 use DateTime;
26 use DateTimeZone;
27 use Exception;
28
29 /**
30  * Temporal class
31  */
32 class DateTimeFormat
33 {
34         const ATOM  = 'Y-m-d\TH:i:s\Z';
35         const MYSQL = 'Y-m-d H:i:s';
36         const HTTP  = 'D, d M Y H:i:s \G\M\T';
37         const JSON  = 'Y-m-d\TH:i:s.v\Z';
38         const API   = 'D M d H:i:s +0000 Y';
39
40         static $localTimezone = 'UTC';
41
42         public static function setLocalTimeZone(string $timezone)
43         {
44                 self::$localTimezone = $timezone;
45         }
46
47         /**
48          * convert() shorthand for UTC.
49          *
50          * @param string $time   A date/time string
51          * @param string $format DateTime format string or Temporal constant
52          * @return string
53          * @throws Exception
54          */
55         public static function utc(string $time, string $format = self::MYSQL): string
56         {
57                 return self::convert($time, 'UTC', 'UTC', $format);
58         }
59
60         /**
61          * convert() shorthand for local.
62          *
63          * @param string $time   A date/time string
64          * @param string $format DateTime format string or Temporal constant
65          * @return string
66          * @throws Exception
67          */
68         public static function local($time, $format = self::MYSQL)
69         {
70                 return self::convert($time, self::$localTimezone, 'UTC', $format);
71         }
72
73         /**
74          * convert() shorthand for timezoned now.
75          *
76          * @param        $timezone
77          * @param string $format DateTime format string or Temporal constant
78          * @return string
79          * @throws Exception
80          */
81         public static function timezoneNow($timezone, $format = self::MYSQL)
82         {
83                 return self::convert('now', $timezone, 'UTC', $format);
84         }
85
86         /**
87          * convert() shorthand for local now.
88          *
89          * @param string $format DateTime format string or Temporal constant
90          * @return string
91          * @throws Exception
92          */
93         public static function localNow($format = self::MYSQL)
94         {
95                 return self::local('now', $format);
96         }
97
98         /**
99          * convert() shorthand for UTC now.
100          *
101          * @param string $format DateTime format string or Temporal constant
102          * @return string
103          * @throws Exception
104          */
105         public static function utcNow(string $format = self::MYSQL): string
106         {
107                 return self::utc('now', $format);
108         }
109
110         /**
111          * General purpose date parse/convert/format function.
112          *
113          * @param string $s       Some parseable date/time string
114          * @param string $tz_to   Destination timezone
115          * @param string $tz_from Source timezone
116          * @param string $format  Output format recognised from php's DateTime class
117          *                        http://www.php.net/manual/en/datetime.format.php
118          *
119          * @return string Formatted date according to given format
120          * @throws Exception
121          */
122         public static function convert(string $s = 'now', string $tz_to = 'UTC', string $tz_from = 'UTC', string $format = self::MYSQL): string
123         {
124                 // Defaults to UTC if nothing is set, but throws an exception if set to empty string.
125                 // Provide some sane defaults regardless.
126                 if ($tz_from === '') {
127                         $tz_from = 'UTC';
128                 }
129
130                 if ($tz_to === '') {
131                         $tz_to = 'UTC';
132                 }
133
134                 if (($s === '') || (!is_string($s))) {
135                         $s = 'now';
136                 }
137
138                 // Lowest possible datetime value
139                 if (substr($s, 0, 10) <= '0001-01-01') {
140                         $d = new DateTime('now', new DateTimeZone('UTC'));
141                         $d->setDate(1, 1, 1)->setTime(0, 0);
142                         return $d->format($format);
143                 }
144
145                 try {
146                         $from_obj = new DateTimeZone($tz_from);
147                 } catch (Exception $e) {
148                         $from_obj = new DateTimeZone('UTC');
149                 }
150
151                 try {
152                         $d = new DateTime($s, $from_obj);
153                 } catch (Exception $e) {
154                         try {
155                                 $d = new DateTime(self::fix($s), $from_obj);
156                         } catch (\Throwable $e) {
157                                 Logger::warning('DateTimeFormat::convert: exception: ' . $e->getMessage());
158                                 $d = new DateTime('now', $from_obj);
159                         }
160                 }
161
162                 try {
163                         $to_obj = new DateTimeZone($tz_to);
164                 } catch (Exception $e) {
165                         $to_obj = new DateTimeZone('UTC');
166                 }
167
168                 $d->setTimezone($to_obj);
169
170                 return $d->format($format);
171         }
172
173         /**
174          * Fix weird date formats.
175          *
176          * Note: This method isn't meant to sanitize valid date/time strings, for example it will mangle relative date
177          * strings like "now - 3 days".
178          *
179          * @see \Friendica\Test\src\Util\DateTimeFormatTest::dataFix() for a list of examples handled by this method.
180          * @param string $dateString
181          * @return string
182          */
183         public static function fix(string $dateString): string
184         {
185                 $search  = ['Mär', 'März', 'Mai', 'Juni', 'Juli', 'Okt', 'Dez', 'ET' , 'ZZ', ' - ', '&#x2B;', '&amp;#43;', ' (Coordinated Universal Time)', '\\'];
186                 $replace = ['Mar', 'Mar' , 'May', 'Jun' , 'Jul' , 'Oct', 'Dec', 'EST', 'Z' , ', ' , '+'     , '+'        , ''                             , ''];
187
188                 $dateString = str_replace($search, $replace, $dateString);
189
190                 $pregPatterns = [
191                         ['#(\w+), (\d+ \w+ \d+) (\d+:\d+:\d+) (.+)#', '$2 $3 $4'],
192                         ['#(\d+:\d+) (\w+), (\w+) (\d+), (\d+)#', '$1 $2 $3 $4 $5'],
193                 ];
194
195                 foreach ($pregPatterns as $pattern) {
196                         $dateString = preg_replace($pattern[0], $pattern[1], $dateString);
197                 }
198
199                 return $dateString;
200         }
201
202         /**
203          * Checks, if the given string is a date with the pattern YYYY-MM
204          *
205          * @param string $dateString The given date
206          *
207          * @return boolean True, if the date is a valid pattern
208          */
209         public function isYearMonth(string $dateString)
210         {
211                 // Check format (2019-01, 2019-1, 2019-10)
212                 if (!preg_match('/^([12]\d{3}-(1[0-2]|0[1-9]|\d))$/', $dateString)) {
213                         return false;
214                 }
215
216                 $date = DateTime::createFromFormat('Y-m', $dateString);
217
218                 if (!$date) {
219                         return false;
220                 }
221
222                 try {
223                         $now = new DateTime();
224                 } catch (\Throwable $t) {
225                         return false;
226                 }
227
228                 if ($date > $now) {
229                         return false;
230                 }
231
232                 return true;
233         }
234
235         /**
236          * Checks, if the given string is a date with the pattern YYYY-MM-DD
237          *
238          * @param string $dateString The given date
239          *
240          * @return boolean True, if the date is a valid pattern
241          */
242         public function isYearMonthDay(string $dateString)
243         {
244                 $date = DateTime::createFromFormat('Y-m-d', $dateString);
245                 if (!$date) {
246                         return false;
247                 }
248
249                 if (DateTime::getLastErrors()['error_count'] || DateTime::getLastErrors()['warning_count']) {
250                         return false;
251                 }
252
253                 return true;
254         }
255 }