ILIAS  release_9 Revision v9.13-25-g2c18ec4c24f
class.ilDateTime.php
Go to the documentation of this file.
1 <?php
2 
3 declare(strict_types=1);
4 
5 /* Copyright (c) 1998-2010 ILIAS open source, Extended GPL, see docs/LICENSE */
6 
7 const IL_CAL_DATETIME = 1;
8 const IL_CAL_DATE = 2;
9 const IL_CAL_UNIX = 3;
10 const IL_CAL_FKT_DATE = 4;
12 const IL_CAL_TIMESTAMP = 6;
13 const IL_CAL_ISO_8601 = 7;
14 
15 const IL_CAL_YEAR = 'year';
16 const IL_CAL_MONTH = 'month';
17 const IL_CAL_WEEK = 'week';
18 const IL_CAL_DAY = 'day';
19 const IL_CAL_HOUR = 'hour';
20 const IL_CAL_SECOND = 'second';
21 
29 {
30  public const YEAR = 'year';
31  public const MONTH = 'month';
32  public const WEEK = 'week';
33  public const DAY = 'day';
34  public const HOUR = 'hour';
35  public const MINUTE = 'minute';
36  public const SECOND = 'second';
37 
38  protected ilLogger $log;
39  protected ?ilTimeZone $timezone = null;
40  protected ?ilTimeZone $default_timezone = null;
41  protected ?DateTime $dt_obj = null;
42 
50  public function __construct($a_date = null, int $a_format = 0, string $a_tz = '')
51  {
52  global $DIC;
53 
54  $this->log = $DIC->logger()->cal();
55 
56  try {
57  $this->timezone = ilTimeZone::_getInstance($a_tz);
58  $this->default_timezone = ilTimeZone::_getInstance('');
59  $this->setDate($a_date, $a_format);
60  } catch (ilTimeZoneException $exc) {
61  $this->log->warning($exc->getMessage());
62  throw new ilDateTimeException('Unsupported timezone given. Timezone: ' . $a_tz);
63  }
64  }
65 
66  public function __clone()
67  {
68  if ($this->dt_obj) {
69  $this->dt_obj = clone $this->dt_obj;
70  }
71  }
72 
73  public function __sleep()
74  {
75  return array('timezone', 'default_timezone', 'dt_obj');
76  }
77 
78  public function __wakeup()
79  {
80  global $DIC;
81  $this->log = $DIC->logger()->cal();
82  }
83 
88  public function isNull(): bool
89  {
90  return !($this->dt_obj instanceof DateTime);
91  }
92 
98  public function switchTimeZone(string $a_timezone_identifier = ''): void
99  {
100  try {
101  $this->timezone = ilTimeZone::_getInstance($a_timezone_identifier);
102  return;
103  } catch (ilTimeZoneException $e) {
104  $this->log->warning('Unsupported timezone given: ' . $a_timezone_identifier);
105  throw new ilDateTimeException('Unsupported timezone given. Timezone: ' . $a_timezone_identifier);
106  }
107  }
108 
109  public function getTimeZoneIdentifier(): string
110  {
111  return $this->timezone->getIdentifier();
112  }
113 
124  public static function _before(
125  ilDateTime $start,
126  ilDateTime $end,
127  string $a_compare_field = '',
128  string $a_tz = ''
129  ): bool {
130  if ($start->isNull() || $end->isNull()) {
131  return false;
132  }
133 
134  switch ($a_compare_field) {
135  case IL_CAL_YEAR:
136  return $start->get(IL_CAL_FKT_DATE, 'Y', $a_tz) < $end->get(IL_CAL_FKT_DATE, 'Y', $a_tz);
137 
138  case IL_CAL_MONTH:
139  return (int) $start->get(IL_CAL_FKT_DATE, 'Ym', $a_tz) < $end->get(IL_CAL_FKT_DATE, 'Ym', $a_tz);
140 
141  case IL_CAL_DAY:
142  return (int) $start->get(IL_CAL_FKT_DATE, 'Ymd', $a_tz) < $end->get(IL_CAL_FKT_DATE, 'Ymd', $a_tz);
143 
144  case '':
145  default:
146  return $start->dt_obj < $end->dt_obj;
147  }
148  }
149 
158  public static function _equals(
159  ilDateTime $start,
160  ilDateTime $end,
161  string $a_compare_field = '',
162  string $a_tz = ''
163  ): bool {
164  if ($start->isNull() || $end->isNull()) {
165  return false;
166  }
167 
168  switch ($a_compare_field) {
169  case IL_CAL_YEAR:
170  return $start->get(IL_CAL_FKT_DATE, 'Y', $a_tz) == $end->get(IL_CAL_FKT_DATE, 'Y', $a_tz);
171 
172  case IL_CAL_MONTH:
173  return (int) $start->get(IL_CAL_FKT_DATE, 'Ym', $a_tz) == $end->get(IL_CAL_FKT_DATE, 'Ym', $a_tz);
174 
175  case IL_CAL_DAY:
176  return (int) $start->get(IL_CAL_FKT_DATE, 'Ymd', $a_tz) == $end->get(IL_CAL_FKT_DATE, 'Ymd', $a_tz);
177 
178  case '':
179  default:
180  return $start->dt_obj == $end->dt_obj;
181  }
182  }
183 
195  public static function _after(
196  ilDateTime $start,
197  ilDateTime $end,
198  string $a_compare_field = '',
199  string $a_tz = ''
200  ): bool {
201  if ($start->isNull() || $end->isNull()) {
202  return false;
203  }
204 
205  switch ($a_compare_field) {
206  case IL_CAL_YEAR:
207  return $start->get(IL_CAL_FKT_DATE, 'Y', $a_tz) > $end->get(IL_CAL_FKT_DATE, 'Y', $a_tz);
208 
209  case IL_CAL_MONTH:
210  return (int) $start->get(IL_CAL_FKT_DATE, 'Ym', $a_tz) > $end->get(IL_CAL_FKT_DATE, 'Ym', $a_tz);
211 
212  case IL_CAL_DAY:
213  return (int) $start->get(IL_CAL_FKT_DATE, 'Ymd', $a_tz) > $end->get(IL_CAL_FKT_DATE, 'Ymd', $a_tz);
214 
215  case '':
216  default:
217  return $start->dt_obj > $end->dt_obj;
218  }
219  }
220 
224  public static function _within(
225  ilDateTime $dt,
226  ilDateTime $start,
227  ilDateTime $end,
228  string $a_compare_field = '',
229  string $a_tz = ''
230  ): bool {
231  return
232  (ilDateTime::_after($dt, $start, $a_compare_field, $a_tz) or ilDateTime::_equals(
233  $dt,
234  $start,
235  $a_compare_field,
236  $a_tz
237  )) &&
238  (ilDateTime::_before($dt, $end, $a_compare_field, $a_tz) or ilDateTime::_equals(
239  $dt,
240  $end,
241  $a_compare_field,
242  $a_tz
243  ));
244  }
245 
252  public function increment(string $a_type, int $a_count = 1): ?int
253  {
254  if ($this->isNull()) {
255  return null;
256  }
257 
258  $sub = ($a_count < 0);
259  $count_str = abs($a_count);
260 
261  switch ($a_type) {
262  case self::YEAR:
263  $count_str .= 'year';
264  break;
265 
266  case self::MONTH:
267  $count_str .= 'month';
268  break;
269 
270  case self::WEEK:
271  $count_str .= 'week';
272  break;
273 
274  case self::DAY:
275  $count_str .= 'day';
276  break;
277 
278  case self::HOUR:
279  $count_str .= 'hour';
280  break;
281 
282  case self::MINUTE:
283  $count_str .= 'minute';
284  break;
285 
286  case self::SECOND:
287  $count_str .= 'second';
288  break;
289  }
290 
291  $interval = date_interval_create_from_date_string($count_str);
292  if (!$sub) {
293  $this->dt_obj->add($interval);
294  } else {
295  $this->dt_obj->sub($interval);
296  }
297  return $this->getUnixTime();
298  }
299 
300  public function getUnixTime(): ?int
301  {
302  if (!$this->isNull()) {
303  return $this->dt_obj->getTimestamp();
304  }
305  return null;
306  }
307 
308  protected function parsePartsToDate(
309  int $a_year,
310  int $a_month,
311  int $a_day,
312  ?int $a_hour = null,
313  ?int $a_min = null,
314  ?int $a_sec = null,
315  ?string $a_timezone = null
316  ): ?DateTime {
317  $a_year = $a_year;
318  $a_month = $a_month;
319  $a_day = $a_day;
320 
321  if (!$a_year) {
322  return null;
323  }
324  $date = null;
325  try {
326  $a_hour = (int) $a_hour;
327  $a_min = (int) $a_min;
328  $a_sec = (int) $a_sec;
329 
330  $format = $a_year . '-' . $a_month . '-' . $a_day;
331 
332  if ($a_hour !== null) {
333  $format .= ' ' . $a_hour . ':' . $a_min . ':' . $a_sec;
334 
335  // use current timezone if no other given
336  if (!$a_timezone) {
337  $a_timezone = $this->getTimeZoneIdentifier();
338  }
339 
340  $date = new DateTime($format, new DateTimeZone($a_timezone));
341  } else {
342  $date = new DateTime($format);
343  }
344  } catch (Exception $ex) {
345  // :TODO: do anything?
346  }
347  return ($date instanceof DateTime)
348  ? $date
349  : null;
350  }
351 
357  public function setDate($a_date, int $a_format): void
358  {
359  $this->dt_obj = null;
360 
361  if (!$a_date) {
362  return;
363  }
364 
365  switch ($a_format) {
366  case IL_CAL_UNIX:
367  try {
368  $this->dt_obj = new DateTime('@' . $a_date);
369  $this->dt_obj->setTimezone(new DateTimeZone($this->getTimeZoneIdentifier()));
370  } catch (Exception $ex) {
371  $message = 'Cannot parse date: ' . $a_date . ' with format ' . $a_format;
372  $this->log->warning($message);
373  throw new ilDateTimeException($message);
374  }
375  break;
376 
377  case IL_CAL_DATETIME:
378  $matches = preg_match(
379  '/^(\d{4})-?(\d{2})-?(\d{2})([T\s]?(\d{2}):?(\d{2}):?(\d{2})(\.\d+)?(Z|[\+\-]\d{2}:?\d{2})?)$/i',
380  $a_date,
381  $d_parts
382  );
383  if ($matches < 1) {
384  $this->log->warning('Cannot parse date: ' . $a_date);
385  $this->log->warning(print_r($matches, true));
386  $this->log->logStack(ilLogLevel::WARNING);
387  throw new ilDateTimeException('Cannot parse date: ' . $a_date);
388  }
389 
390  $tz_id = (isset($d_parts[9]) && $d_parts[9] === 'Z')
391  ? 'UTC'
392  : $this->getTimeZoneIdentifier();
393  $this->dt_obj = $this->parsePartsToDate(
394  (int) $d_parts[1],
395  (int) $d_parts[2],
396  (int) $d_parts[3],
397  (int) $d_parts[5],
398  (int) $d_parts[6],
399  (int) $d_parts[7],
400  $tz_id
401  );
402  break;
403 
404  case IL_CAL_DATE:
405  try {
406  // Pure dates are not timezone sensible.
407  $this->dt_obj = new DateTime($a_date, new DateTimeZone('UTC'));
408  } catch (Exception $ex) {
409  $this->log->warning('Cannot parse date : ' . $a_date);
410  throw new ilDateTimeException('Cannot parse date: ' . $a_date);
411  }
412  break;
413 
414  case IL_CAL_FKT_GETDATE:
415  // Format like getdate parameters
416  $this->dt_obj = $this->parsePartsToDate(
417  (int) $a_date['year'],
418  (int) $a_date['mon'],
419  (int) $a_date['mday'],
420  (int) $a_date['hours'],
421  (int) $a_date['minutes'],
422  (int) ($a_date['seconds'] ?? 0),
423  $this->getTimeZoneIdentifier()
424  );
425  break;
426 
427  case IL_CAL_TIMESTAMP:
428  if (!preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $a_date, $d_parts)) {
429  $this->log->warning('Cannot parse date: ' . $a_date);
430  throw new ilDateTimeException('Cannot parse date.');
431  }
432  $this->dt_obj = $this->parsePartsToDate(
433  (int) $d_parts[1],
434  (int) $d_parts[2],
435  (int) $d_parts[3],
436  (int) $d_parts[4],
437  (int) $d_parts[5],
438  (int) $d_parts[6],
439  $this->getTimeZoneIdentifier()
440  );
441  break;
442 
443  case IL_CAL_ISO_8601:
448  $this->dt_obj = DateTime::createFromFormat(
449  DateTime::ATOM,
450  $a_date,
451  new DateTimeZone($this->getTimeZoneIdentifier())
452  );
453  break;
454  }
455  // remove set timezone since it does not influence the internal date.
456  // the tz must be passed in the moment of the creation of the date object.
457  }
458 
463  public function get(int $a_format, string $a_format_str = '', string $a_tz = '')
464  {
465  if ($this->isNull()) {
466  return null;
467  }
468 
469  $timezone = $this->default_timezone;
470  if ($a_tz) {
471  try {
472  $timezone = ilTimeZone::_getInstance($a_tz);
473  } catch (ilTimeZoneException $exc) {
474  $this->log->warning('Invalid timezone given. Timezone: ' . $a_tz);
475  }
476  }
477  $out_date = clone($this->dt_obj);
478  $out_date->setTimezone(new DateTimeZone($timezone->getIdentifier()));
479 
480  $date = null;
481  switch ($a_format) {
482  case IL_CAL_UNIX:
483  // timezone unrelated
484  $date = $this->getUnixTime();
485  break;
486 
487  case IL_CAL_DATE:
488  $date = $out_date->format('Y-m-d');
489  break;
490 
491  case IL_CAL_DATETIME:
492  $date = $out_date->format('Y-m-d H:i:s');
493  break;
494 
495  case IL_CAL_FKT_DATE:
496  $date = $out_date->format($a_format_str);
497  break;
498 
499  case IL_CAL_FKT_GETDATE:
500  $date = array(
501  'seconds' => (int) $out_date->format('s')
502  ,
503  'minutes' => (int) $out_date->format('i')
504  ,
505  'hours' => (int) $out_date->format('G')
506  ,
507  'mday' => (int) $out_date->format('j')
508  ,
509  'wday' => (int) $out_date->format('w')
510  ,
511  'mon' => (int) $out_date->format('n')
512  ,
513  'year' => (int) $out_date->format('Y')
514  ,
515  'yday' => (int) $out_date->format('z')
516  ,
517  'weekday' => $out_date->format('l')
518  ,
519  'month' => $out_date->format('F')
520  ,
521  'isoday' => (int) $out_date->format('N')
522  );
523  break;
524 
525  case IL_CAL_ISO_8601:
526  $date = $out_date->format('c');
527  break;
528 
529  case IL_CAL_TIMESTAMP:
530  $date = $out_date->format('YmdHis');
531  break;
532  }
533  return $date;
534  }
535 
543  public function __toString(): string
544  {
545  return $this->get(IL_CAL_DATETIME) . '<br>';
546  }
547 }
get(int $a_format, string $a_format_str='', string $a_tz='')
get formatted date
const IL_CAL_DATETIME
parsePartsToDate(int $a_year, int $a_month, int $a_day, ?int $a_hour=null, ?int $a_min=null, ?int $a_sec=null, ?string $a_timezone=null)
static _getInstance(string $a_tz='')
get instance by timezone
static _before(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
compare two dates and check start is before end This method does not consider tz offsets.
const IL_CAL_HOUR
ilTimeZone $timezone
ilTimeZone $default_timezone
setDate($a_date, int $a_format)
Set date.
increment(string $a_type, int $a_count=1)
const IL_CAL_MONTH
const IL_CAL_UNIX
const IL_CAL_WEEK
DateTime $dt_obj
__toString()
to string for date time objects Output is user time zone public
global $DIC
Definition: feed.php:28
const IL_CAL_DAY
static _after(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
compare two dates and check start is after end This method does not consider tz offsets.
Class for TimeZone exceptions.
const IL_CAL_FKT_DATE
const IL_CAL_SECOND
const IL_CAL_ISO_8601
const IL_CAL_FKT_GETDATE
const IL_CAL_DATE
static _equals(ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
Check if two date are equal.
__construct($a_date=null, int $a_format=0, string $a_tz='')
Create new date object.
$message
Definition: xapiexit.php:32
const IL_CAL_TIMESTAMP
switchTimeZone(string $a_timezone_identifier='')
Switch timezone.
static _within(ilDateTime $dt, ilDateTime $start, ilDateTime $end, string $a_compare_field='', string $a_tz='')
Check whether an date is within a date duration given by start and end.
isNull()
Check if a date is null (Datetime == &#39;0000-00-00 00:00:00&#39;, unixtime == 0,...)
const IL_CAL_YEAR