ILIAS  release_8 Revision v8.19
All Data Structures Namespaces Files Functions Variables Modules Pages
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  }
150 
159  public static function _equals(
160  ilDateTime $start,
161  ilDateTime $end,
162  string $a_compare_field = '',
163  string $a_tz = ''
164  ): bool {
165  if ($start->isNull() || $end->isNull()) {
166  return false;
167  }
168 
169  switch ($a_compare_field) {
170  case IL_CAL_YEAR:
171  return $start->get(IL_CAL_FKT_DATE, 'Y', $a_tz) == $end->get(IL_CAL_FKT_DATE, 'Y', $a_tz);
172 
173  case IL_CAL_MONTH:
174  return (int) $start->get(IL_CAL_FKT_DATE, 'Ym', $a_tz) == $end->get(IL_CAL_FKT_DATE, 'Ym', $a_tz);
175 
176  case IL_CAL_DAY:
177  return (int) $start->get(IL_CAL_FKT_DATE, 'Ymd', $a_tz) == $end->get(IL_CAL_FKT_DATE, 'Ymd', $a_tz);
178 
179  case '':
180  default:
181  return $start->dt_obj == $end->dt_obj;
182 
183  }
184  }
185 
197  public static function _after(
198  ilDateTime $start,
199  ilDateTime $end,
200  string $a_compare_field = '',
201  string $a_tz = ''
202  ): bool {
203  if ($start->isNull() || $end->isNull()) {
204  return false;
205  }
206 
207  switch ($a_compare_field) {
208  case IL_CAL_YEAR:
209  return $start->get(IL_CAL_FKT_DATE, 'Y', $a_tz) > $end->get(IL_CAL_FKT_DATE, 'Y', $a_tz);
210 
211  case IL_CAL_MONTH:
212  return (int) $start->get(IL_CAL_FKT_DATE, 'Ym', $a_tz) > $end->get(IL_CAL_FKT_DATE, 'Ym', $a_tz);
213 
214  case IL_CAL_DAY:
215  return (int) $start->get(IL_CAL_FKT_DATE, 'Ymd', $a_tz) > $end->get(IL_CAL_FKT_DATE, 'Ymd', $a_tz);
216 
217  case '':
218  default:
219  return $start->dt_obj > $end->dt_obj;
220 
221  }
222  }
223 
227  public static function _within(
228  ilDateTime $dt,
229  ilDateTime $start,
230  ilDateTime $end,
231  string $a_compare_field = '',
232  string $a_tz = ''
233  ): bool {
234  return
235  (ilDateTime::_after($dt, $start, $a_compare_field, $a_tz) or ilDateTime::_equals(
236  $dt,
237  $start,
238  $a_compare_field,
239  $a_tz
240  )) &&
241  (ilDateTime::_before($dt, $end, $a_compare_field, $a_tz) or ilDateTime::_equals(
242  $dt,
243  $end,
244  $a_compare_field,
245  $a_tz
246  ));
247  }
248 
255  public function increment(string $a_type, int $a_count = 1): ?int
256  {
257  if ($this->isNull()) {
258  return null;
259  }
260 
261  $sub = ($a_count < 0);
262  $count_str = abs($a_count);
263 
264  switch ($a_type) {
265  case self::YEAR:
266  $count_str .= 'year';
267  break;
268 
269  case self::MONTH:
270  $count_str .= 'month';
271  break;
272 
273  case self::WEEK:
274  $count_str .= 'week';
275  break;
276 
277  case self::DAY:
278  $count_str .= 'day';
279  break;
280 
281  case self::HOUR:
282  $count_str .= 'hour';
283  break;
284 
285  case self::MINUTE:
286  $count_str .= 'minute';
287  break;
288 
289  case self::SECOND:
290  $count_str .= 'second';
291  break;
292  }
293 
294  $interval = date_interval_create_from_date_string($count_str);
295  if (!$sub) {
296  $this->dt_obj->add($interval);
297  } else {
298  $this->dt_obj->sub($interval);
299  }
300  return $this->getUnixTime();
301  }
302 
303  public function getUnixTime(): ?int
304  {
305  if (!$this->isNull()) {
306  return $this->dt_obj->getTimestamp();
307  }
308  return null;
309  }
310 
311  protected function parsePartsToDate(
312  int $a_year,
313  int $a_month,
314  int $a_day,
315  ?int $a_hour = null,
316  ?int $a_min = null,
317  ?int $a_sec = null,
318  ?string $a_timezone = null
319  ): ?DateTime {
320  $a_year = $a_year;
321  $a_month = $a_month;
322  $a_day = $a_day;
323 
324  if (!$a_year) {
325  return null;
326  }
327  $date = null;
328  try {
329  $a_hour = (int) $a_hour;
330  $a_min = (int) $a_min;
331  $a_sec = (int) $a_sec;
332 
333  $format = $a_year . '-' . $a_month . '-' . $a_day;
334 
335  if ($a_hour !== null) {
336  $format .= ' ' . $a_hour . ':' . $a_min . ':' . $a_sec;
337 
338  // use current timezone if no other given
339  if (!$a_timezone) {
340  $a_timezone = $this->getTimeZoneIdentifier();
341  }
342 
343  $date = new DateTime($format, new DateTimeZone($a_timezone));
344  } else {
345  $date = new DateTime($format);
346  }
347  } catch (Exception $ex) {
348  // :TODO: do anything?
349  }
350  return ($date instanceof DateTime)
351  ? $date
352  : null;
353  }
354 
360  public function setDate($a_date, int $a_format): void
361  {
362  $this->dt_obj = null;
363 
364  if (!$a_date) {
365  return;
366  }
367 
368  switch ($a_format) {
369  case IL_CAL_UNIX:
370  try {
371  $this->dt_obj = new DateTime('@' . $a_date);
372  $this->dt_obj->setTimezone(new DateTimeZone($this->getTimeZoneIdentifier()));
373  } catch (Exception $ex) {
374  $message = 'Cannot parse date: ' . $a_date . ' with format ' . $a_format;
375  $this->log->warning($message);
376  throw new ilDateTimeException($message);
377  }
378  break;
379 
380  case IL_CAL_DATETIME:
381  $matches = preg_match(
382  '/^(\d{4})-?(\d{2})-?(\d{2})([T\s]?(\d{2}):?(\d{2}):?(\d{2})(\.\d+)?(Z|[\+\-]\d{2}:?\d{2})?)$/i',
383  $a_date,
384  $d_parts
385  );
386  if ($matches < 1) {
387  $this->log->warning('Cannot parse date: ' . $a_date);
388  $this->log->warning(print_r($matches, true));
389  $this->log->logStack(ilLogLevel::WARNING);
390  throw new ilDateTimeException('Cannot parse date: ' . $a_date);
391  }
392 
393  $tz_id = (isset($d_parts[9]) && $d_parts[9] === 'Z')
394  ? 'UTC'
395  : $this->getTimeZoneIdentifier();
396  $this->dt_obj = $this->parsePartsToDate(
397  (int) $d_parts[1],
398  (int) $d_parts[2],
399  (int) $d_parts[3],
400  (int) $d_parts[5],
401  (int) $d_parts[6],
402  (int) $d_parts[7],
403  $tz_id
404  );
405  break;
406 
407  case IL_CAL_DATE:
408  try {
409  // Pure dates are not timezone sensible.
410  $this->dt_obj = new DateTime($a_date, new DateTimeZone('UTC'));
411  } catch (Exception $ex) {
412  $this->log->warning('Cannot parse date : ' . $a_date);
413  throw new ilDateTimeException('Cannot parse date: ' . $a_date);
414  }
415  break;
416 
417  case IL_CAL_FKT_GETDATE:
418  // Format like getdate parameters
419  $this->dt_obj = $this->parsePartsToDate(
420  (int) $a_date['year'],
421  (int) $a_date['mon'],
422  (int) $a_date['mday'],
423  (int) $a_date['hours'],
424  (int) $a_date['minutes'],
425  (int) ($a_date['seconds'] ?? 0),
426  $this->getTimeZoneIdentifier()
427  );
428  break;
429 
430  case IL_CAL_TIMESTAMP:
431  if (!preg_match("/(\d{4})(\d{2})(\d{2})(\d{2})(\d{2})(\d{2})/", $a_date, $d_parts)) {
432  $this->log->warning('Cannot parse date: ' . $a_date);
433  throw new ilDateTimeException('Cannot parse date.');
434  }
435  $this->dt_obj = $this->parsePartsToDate(
436  (int) $d_parts[1],
437  (int) $d_parts[2],
438  (int) $d_parts[3],
439  (int) $d_parts[4],
440  (int) $d_parts[5],
441  (int) $d_parts[6],
442  $this->getTimeZoneIdentifier()
443  );
444  break;
445 
446  case IL_CAL_ISO_8601:
451  $this->dt_obj = DateTime::createFromFormat(
452  DateTime::ATOM,
453  $a_date,
454  new DateTimeZone($this->getTimeZoneIdentifier())
455  );
456  break;
457  }
458  // remove set timezone since it does not influence the internal date.
459  // the tz must be passed in the moment of the creation of the date object.
460  }
461 
466  public function get(int $a_format, string $a_format_str = '', string $a_tz = '')
467  {
468  if ($this->isNull()) {
469  return null;
470  }
471 
472  $timezone = $this->default_timezone;
473  if ($a_tz) {
474  try {
475  $timezone = ilTimeZone::_getInstance($a_tz);
476  } catch (ilTimeZoneException $exc) {
477  $this->log->warning('Invalid timezone given. Timezone: ' . $a_tz);
478  }
479  }
480  $out_date = clone($this->dt_obj);
481  $out_date->setTimezone(new DateTimeZone($timezone->getIdentifier()));
482 
483  $date = null;
484  switch ($a_format) {
485  case IL_CAL_UNIX:
486  // timezone unrelated
487  $date = $this->getUnixTime();
488  break;
489 
490  case IL_CAL_DATE:
491  $date = $out_date->format('Y-m-d');
492  break;
493 
494  case IL_CAL_DATETIME:
495  $date = $out_date->format('Y-m-d H:i:s');
496  break;
497 
498  case IL_CAL_FKT_DATE:
499  $date = $out_date->format($a_format_str);
500  break;
501 
502  case IL_CAL_FKT_GETDATE:
503  $date = array(
504  'seconds' => (int) $out_date->format('s')
505  ,
506  'minutes' => (int) $out_date->format('i')
507  ,
508  'hours' => (int) $out_date->format('G')
509  ,
510  'mday' => (int) $out_date->format('j')
511  ,
512  'wday' => (int) $out_date->format('w')
513  ,
514  'mon' => (int) $out_date->format('n')
515  ,
516  'year' => (int) $out_date->format('Y')
517  ,
518  'yday' => (int) $out_date->format('z')
519  ,
520  'weekday' => $out_date->format('l')
521  ,
522  'month' => $out_date->format('F')
523  ,
524  'isoday' => (int) $out_date->format('N')
525  );
526  break;
527 
528  case IL_CAL_ISO_8601:
529  $date = $out_date->format('c');
530  break;
531 
532  case IL_CAL_TIMESTAMP:
533  $date = $out_date->format('YmdHis');
534  break;
535  }
536  return $date;
537  }
538 
546  public function __toString(): string
547  {
548  return $this->get(IL_CAL_DATETIME) . '<br>';
549  }
550 }
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
$format
Definition: metadata.php:235
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