]> git.mxchange.org Git - friendica.git/blob - src/Util/DateTimeFormat.php
Juts another date format fix
[friendica.git] / src / Util / DateTimeFormat.php
1 <?php
2 /**
3  * @copyright Copyright (C) 2010-2022, 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($s = 'now', $tz_to = 'UTC', $tz_from = 'UTC', $format = self::MYSQL)
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                 $s = self::fixDateFormat($s);
139
140                 /*
141                  * Slight hackish adjustment so that 'zero' datetime actually returns what is intended
142                  * otherwise we end up with -0001-11-30 ...
143                  * add 32 days so that we at least get year 00, and then hack around the fact that
144                  * months and days always start with 1.
145                  */
146                 if (substr($s, 0, 10) <= '0001-01-01') {
147                         if ($s < '0000-00-00') {
148                                 $s = '0000-00-00';
149                         }
150                         $d = new DateTime($s . ' + 32 days', new DateTimeZone('UTC'));
151                         return str_replace('1', '0', $d->format($format));
152                 }
153
154                 try {
155                         $from_obj = new DateTimeZone($tz_from);
156                 } catch (Exception $e) {
157                         $from_obj = new DateTimeZone('UTC');
158                 }
159
160                 try {
161                         $d = new DateTime($s, $from_obj);
162                 } catch (Exception $e) {
163                         Logger::warning('DateTimeFormat::convert: exception: ' . $e->getMessage());
164                         $d = new DateTime('now', $from_obj);
165                 }
166
167                 try {
168                         $to_obj = new DateTimeZone($tz_to);
169                 } catch (Exception $e) {
170                         $to_obj = new DateTimeZone('UTC');
171                 }
172
173                 $d->setTimezone($to_obj);
174
175                 return $d->format($format);
176         }
177
178         /**
179          * Fix weird date formats
180          *
181          * @param string $dateString
182          * @return string
183          */
184         private static function fixDateFormat(string $dateString): string
185         {
186                 $patterns = [
187                         ['#(\w+), (\d+/\d+/\d+) - (\d+:\d+)#', '$1, $2 $3'],
188                         ['#(\d+-\d+-\d+)T(\d+:\d+:\d+)ZZ#', '$1T$2Z'],
189                         ['#(\d+-\d+-\d+)T(\d+:\d+:\d+\.\d+)ZZ#', '$1T$2Z'],
190                         ['#(\w+), (\d+ \w+ \d+) (\d+:\d+:\d+) (.+)#', '$2 $3 $4'],
191                         ['#(\d+:\d+) (\w+), (\w+) (\d+), (\d+)#', '$1 $2 $3 $4 $5'],
192                         ['#(\w+ \d+, \d+) - (\d+:\d+)#', '$1, $2'],
193                 ];
194
195                 foreach ($patterns 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 }