Merge branch '3.0'
[GitHub/WoltLab/WCF.git] / wcfsetup / install / files / lib / util / DateUtil.class.php
CommitLineData
11ade432
AE
1<?php
2namespace wcf\util;
11ade432 3use wcf\data\language\Language;
ec1b1daf 4use wcf\data\user\User;
b4cbf821 5use wcf\system\exception\SystemException;
ec1b1daf 6use wcf\system\WCF;
11ade432
AE
7
8/**
9 * Contains date-related functions.
9f959ced
MS
10 *
11 * @author Marcel Werk
c839bd49 12 * @copyright 2001-2018 WoltLab GmbH
11ade432 13 * @license GNU Lesser General Public License <http://opensource.org/licenses/lgpl-license.php>
e71525e4 14 * @package WoltLabSuite\Core\Util
11ade432 15 */
18284789 16final class DateUtil {
11ade432
AE
17 /**
18 * name of the default date format language variable
9f959ced 19 * @var string
11ade432 20 */
e89c399a 21 const DATE_FORMAT = 'wcf.date.dateFormat';
11ade432
AE
22
23 /**
24 * name of the default time format language variable
9f959ced 25 * @var string
11ade432 26 */
e89c399a 27 const TIME_FORMAT = 'wcf.date.timeFormat';
11ade432 28
3b007c94
JR
29 /**
30 * format the interval to be used as a standalone phrase
31 * @var integer
32 */
33 const FORMAT_DEFAULT = 1;
34
35 /**
36 * format the interval to be used as a phrase in a sentence
37 * @var integer
38 */
39 const FORMAT_SENTENCE = 2;
40
41 /**
42 * format the interval without direction
43 * @var integer
44 */
45 const FORMAT_PLAIN = 3;
46
11ade432
AE
47 /**
48 * list of available time zones
7a23a706 49 * @var string[]
11ade432 50 */
058cbd6a 51 protected static $availableTimezones = [
523fdcf2
MW
52 // there is not support for UTC-12:00 in php
53 // '...', // (UTC-12:00) International Date Line West
54 'Pacific/Samoa', // (UTC-11:00) Midway Island, American Samoa
55 'Pacific/Honolulu', // (UTC-10:00) Hawaii
56 'America/Anchorage', // (UTC-09:00) Alaska
57 'America/Los_Angeles', // (UTC-08:00) Pacific Time (US & Canada), Tijuana, Baja California
58 'America/Phoenix', // (UTC-07:00) Arizona
59 'America/Chihuahua', // (UTC-07:00) Chihuahua, Mazatlan
60 'America/Denver', // (UTC-07:00) Mountain Time (US & Canada)
61 'America/Chicago', // (UTC-06:00) Central Time (US & Canada)
62 'America/Mexico_City', // (UTC-06:00) Mexico City, Monterrey
63 'America/Tegucigalpa', // (UTC-06:00) Central America
64 'America/Regina', // (UTC-06:00) Saskatchewan
65 'America/Bogota', // (UTC-05:00) Bogota, Lima
66 'America/New_York', // (UTC-05:00) Eastern Time (US & Canada)
67 'America/Indiana/Indianapolis', // (UTC-05:00) Indiana (East)
68 'America/Rio_Branco', // (UTC-05:00) Rio Branco
69 'America/Caracas', // (UTC-04:30) Caracas
70 'America/Asuncion', // (UTC-04:00) Asuncion
71 'America/Halifax', // (UTC-04:00) Atlantic Time (Canada)
72 'America/Cuiaba', // (UTC-04:00) Cuiaba
73 'America/La_Paz', // (UTC-04:00) Georgetown, La Paz, Manaus
74 'America/Santiago', // (UTC-04:00) Santiago
75 'America/St_Johns', // (UTC-03:30) Newfoundland
76 'America/Sao_Paulo', // (UTC-03:00) Brasilia
77 'America/Argentina/Buenos_Aires', // (UTC-03:00) Buenos Aires
78 'America/Cayenne', // (UTC-03:00) Cayenne
79 'America/Godthab', // (UTC-03:00) Greenland
80 'America/Montevideo', // (UTC-03:00) Montevideo
81 'Atlantic/South_Georgia', // (UTC-02:00) Mid-Atlantic
82 'Atlantic/Azores', // (UTC-01:00) Azores
83 'Atlantic/Cape_Verde', // (UTC-01:00) Cape Verde Is.
84 'Africa/Casablanca', // (UTC) Casablanca
85 'Europe/London', // (UTC) Dublin, Lisbon, London
86 'Africa/Monrovia', // (UTC) Monrovia, Reykjavik
87 'Europe/Berlin', // (UTC+01:00) Amsterdam, Berlin, Rome, Stockholm, Vienna
88 'Europe/Belgrade', // (UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague
89 'Europe/Paris', // (UTC+01:00) Brussels, Copenhagen, Madrid, Paris
90 'Europe/Sarajevo', // (UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb
91 'Africa/Algiers', // (UTC+01:00) West Central Africa
92 'Africa/Windhoek', // (UTC+01:00) Windhoek
93 'Europe/Athens', // (UTC+02:00) Athens, Bucharest, Istanbul
94 'Asia/Beirut', // (UTC+02:00) Beirut
95 'Asia/Damascus', // (UTC+02:00) Damascus
96 'Africa/Harare', // (UTC+02:00) Harare, Pretoria
97 'Europe/Helsinki', // (UTC+02:00) Helsinki, Kiev, Riga, Sofia, Tallinn, Vilnius
98 'Asia/Jerusalem', // (UTC+02:00) Jerusalem
99 'Africa/Cairo', // (UTC+02:00) Cairo
bfdd4392 100 'Europe/Kaliningrad', // (UTC+02:00) Kaliningrad
523fdcf2
MW
101 'Asia/Amman', // (UTC+03:00) Amman
102 'Asia/Baghdad', // (UTC+03:00) Baghdad
bfdd4392
MW
103 'Europe/Minsk', // (UTC+03:00) Minsk
104 'Europe/Moscow', // (UTC+03:00) Moscow, Volgograd
523fdcf2
MW
105 'Asia/Kuwait', // (UTC+03:00) Kuwait, Riyadh
106 'Africa/Nairobi', // (UTC+03:00) Nairobi
107 'Asia/Tehran', // (UTC+03:30) Tehran
108 'Asia/Muscat', // (UTC+04:00) Muscat
109 'Asia/Baku', // (UTC+04:00) Baku
110 'Asia/Yerevan', // (UTC+04:00) Yerevan
523fdcf2
MW
111 'Indian/Mauritius', // (UTC+04:00) Port Loius
112 'Asia/Tbilisi', // (UTC+04:00) Tbilisi
113 'Asia/Kabul', // (UTC+04:30) Kabul
114 'Asia/Karachi', // (UTC+05:00) Karachi
bfdd4392 115 'Asia/Yekaterinburg', // (UTC+05:00) Ekaterinburg
523fdcf2
MW
116 'Asia/Tashkent', // (UTC+05:00) Tashkent
117 'Asia/Kolkata', // (UTC+05:30) Calcutta, New Dehli
118 'Asia/Colombo', // (UTC+05:30) Sri Jayawardenepura
119 'Asia/Katmandu', // (UTC+05:45) Kathmandu
120 'Asia/Almaty', // (UTC+06:00) Almaty
121 'Asia/Dhaka', // (UTC+06:00) Dhaka
bfdd4392 122 'Asia/Novosibirsk', // (UTC+06:00) Novosibirsk
523fdcf2
MW
123 'Asia/Rangoon', // (UTC+06:30) Yangon (Rangoon)
124 'Asia/Bangkok', // (UTC+07:00) Bangkok, Jakarta
bfdd4392
MW
125 'Asia/Krasnoyarsk', // (UTC+07:00) Krasnoyarsk
126 'Asia/Irkutsk', // (UTC+08:00) Irkutsk
523fdcf2
MW
127 'Asia/Kuala_Lumpur', // (UTC+08:00) Kuala Lumpur, Singapore
128 'Asia/Chongqing', // (UTC+08:00) Beijing, Chongqing, Hong Kong
129 'Australia/Perth', // (UTC+08:00) Perth
130 'Asia/Taipei', // (UTC+08:00) Taipei
131 'Asia/Ulaanbaatar', // (UTC+08:00) Ulaan Bataar
bfdd4392 132 'Asia/Yakutsk', // (UTC+09:00) Yakutsk
523fdcf2
MW
133 'Asia/Tokyo', // (UTC+09:00) Tokyo
134 'Asia/Seoul', // (UTC+09:00) Seoul
135 'Australia/Adelaide', // (UTC+09:30) Adelaide
136 'Australia/Darwin', // (UTC+09:30) Darwin
137 'Australia/Brisbane', // (UTC+10:00) Brisbane
138 'Australia/Sydney', // (UTC+10:00) Canberra, Melbourne, Sydney
139 'Pacific/Guam', // (UTC+10:00) Guam, Port Moresby
140 'Australia/Hobart', // (UTC+10:00) Hobart
bfdd4392 141 'Asia/Vladivostok', // (UTC+10:00) Vladivostok
523fdcf2 142 'Pacific/Noumea', // (UTC+11:00) New Caledonia
523fdcf2
MW
143 'Pacific/Auckland', // (UTC+12:00) Auckland
144 'Pacific/Fiji', // (UTC+12:00) Fiji
523fdcf2
MW
145 'Pacific/Tongatapu', // (UTC+13:00) Nukualofa
146 'Pacific/Apia', // (UTC+13:00) Samoa
058cbd6a 147 ];
11ade432 148
d0b48367
MS
149 /**
150 * first day of the week
151 * 0=sunday
152 * 1=monday
153 * @var integer
154 */
155 private static $firstDayOfTheWeek = null;
156
157 /**
158 * order of the week days
7a23a706 159 * @var string[]
d0b48367
MS
160 */
161 private static $weekDays = null;
162
163 /**
164 * order of the week days (short textual representation)
7a23a706 165 * @var string[]
d0b48367
MS
166 */
167 private static $shortWeekDays = null;
168
11ade432
AE
169 /**
170 * Returns a formatted date.
171 *
4e25add7
MS
172 * @param \DateTime $time
173 * @param string $format
174 * @param Language $language
175 * @param User $user
9f959ced 176 * @return string
11ade432
AE
177 */
178 public static function format(\DateTime $time = null, $format = null, Language $language = null, User $user = null) {
179 // get default values
180 if ($time === null) $time = new \DateTime();
181 if ($user === null) $user = WCF::getUser();
182 if ($language === null) $language = WCF::getLanguage();
183 if ($format === null) $format = self::DATE_FORMAT;
184
185 // set time zone
186 $time->setTimezone($user->getTimeZone());
187
188 // format date
189 $output = $time->format($language->get($format));
190
191 // localize output
4d38eef7 192 $output = self::localizeDate($output, $language->get($format), $language);
11ade432
AE
193
194 return $output;
195 }
196
8ee81f2f 197 /**
2246b509 198 * Returns a formatted date interval.
8ee81f2f 199 *
2246b509
MS
200 * @param \DateInterval $interval interval to be formatted
201 * @param boolean $fullInterval if `true`, the complete interval is returned, otherwise a rounded interval is used
3b007c94 202 * @param integer $formatType format type for the interval, use the class constant FORMAT_DEFAULT, FORMAT_SENTENCE or FORMAT_PLAIN
8ee81f2f
MS
203 * @return string
204 */
3b007c94 205 public static function formatInterval(\DateInterval $interval, $fullInterval = false, $formatType = self::FORMAT_DEFAULT) {
8ee81f2f
MS
206 $years = $interval->format('%y');
207 $months = $interval->format('%m');
208 $days = $interval->format('%d');
209 $weeks = floor($days / 7);
210 $hours = $interval->format('%h');
211 $minutes = $interval->format('%i');
e0d32362
MS
212
213 $direction = '';
506966c4
TD
214 switch ($interval->format('%R')) {
215 case '+':
216 $direction = 'past';
217 break;
218 case '-':
219 $direction = 'future';
220 break;
221 }
8ee81f2f 222
3b007c94
JR
223 switch ($formatType) {
224 case self::FORMAT_DEFAULT:
225 $languageItemSuffix = $direction;
226 break;
227
228 case self::FORMAT_SENTENCE:
229 $languageItemSuffix = $direction . '.inSentence';
230 break;
7ec10692 231
3b007c94
JR
232 case self::FORMAT_PLAIN:
233 $languageItemSuffix = 'plain';
234 break;
235
236 default:
237 throw new \InvalidArgumentException('Invalid $formatType value');
2246b509
MS
238 }
239
8ee81f2f 240 if ($fullInterval) {
2246b509 241 return WCF::getLanguage()->getDynamicVariable('wcf.date.interval.full.' . $languageItemSuffix, [
8ee81f2f
MS
242 'days' => $days - 7 * $weeks,
243 'firstElement' => $years ? 'years' : ($months ? 'months' : ($weeks ? 'weeks' : ($days ? 'days' : ($hours ? 'hours' : 'minutes')))),
244 'hours' => $hours,
245 'lastElement' => !$minutes ? (!$hours ? (!$days ? (!$weeks ? (!$months ? 'years' : 'months') : 'weeks') : 'days') : 'hours') : 'minutes',
246 'minutes' => $minutes,
247 'months' => $months,
248 'weeks' => $weeks,
249 'years' => $years
058cbd6a 250 ]);
8ee81f2f
MS
251 }
252
253 if ($years) {
2246b509 254 return WCF::getLanguage()->getDynamicVariable('wcf.date.interval.years.' . $languageItemSuffix, [
8ee81f2f 255 'years' => $years
058cbd6a 256 ]);
8ee81f2f
MS
257 }
258
259 if ($months) {
2246b509 260 return WCF::getLanguage()->getDynamicVariable('wcf.date.interval.months.' . $languageItemSuffix, [
8ee81f2f 261 'months' => $months
058cbd6a 262 ]);
8ee81f2f
MS
263 }
264
acfd3874 265 if ($weeks) {
2246b509 266 return WCF::getLanguage()->getDynamicVariable('wcf.date.interval.weeks.' . $languageItemSuffix, [
acfd3874 267 'weeks' => $weeks
058cbd6a 268 ]);
acfd3874
MS
269 }
270
8ee81f2f 271 if ($days) {
2246b509 272 return WCF::getLanguage()->getDynamicVariable('wcf.date.interval.days.' . $languageItemSuffix, [
8ee81f2f 273 'days' => $days
058cbd6a 274 ]);
8ee81f2f
MS
275 }
276
277 if ($hours) {
2246b509 278 return WCF::getLanguage()->getDynamicVariable('wcf.date.interval.hours.' . $languageItemSuffix, [
8ee81f2f 279 'hours' => $hours
058cbd6a 280 ]);
8ee81f2f
MS
281 }
282
2246b509 283 return WCF::getLanguage()->getDynamicVariable('wcf.date.interval.minutes.' . $languageItemSuffix, [
8ee81f2f 284 'minutes' => $minutes
058cbd6a 285 ]);
8ee81f2f
MS
286 }
287
11ade432
AE
288 /**
289 * Returns a localized date output.
290 *
4e25add7
MS
291 * @param string $date
292 * @param string $format
293 * @param Language $language
9f959ced 294 * @return string
11ade432
AE
295 */
296 public static function localizeDate($date, $format, Language $language) {
297 if ($language->languageCode != 'en') {
298 // full textual representation of the day of the week (l)
4d38eef7 299 if (strpos($format, 'l') !== false) {
058cbd6a 300 $date = str_replace(['Sunday', 'Monday', 'Tuesday', 'Wednesday', 'Thursday', 'Friday', 'Saturday'], [
e89c399a
MW
301 $language->get('wcf.date.day.sunday'),
302 $language->get('wcf.date.day.monday'),
303 $language->get('wcf.date.day.tuesday'),
304 $language->get('wcf.date.day.wednesday'),
305 $language->get('wcf.date.day.thursday'),
306 $language->get('wcf.date.day.friday'),
307 $language->get('wcf.date.day.saturday')
058cbd6a 308 ], $date);
11ade432
AE
309 }
310
311 // textual representation of a day, three letters (D)
4d38eef7 312 if (strpos($format, 'D') !== false) {
058cbd6a 313 $date = str_replace(['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'], [
e89c399a
MW
314 $language->get('wcf.date.day.sun'),
315 $language->get('wcf.date.day.mon'),
316 $language->get('wcf.date.day.tue'),
317 $language->get('wcf.date.day.wed'),
318 $language->get('wcf.date.day.thu'),
319 $language->get('wcf.date.day.fri'),
320 $language->get('wcf.date.day.sat')
058cbd6a 321 ], $date);
11ade432
AE
322 }
323
324 // full textual representation of a month (F)
4d38eef7 325 if (strpos($format, 'F') !== false) {
058cbd6a 326 $date = str_replace(['January', 'February', 'March', 'April', 'May', 'June', 'July', 'August', 'September', 'October', 'November', 'December'], [
e89c399a
MW
327 $language->get('wcf.date.month.january'),
328 $language->get('wcf.date.month.february'),
329 $language->get('wcf.date.month.march'),
330 $language->get('wcf.date.month.april'),
331 $language->get('wcf.date.month.may'),
332 $language->get('wcf.date.month.june'),
333 $language->get('wcf.date.month.july'),
334 $language->get('wcf.date.month.august'),
335 $language->get('wcf.date.month.september'),
336 $language->get('wcf.date.month.october'),
337 $language->get('wcf.date.month.november'),
338 $language->get('wcf.date.month.december')
058cbd6a 339 ], $date);
11ade432
AE
340 }
341
342 // short textual representation of a month (M)
4d38eef7 343 if (strpos($format, 'M') !== false) {
058cbd6a 344 $date = str_replace(['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'], [
020bf2c7
AE
345 $language->get('wcf.date.month.short.jan'),
346 $language->get('wcf.date.month.short.feb'),
347 $language->get('wcf.date.month.short.mar'),
348 $language->get('wcf.date.month.short.apr'),
349 $language->get('wcf.date.month.short.may'),
350 $language->get('wcf.date.month.short.jun'),
351 $language->get('wcf.date.month.short.jul'),
352 $language->get('wcf.date.month.short.aug'),
353 $language->get('wcf.date.month.short.sep'),
354 $language->get('wcf.date.month.short.oct'),
355 $language->get('wcf.date.month.short.nov'),
356 $language->get('wcf.date.month.short.dec')
058cbd6a 357 ], $date);
11ade432
AE
358 }
359 }
360
361 return $date;
362 }
363
364 /**
365 * Creates a DateTime object with the given unix timestamp.
366 *
367 * @param integer $timestamp
042172ff 368 * @return \DateTime
11ade432
AE
369 */
370 public static function getDateTimeByTimestamp($timestamp) {
371 return new \DateTime('@'.$timestamp);
372 }
373
374 /**
375 * Returns a list of available timezones.
376 *
7a23a706 377 * @return string[]
11ade432
AE
378 */
379 public static function getAvailableTimezones() {
380 return self::$availableTimezones;
381 }
136fba63
MW
382
383 /**
384 * Calculates the age of a given date.
385 *
386 * @param string $date format YYYY-MM-DD
387 * @return integer
388 */
389 public static function getAge($date) {
390 // split date
391 $year = $month = $day = 0;
392 $value = explode('-', $date);
393 if (isset($value[0])) $year = intval($value[0]);
394 if (isset($value[1])) $month = intval($value[1]);
395 if (isset($value[2])) $day = intval($value[2]);
87d3a054 396
136fba63
MW
397 // calc
398 if ($year) {
399 $age = self::format(null, 'Y') - $year;
8226fe09 400 if (self::format(null, 'n') < $month) $age--;
7525af28 401 else if (self::format(null, 'n') == $month && self::format(null, 'j') < $day) $age--;
136fba63
MW
402 return $age;
403 }
404
405 return 0;
406 }
18284789 407
b4cbf821
AE
408 /**
409 * Validates if given date is valid ISO-8601.
410 *
411 * @param string $date
2b770bdd 412 * @throws SystemException
b4cbf821
AE
413 */
414 public static function validateDate($date) {
afc3ddab 415 if (preg_match('~^(?P<year>[0-9]{4})-(?P<month>[0-9]{2})-(?P<day>[0-9]{2})~', $date, $matches)) {
0840ec5a
AE
416 if (!checkdate($matches['month'], $matches['day'], $matches['year'])) {
417 throw new SystemException("Date '".$date."' is invalid");
418 }
b4cbf821 419 }
0840ec5a
AE
420 else {
421 throw new SystemException("Date '".$date."' is not a valid ISO-8601 date");
b4cbf821
AE
422 }
423 }
424
d0b48367
MS
425 /**
426 * Returns the first day of the week.
427 *
428 * @return integer
429 */
430 public static function getFirstDayOfTheWeek() {
431 if (self::$firstDayOfTheWeek === null) {
432 self::$firstDayOfTheWeek = intval(WCF::getLanguage()->get('wcf.date.firstDayOfTheWeek'));
433 if (self::$firstDayOfTheWeek != 1 && self::$firstDayOfTheWeek != 0) self::$firstDayOfTheWeek = 0;
434 }
435
436 return self::$firstDayOfTheWeek;
437 }
438
439 /**
440 * Returns the order of the week days.
441 *
7a23a706 442 * @return string[]
d0b48367
MS
443 */
444 public static function getWeekDays() {
445 if (self::$weekDays === null) {
446 if (self::getFirstDayOfTheWeek() == 1) {
058cbd6a 447 self::$weekDays = [
d0b48367
MS
448 1 => 'monday',
449 2 => 'tuesday',
450 3 => 'wednesday',
451 4 => 'thursday',
452 5 => 'friday',
453 6 => 'saturday',
454 0 => 'sunday'
058cbd6a 455 ];
d0b48367
MS
456 }
457 else {
058cbd6a 458 self::$weekDays = [
d0b48367
MS
459 0 => 'sunday',
460 1 => 'monday',
461 2 => 'tuesday',
462 3 => 'wednesday',
463 4 => 'thursday',
464 5 => 'friday',
465 6 => 'saturday'
058cbd6a 466 ];
d0b48367
MS
467 }
468 }
469
470 return self::$weekDays;
471 }
472
473 /**
474 * Returns the order of the week days (short textual representation).
475 *
7a23a706 476 * @return string[]
d0b48367
MS
477 */
478 public static function getShortWeekDays() {
479 if (self::$shortWeekDays === null) {
480 if (self::getFirstDayOfTheWeek() == 1) {
058cbd6a 481 self::$shortWeekDays = [
d0b48367
MS
482 1 => 'mon',
483 2 => 'tue',
484 3 => 'wed',
485 4 => 'thu',
486 5 => 'fri',
487 6 => 'sat',
488 0 => 'sun'
058cbd6a 489 ];
d0b48367
MS
490 }
491 else {
058cbd6a 492 self::$shortWeekDays = [
d0b48367
MS
493 0 => 'sun',
494 1 => 'mon',
495 2 => 'tue',
496 3 => 'wed',
497 4 => 'thu',
498 5 => 'fri',
499 6 => 'sat'
058cbd6a 500 ];
d0b48367
MS
501 }
502 }
503
504 return self::$shortWeekDays;
505 }
506
507 /**
508 * Returns the number of weeks in the given year.
509 *
510 * @param integer $year
511 * @return integer
512 */
513 public static function getWeeksInYear($year) {
514 $date = new \DateTime();
515 $date->setISODate($year, 53, self::getFirstDayOfTheWeek());
516 return ($date->format('W') == 53 ? 53 : 52);
517 }
439109fb
CW
518
519 /**
520 * Returns the relative date time identical to the relative time generated
521 * through JavaScript.
522 *
523 * @param \DateTime $dateTimeObject target date object
524 * @param integer $timestamp target timestamp
525 * @param string $date localized date
526 * @param string $time localized time
527 * @param boolean $isFutureDate true if timestamp is in the future
528 * @return string relative time
529 */
530 public static function getRelativeTime(\DateTime $dateTimeObject, $timestamp, $date, $time, $isFutureDate) {
531 if ($isFutureDate) {
532 return str_replace('%time%', $time, str_replace('%date%', $date, WCF::getLanguage()->get('wcf.date.dateTimeFormat')));
533 }
534
535 // timestamp is less than 60 seconds ago
536 if ($timestamp >= TIME_NOW || TIME_NOW < ($timestamp + 60)) {
537 return WCF::getLanguage()->get('wcf.date.relative.now');
538 }
539 // timestamp is less than 60 minutes ago (display 1 hour ago rather than 60 minutes ago)
540 else if (TIME_NOW < ($timestamp + 3540)) {
541 $minutes = max(round((TIME_NOW - $timestamp) / 60), 1);
542
543 return WCF::getLanguage()->getDynamicVariable('wcf.date.relative.minutes', ['minutes' => $minutes]);
544 }
545 // timestamp is less than 24 hours ago
546 else if (TIME_NOW < ($timestamp + 86400)) {
547 $hours = round((TIME_NOW - $timestamp) / 3600);
548
549 return WCF::getLanguage()->getDynamicVariable('wcf.date.relative.hours', ['hours' => $hours]);
550 }
551 // timestamp is less than 6 days ago
552 else if (TIME_NOW < ($timestamp + 518400)) {
553 $dtoNoTime = clone $dateTimeObject;
554 $dtoNoTime->setTime(0, 0, 0);
555 $currentDateTimeObject = self::getDateTimeByTimestamp(TIME_NOW);
b2204e3d 556 $currentDateTimeObject->setTimezone(WCF::getUser()->getTimeZone());
439109fb
CW
557 $currentDateTimeObject->setTime(0, 0, 0);
558
559 $days = $dtoNoTime->diff($currentDateTimeObject)->days;
560 $day = self::format($dateTimeObject, 'l');
561
562 return WCF::getLanguage()->getDynamicVariable('wcf.date.relative.pastDays', [
563 'days' => $days,
564 'day' => $day,
565 'time' => $time
566 ]);
567 }
568
569 // timestamp is between ~700 million years BC and last week
570 $datetime = WCF::getLanguage()->get('wcf.date.shortDateTimeFormat');
571 $datetime = str_replace('%date%', $date, $datetime);
572 $datetime = str_replace('%time%', $time, $datetime);
573
574 return $datetime;
575 }
d0b48367 576
1d5f9363
MS
577 /**
578 * Forbid creation of DateUtil objects.
579 */
580 private function __construct() {
581 // does nothing
582 }
11ade432 583}