ILIAS  release_5-4 Revision v5.4.26-12-gabc799a52e6
Date.php
Go to the documentation of this file.
1<?php
2
4
5use DateTime;
6use DateTimeInterface;
7use DateTimeZone;
11use PhpOffice\PhpSpreadsheet\Exception as PhpSpreadsheetException;
13
14class 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}
An exception for terminatinating execution or to throw for unit testing.
static getCompatibilityMode()
Return the current Compatibility Mode.
Definition: Functions.php:93
getWorksheet()
Get parent worksheet.
Definition: Cell.php:479
getCoordinate()
Get cell coordinate.
Definition: Cell.php:140
getCalculatedValue($resetLog=true)
Get calculated cell value.
Definition: Cell.php:251
static PHPToExcel($dateValue)
Convert a date from PHP to an MS Excel serialized date/time value.
Definition: Date.php:231
static dayStringToNumber($day)
Strips an ordinal from a numeric value.
Definition: Date.php:494
static stringToExcel($dateValue)
Convert a date/time string to Excel time.
Definition: Date.php:441
static formattedDateTimeFromTimestamp(string $date, string $format, ?DateTimeZone $timeZone=null)
Definition: Date.php:512
static timestampToExcel($dateValue)
Convert a Unix timestamp to an MS Excel serialized date/time value.
Definition: Date.php:270
static getDefaultOrLocalTimezone()
Return the Default timezone, or local timezone if default is not set.
Definition: Date.php:128
static setExcelCalendar($baseDate)
Set the Excel calendar (Windows 1900 or Mac 1904).
Definition: Date.php:73
const CALENDAR_WINDOWS_1900
constants
Definition: Date.php:17
static isDateTimeFormat(NumberFormat $pFormat)
Is a given number format a date/time?
Definition: Date.php:346
static getExcelCalendar()
Return the Excel calendar (Windows 1900 or Mac 1904).
Definition: Date.php:92
static dateTimeToExcel(DateTimeInterface $dateValue)
Convert a PHP DateTime object to an MS Excel serialized date/time value.
Definition: Date.php:251
static formattedPHPToExcel($year, $month, $day, $hours=0, $minutes=0, $seconds=0)
formattedPHPToExcel.
Definition: Date.php:291
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 isDateTime(Cell $pCell)
Is a given cell a date/time?
Definition: Date.php:331
static monthStringToNumber($month)
Converts a month name (either a long or a short name) to a month number.
Definition: Date.php:474
static excelToTimestamp($excelTimestamp, $timeZone=null)
Convert a MS serialized datetime value from Excel to a unix timestamp.
Definition: Date.php:217
static excelToDateTimeObject($excelTimestamp, $timeZone=null)
Convert a MS serialized datetime value from Excel to a PHP Date/Time object.
Definition: Date.php:170
static validateTimeZone($timeZone)
Validate a timezone.
Definition: Date.php:148
static isDateTimeFormatCode($pFormatCode)
Is a given number format code a date/time?
Definition: Date.php:360
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
$format
Definition: metadata.php:141