ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Date.php
Go to the documentation of this file.
1 <?php
2 
4 
5 use DateTime;
7 use DateTimeZone;
13 
14 class Date
15 {
17  const CALENDAR_WINDOWS_1900 = 1900; // Base date of 1st Jan 1900 = 1.0
18  const CALENDAR_MAC_1904 = 1904; // Base date of 2nd Jan 1904 = 1.0
19 
26  public static $monthNames = [
27  'Jan' => 'January',
28  'Feb' => 'February',
29  'Mar' => 'March',
30  'Apr' => 'April',
31  'May' => 'May',
32  'Jun' => 'June',
33  'Jul' => 'July',
34  'Aug' => 'August',
35  'Sep' => 'September',
36  'Oct' => 'October',
37  'Nov' => 'November',
38  'Dec' => 'December',
39  ];
40 
44  public static $numberSuffixes = [
45  'st',
46  'nd',
47  'rd',
48  'th',
49  ];
50 
57  protected static $excelCalendar = self::CALENDAR_WINDOWS_1900;
58 
64  protected static $defaultTimeZone;
65 
73  public static function setExcelCalendar($baseDate)
74  {
75  if (
76  ($baseDate == self::CALENDAR_WINDOWS_1900) ||
77  ($baseDate == self::CALENDAR_MAC_1904)
78  ) {
79  self::$excelCalendar = $baseDate;
80 
81  return true;
82  }
83 
84  return false;
85  }
86 
92  public static function getExcelCalendar()
93  {
94  return self::$excelCalendar;
95  }
96 
104  public static function setDefaultTimezone($timeZone)
105  {
106  try {
107  $timeZone = self::validateTimeZone($timeZone);
108  self::$defaultTimeZone = $timeZone;
109  $retval = true;
110  } catch (PhpSpreadsheetException $e) {
111  $retval = false;
112  }
113 
114  return $retval;
115  }
116 
120  public static function getDefaultTimezone(): DateTimeZone
121  {
122  return self::$defaultTimeZone ?? new DateTimeZone('UTC');
123  }
124 
128  public static function getDefaultOrLocalTimezone(): DateTimeZone
129  {
130  return self::$defaultTimeZone ?? new DateTimeZone(date_default_timezone_get());
131  }
132 
136  public static function getDefaultTimezoneOrNull(): ?DateTimeZone
137  {
138  return self::$defaultTimeZone;
139  }
140 
148  private static function validateTimeZone($timeZone)
149  {
150  if ($timeZone instanceof DateTimeZone || $timeZone === null) {
151  return $timeZone;
152  }
153  if (in_array($timeZone, DateTimeZone::listIdentifiers(DateTimeZone::ALL_WITH_BC))) {
154  return new DateTimeZone($timeZone);
155  }
156 
157  throw new PhpSpreadsheetException('Invalid timezone');
158  }
159 
170  public static function excelToDateTimeObject($excelTimestamp, $timeZone = null)
171  {
172  $timeZone = ($timeZone === null) ? self::getDefaultTimezone() : self::validateTimeZone($timeZone);
174  if ($excelTimestamp < 1 && self::$excelCalendar === self::CALENDAR_WINDOWS_1900) {
175  // Unix timestamp base date
176  $baseDate = new DateTime('1970-01-01', $timeZone);
177  } else {
178  // MS Excel calendar base dates
179  if (self::$excelCalendar == self::CALENDAR_WINDOWS_1900) {
180  // Allow adjustment for 1900 Leap Year in MS Excel
181  $baseDate = ($excelTimestamp < 60) ? new DateTime('1899-12-31', $timeZone) : new DateTime('1899-12-30', $timeZone);
182  } else {
183  $baseDate = new DateTime('1904-01-01', $timeZone);
184  }
185  }
186  } else {
187  $baseDate = new DateTime('1899-12-30', $timeZone);
188  }
189 
190  $days = floor($excelTimestamp);
191  $partDay = $excelTimestamp - $days;
192  $hours = floor($partDay * 24);
193  $partDay = $partDay * 24 - $hours;
194  $minutes = floor($partDay * 60);
195  $partDay = $partDay * 60 - $minutes;
196  $seconds = round($partDay * 60);
197 
198  if ($days >= 0) {
199  $days = '+' . $days;
200  }
201  $interval = $days . ' days';
202 
203  return $baseDate->modify($interval)
204  ->setTime((int) $hours, (int) $minutes, (int) $seconds);
205  }
206 
217  public static function excelToTimestamp($excelTimestamp, $timeZone = null)
218  {
219  return (int) self::excelToDateTimeObject($excelTimestamp, $timeZone)
220  ->format('U');
221  }
222 
231  public static function PHPToExcel($dateValue)
232  {
233  if ((is_object($dateValue)) && ($dateValue instanceof DateTimeInterface)) {
234  return self::dateTimeToExcel($dateValue);
235  } elseif (is_numeric($dateValue)) {
236  return self::timestampToExcel($dateValue);
237  } elseif (is_string($dateValue)) {
238  return self::stringToExcel($dateValue);
239  }
240 
241  return false;
242  }
243 
251  public static function dateTimeToExcel(DateTimeInterface $dateValue)
252  {
253  return self::formattedPHPToExcel(
254  (int) $dateValue->format('Y'),
255  (int) $dateValue->format('m'),
256  (int) $dateValue->format('d'),
257  (int) $dateValue->format('H'),
258  (int) $dateValue->format('i'),
259  (int) $dateValue->format('s')
260  );
261  }
262 
270  public static function timestampToExcel($dateValue)
271  {
272  if (!is_numeric($dateValue)) {
273  return false;
274  }
275 
276  return self::dateTimeToExcel(new DateTime('@' . $dateValue));
277  }
278 
291  public static function formattedPHPToExcel($year, $month, $day, $hours = 0, $minutes = 0, $seconds = 0)
292  {
293  if (self::$excelCalendar == self::CALENDAR_WINDOWS_1900) {
294  //
295  // Fudge factor for the erroneous fact that the year 1900 is treated as a Leap Year in MS Excel
296  // This affects every date following 28th February 1900
297  //
298  $excel1900isLeapYear = true;
299  if (($year == 1900) && ($month <= 2)) {
300  $excel1900isLeapYear = false;
301  }
302  $myexcelBaseDate = 2415020;
303  } else {
304  $myexcelBaseDate = 2416481;
305  $excel1900isLeapYear = false;
306  }
307 
308  // Julian base date Adjustment
309  if ($month > 2) {
310  $month -= 3;
311  } else {
312  $month += 9;
313  --$year;
314  }
315 
316  // Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0)
317  $century = (int) substr($year, 0, 2);
318  $decade = (int) substr($year, 2, 2);
319  $excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $myexcelBaseDate + $excel1900isLeapYear;
320 
321  $excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
322 
323  return (float) $excelDate + $excelTime;
324  }
325 
331  public static function isDateTime(Cell $pCell)
332  {
333  return is_numeric($pCell->getCalculatedValue()) &&
334  self::isDateTimeFormat(
335  $pCell->getWorksheet()->getStyle(
336  $pCell->getCoordinate()
337  )->getNumberFormat()
338  );
339  }
340 
346  public static function isDateTimeFormat(NumberFormat $pFormat)
347  {
348  return self::isDateTimeFormatCode($pFormat->getFormatCode());
349  }
350 
351  private static $possibleDateFormatCharacters = 'eymdHs';
352 
360  public static function isDateTimeFormatCode($pFormatCode)
361  {
362  if (strtolower($pFormatCode) === strtolower(NumberFormat::FORMAT_GENERAL)) {
363  // "General" contains an epoch letter 'e', so we trap for it explicitly here (case-insensitive check)
364  return false;
365  }
366  if (preg_match('/[0#]E[+-]0/i', $pFormatCode)) {
367  // Scientific format
368  return false;
369  }
370 
371  // Switch on formatcode
372  switch ($pFormatCode) {
373  // Explicitly defined date formats
396  return true;
397  }
398 
399  // Typically number, currency or accounting (or occasionally fraction) formats
400  if ((substr($pFormatCode, 0, 1) == '_') || (substr($pFormatCode, 0, 2) == '0 ')) {
401  return false;
402  }
403  // Some "special formats" provided in German Excel versions were detected as date time value,
404  // so filter them out here - "\C\H\-00000" (Switzerland) and "\D-00000" (Germany).
405  if (\strpos($pFormatCode, '-00000') !== false) {
406  return false;
407  }
408  // Try checking for any of the date formatting characters that don't appear within square braces
409  if (preg_match('/(^|\])[^\[]*[' . self::$possibleDateFormatCharacters . ']/i', $pFormatCode)) {
410  // We might also have a format mask containing quoted strings...
411  // we don't want to test for any of our characters within the quoted blocks
412  if (strpos($pFormatCode, '"') !== false) {
413  $segMatcher = false;
414  foreach (explode('"', $pFormatCode) as $subVal) {
415  // Only test in alternate array entries (the non-quoted blocks)
416  if (
417  ($segMatcher = !$segMatcher) &&
418  (preg_match('/(^|\])[^\[]*[' . self::$possibleDateFormatCharacters . ']/i', $subVal))
419  ) {
420  return true;
421  }
422  }
423 
424  return false;
425  }
426 
427  return true;
428  }
429 
430  // No date...
431  return false;
432  }
433 
441  public static function stringToExcel($dateValue)
442  {
443  if (strlen($dateValue) < 2) {
444  return false;
445  }
446  if (!preg_match('/^(\d{1,4}[ \.\/\-][A-Z]{3,9}([ \.\/\-]\d{1,4})?|[A-Z]{3,9}[ \.\/\-]\d{1,4}([ \.\/\-]\d{1,4})?|\d{1,4}[ \.\/\-]\d{1,4}([ \.\/\-]\d{1,4})?)( \d{1,2}:\d{1,2}(:\d{1,2})?)?$/iu', $dateValue)) {
447  return false;
448  }
449 
450  $dateValueNew = DateTimeExcel\DateValue::fromString($dateValue);
451 
452  if ($dateValueNew === Functions::VALUE()) {
453  return false;
454  }
455 
456  if (strpos($dateValue, ':') !== false) {
457  $timeValue = DateTimeExcel\TimeValue::fromString($dateValue);
458  if ($timeValue === Functions::VALUE()) {
459  return false;
460  }
461  $dateValueNew += $timeValue;
462  }
463 
464  return $dateValueNew;
465  }
466 
474  public static function monthStringToNumber($month)
475  {
476  $monthIndex = 1;
477  foreach (self::$monthNames as $shortMonthName => $longMonthName) {
478  if (($month === $longMonthName) || ($month === $shortMonthName)) {
479  return $monthIndex;
480  }
481  ++$monthIndex;
482  }
483 
484  return $month;
485  }
486 
494  public static function dayStringToNumber($day)
495  {
496  $strippedDayValue = (str_replace(self::$numberSuffixes, '', $day));
497  if (is_numeric($strippedDayValue)) {
498  return (int) $strippedDayValue;
499  }
500 
501  return $day;
502  }
503 
504  public static function dateTimeFromTimestamp(string $date, ?DateTimeZone $timeZone = null): DateTime
505  {
506  $dtobj = DateTime::createFromFormat('U', $date) ?: new DateTime();
507  $dtobj->setTimeZone($timeZone ?? self::getDefaultOrLocalTimezone());
508 
509  return $dtobj;
510  }
511 
512  public static function formattedDateTimeFromTimestamp(string $date, string $format, ?DateTimeZone $timeZone = null): string
513  {
514  $dtobj = self::dateTimeFromTimestamp($date, $timeZone);
515 
516  return $dtobj->format($format);
517  }
518 }
$format
Definition: metadata.php:141
getCoordinate()
Get cell coordinate.
Definition: Cell.php:140
static monthStringToNumber($month)
Converts a month name (either a long or a short name) to a month number.
Definition: Date.php:474
static excelToDateTimeObject($excelTimestamp, $timeZone=null)
Convert a MS serialized datetime value from Excel to a PHP Date/Time object.
Definition: Date.php:170
static getDefaultTimezone()
Return the Default timezone, or UTC if default not set.
Definition: Date.php:120
static getDefaultTimezoneOrNull()
Return the Default timezone even if null.
Definition: Date.php:136
static isDateTimeFormatCode($pFormatCode)
Is a given number format code a date/time?
Definition: Date.php:360
static getDefaultOrLocalTimezone()
Return the Default timezone, or local timezone if default is not set.
Definition: Date.php:128
static isDateTimeFormat(NumberFormat $pFormat)
Is a given number format a date/time?
Definition: Date.php:346
static formattedPHPToExcel($year, $month, $day, $hours=0, $minutes=0, $seconds=0)
formattedPHPToExcel.
Definition: Date.php:291
static dayStringToNumber($day)
Strips an ordinal from a numeric value.
Definition: Date.php:494
static formattedDateTimeFromTimestamp(string $date, string $format, ?DateTimeZone $timeZone=null)
Definition: Date.php:512
static dateTimeFromTimestamp(string $date, ?DateTimeZone $timeZone=null)
Definition: Date.php:504
static setDefaultTimezone($timeZone)
Set the Default timezone to use for dates.
Definition: Date.php:104
static timestampToExcel($dateValue)
Convert a Unix timestamp to an MS Excel serialized date/time value.
Definition: Date.php:270
static PHPToExcel($dateValue)
Convert a date from PHP to an MS Excel serialized date/time value.
Definition: Date.php:231
const CALENDAR_WINDOWS_1900
constants
Definition: Date.php:17
static validateTimeZone($timeZone)
Validate a timezone.
Definition: Date.php:148
static dateTimeToExcel(DateTimeInterface $dateValue)
Convert a PHP DateTime object to an MS Excel serialized date/time value.
Definition: Date.php:251
static setExcelCalendar($baseDate)
Set the Excel calendar (Windows 1900 or Mac 1904).
Definition: Date.php:73
getCalculatedValue($resetLog=true)
Get calculated cell value.
Definition: Cell.php:251
static excelToTimestamp($excelTimestamp, $timeZone=null)
Convert a MS serialized datetime value from Excel to a unix timestamp.
Definition: Date.php:217
static stringToExcel($dateValue)
Convert a date/time string to Excel time.
Definition: Date.php:441
static isDateTime(Cell $pCell)
Is a given cell a date/time?
Definition: Date.php:331
getWorksheet()
Get parent worksheet.
Definition: Cell.php:479
static getCompatibilityMode()
Return the current Compatibility Mode.
Definition: Functions.php:93
static getExcelCalendar()
Return the Excel calendar (Windows 1900 or Mac 1904).
Definition: Date.php:92