]> git.mxchange.org Git - friendica.git/blob - src/Util/DateTimeFormat.php
Merge pull request #11973 from MrPetovan/task/test-fixDateFormat
[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::fix($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          * @see \Friendica\Test\src\Util\DateTimeFormatTest::dataFix() for a list of examples handled by this method.
182          * @param string $dateString
183          * @return string
184          */
185         public static function fix(string $dateString): string
186         {
187                 $patterns = [
188                         ['#(\w+), (\d+/\d+/\d+) - (\d+:\d+)#', '$1, $2 $3'],
189                         ['#(\d+-\d+-\d+)T(\d+:\d+:\d+)ZZ#', '$1T$2Z'],
190                         ['#(\d+-\d+-\d+)T(\d+:\d+:\d+\.\d+)ZZ#', '$1T$2Z'],
191                         ['#(\w+), (\d+ \w+ \d+) (\d+:\d+:\d+) (.+)#', '$2 $3 $4'],
192                         ['#(\d+:\d+) (\w+), (\w+) (\d+), (\d+)#', '$1 $2 $3 $4 $5'],
193                         ['#(\w+ \d+, \d+) - (\d+:\d+)#', '$1, $2'],
194                         ['~(\d+-\d+-\d+)T(\d+:\d+:\d+)&#x2B;(\d+:\d+)~', '$1T$2+$3'],
195                 ];
196
197                 foreach ($patterns as $pattern) {
198                         $dateString = preg_replace($pattern[0], $pattern[1], $dateString);
199                 }
200
201                 return $dateString;
202         }
203
204         /**
205          * Checks, if the given string is a date with the pattern YYYY-MM
206          *
207          * @param string $dateString The given date
208          *
209          * @return boolean True, if the date is a valid pattern
210          */
211         public function isYearMonth(string $dateString)
212         {
213                 // Check format (2019-01, 2019-1, 2019-10)
214                 if (!preg_match('/^([12]\d{3}-(1[0-2]|0[1-9]|\d))$/', $dateString)) {
215                         return false;
216                 }
217
218                 $date = DateTime::createFromFormat('Y-m', $dateString);
219
220                 if (!$date) {
221                         return false;
222                 }
223
224                 try {
225                         $now = new DateTime();
226                 } catch (\Throwable $t) {
227                         return false;
228                 }
229
230                 if ($date > $now) {
231                         return false;
232                 }
233
234                 return true;
235         }
236
237         /**
238          * Checks, if the given string is a date with the pattern YYYY-MM-DD
239          *
240          * @param string $dateString The given date
241          *
242          * @return boolean True, if the date is a valid pattern
243          */
244         public function isYearMonthDay(string $dateString)
245         {
246                 $date = DateTime::createFromFormat('Y-m-d', $dateString);
247                 if (!$date) {
248                         return false;
249                 }
250
251                 if (DateTime::getLastErrors()['error_count'] || DateTime::getLastErrors()['warning_count']) {
252                         return false;
253                 }
254
255                 return true;
256         }
257 }