Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 400 and 401]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
   4  
   5  use DateTime;
   6  use PhpOffice\PhpSpreadsheet\Calculation\Exception;
   7  use PhpOffice\PhpSpreadsheet\Calculation\Functions;
   8  use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
   9  use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
  10  
  11  class Helpers
  12  {
  13      /**
  14       * Identify if a year is a leap year or not.
  15       *
  16       * @param int|string $year The year to test
  17       *
  18       * @return bool TRUE if the year is a leap year, otherwise FALSE
  19       */
  20      public static function isLeapYear($year): bool
  21      {
  22          return (($year % 4) === 0) && (($year % 100) !== 0) || (($year % 400) === 0);
  23      }
  24  
  25      /**
  26       * getDateValue.
  27       *
  28       * @param mixed $dateValue
  29       *
  30       * @return float Excel date/time serial value
  31       */
  32      public static function getDateValue($dateValue, bool $allowBool = true): float
  33      {
  34          if (is_object($dateValue)) {
  35              $retval = SharedDateHelper::PHPToExcel($dateValue);
  36              if (is_bool($retval)) {
  37                  throw new Exception(ExcelError::VALUE());
  38              }
  39  
  40              return $retval;
  41          }
  42  
  43          self::nullFalseTrueToNumber($dateValue, $allowBool);
  44          if (!is_numeric($dateValue)) {
  45              $saveReturnDateType = Functions::getReturnDateType();
  46              Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
  47              $dateValue = DateValue::fromString($dateValue);
  48              Functions::setReturnDateType($saveReturnDateType);
  49              if (!is_numeric($dateValue)) {
  50                  throw new Exception(ExcelError::VALUE());
  51              }
  52          }
  53          if ($dateValue < 0 && Functions::getCompatibilityMode() !== Functions::COMPATIBILITY_OPENOFFICE) {
  54              throw new Exception(ExcelError::NAN());
  55          }
  56  
  57          return (float) $dateValue;
  58      }
  59  
  60      /**
  61       * getTimeValue.
  62       *
  63       * @param string $timeValue
  64       *
  65       * @return mixed Excel date/time serial value, or string if error
  66       */
  67      public static function getTimeValue($timeValue)
  68      {
  69          $saveReturnDateType = Functions::getReturnDateType();
  70          Functions::setReturnDateType(Functions::RETURNDATE_EXCEL);
  71          $timeValue = TimeValue::fromString($timeValue);
  72          Functions::setReturnDateType($saveReturnDateType);
  73  
  74          return $timeValue;
  75      }
  76  
  77      /**
  78       * Adjust date by given months.
  79       *
  80       * @param mixed $dateValue
  81       */
  82      public static function adjustDateByMonths($dateValue = 0, float $adjustmentMonths = 0): DateTime
  83      {
  84          // Execute function
  85          $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
  86          $oMonth = (int) $PHPDateObject->format('m');
  87          $oYear = (int) $PHPDateObject->format('Y');
  88  
  89          $adjustmentMonthsString = (string) $adjustmentMonths;
  90          if ($adjustmentMonths > 0) {
  91              $adjustmentMonthsString = '+' . $adjustmentMonths;
  92          }
  93          if ($adjustmentMonths != 0) {
  94              $PHPDateObject->modify($adjustmentMonthsString . ' months');
  95          }
  96          $nMonth = (int) $PHPDateObject->format('m');
  97          $nYear = (int) $PHPDateObject->format('Y');
  98  
  99          $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
 100          if ($monthDiff != $adjustmentMonths) {
 101              $adjustDays = (int) $PHPDateObject->format('d');
 102              $adjustDaysString = '-' . $adjustDays . ' days';
 103              $PHPDateObject->modify($adjustDaysString);
 104          }
 105  
 106          return $PHPDateObject;
 107      }
 108  
 109      /**
 110       * Help reduce perceived complexity of some tests.
 111       *
 112       * @param mixed $value
 113       * @param mixed $altValue
 114       */
 115      public static function replaceIfEmpty(&$value, $altValue): void
 116      {
 117          $value = $value ?: $altValue;
 118      }
 119  
 120      /**
 121       * Adjust year in ambiguous situations.
 122       */
 123      public static function adjustYear(string $testVal1, string $testVal2, string &$testVal3): void
 124      {
 125          if (!is_numeric($testVal1) || $testVal1 < 31) {
 126              if (!is_numeric($testVal2) || $testVal2 < 12) {
 127                  if (is_numeric($testVal3) && $testVal3 < 12) {
 128                      $testVal3 += 2000;
 129                  }
 130              }
 131          }
 132      }
 133  
 134      /**
 135       * Return result in one of three formats.
 136       *
 137       * @return mixed
 138       */
 139      public static function returnIn3FormatsArray(array $dateArray, bool $noFrac = false)
 140      {
 141          $retType = Functions::getReturnDateType();
 142          if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
 143              return new DateTime(
 144                  $dateArray['year']
 145                  . '-' . $dateArray['month']
 146                  . '-' . $dateArray['day']
 147                  . ' ' . $dateArray['hour']
 148                  . ':' . $dateArray['minute']
 149                  . ':' . $dateArray['second']
 150              );
 151          }
 152          $excelDateValue =
 153              SharedDateHelper::formattedPHPToExcel(
 154                  $dateArray['year'],
 155                  $dateArray['month'],
 156                  $dateArray['day'],
 157                  $dateArray['hour'],
 158                  $dateArray['minute'],
 159                  $dateArray['second']
 160              );
 161          if ($retType === Functions::RETURNDATE_EXCEL) {
 162              return $noFrac ? floor($excelDateValue) : (float) $excelDateValue;
 163          }
 164          // RETURNDATE_UNIX_TIMESTAMP)
 165  
 166          return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
 167      }
 168  
 169      /**
 170       * Return result in one of three formats.
 171       *
 172       * @return mixed
 173       */
 174      public static function returnIn3FormatsFloat(float $excelDateValue)
 175      {
 176          $retType = Functions::getReturnDateType();
 177          if ($retType === Functions::RETURNDATE_EXCEL) {
 178              return $excelDateValue;
 179          }
 180          if ($retType === Functions::RETURNDATE_UNIX_TIMESTAMP) {
 181              return (int) SharedDateHelper::excelToTimestamp($excelDateValue);
 182          }
 183          // RETURNDATE_PHP_DATETIME_OBJECT
 184  
 185          return SharedDateHelper::excelToDateTimeObject($excelDateValue);
 186      }
 187  
 188      /**
 189       * Return result in one of three formats.
 190       *
 191       * @return mixed
 192       */
 193      public static function returnIn3FormatsObject(DateTime $PHPDateObject)
 194      {
 195          $retType = Functions::getReturnDateType();
 196          if ($retType === Functions::RETURNDATE_PHP_DATETIME_OBJECT) {
 197              return $PHPDateObject;
 198          }
 199          if ($retType === Functions::RETURNDATE_EXCEL) {
 200              return (float) SharedDateHelper::PHPToExcel($PHPDateObject);
 201          }
 202          // RETURNDATE_UNIX_TIMESTAMP
 203          $stamp = SharedDateHelper::PHPToExcel($PHPDateObject);
 204          $stamp = is_bool($stamp) ? ((int) $stamp) : $stamp;
 205  
 206          return (int) SharedDateHelper::excelToTimestamp($stamp);
 207      }
 208  
 209      private static function baseDate(): int
 210      {
 211          if (Functions::getCompatibilityMode() === Functions::COMPATIBILITY_OPENOFFICE) {
 212              return 0;
 213          }
 214          if (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904) {
 215              return 0;
 216          }
 217  
 218          return 1;
 219      }
 220  
 221      /**
 222       * Many functions accept null/false/true argument treated as 0/0/1.
 223       *
 224       * @param mixed $number
 225       */
 226      public static function nullFalseTrueToNumber(&$number, bool $allowBool = true): void
 227      {
 228          $number = Functions::flattenSingleValue($number);
 229          $nullVal = self::baseDate();
 230          if ($number === null) {
 231              $number = $nullVal;
 232          } elseif ($allowBool && is_bool($number)) {
 233              $number = $nullVal + (int) $number;
 234          }
 235      }
 236  
 237      /**
 238       * Many functions accept null argument treated as 0.
 239       *
 240       * @param mixed $number
 241       *
 242       * @return float|int
 243       */
 244      public static function validateNumericNull($number)
 245      {
 246          $number = Functions::flattenSingleValue($number);
 247          if ($number === null) {
 248              return 0;
 249          }
 250          if (is_int($number)) {
 251              return $number;
 252          }
 253          if (is_numeric($number)) {
 254              return (float) $number;
 255          }
 256  
 257          throw new Exception(ExcelError::VALUE());
 258      }
 259  
 260      /**
 261       * Many functions accept null/false/true argument treated as 0/0/1.
 262       *
 263       * @param mixed $number
 264       *
 265       * @return float
 266       */
 267      public static function validateNotNegative($number)
 268      {
 269          if (!is_numeric($number)) {
 270              throw new Exception(ExcelError::VALUE());
 271          }
 272          if ($number >= 0) {
 273              return (float) $number;
 274          }
 275  
 276          throw new Exception(ExcelError::NAN());
 277      }
 278  
 279      public static function silly1900(DateTime $PHPDateObject, string $mod = '-1 day'): void
 280      {
 281          $isoDate = $PHPDateObject->format('c');
 282          if ($isoDate < '1900-03-01') {
 283              $PHPDateObject->modify($mod);
 284          }
 285      }
 286  
 287      public static function dateParse(string $string): array
 288      {
 289          return self::forceArray(date_parse($string));
 290      }
 291  
 292      public static function dateParseSucceeded(array $dateArray): bool
 293      {
 294          return $dateArray['error_count'] === 0;
 295      }
 296  
 297      /**
 298       * Despite documentation, date_parse probably never returns false.
 299       * Just in case, this routine helps guarantee it.
 300       *
 301       * @param array|false $dateArray
 302       */
 303      private static function forceArray($dateArray): array
 304      {
 305          return is_array($dateArray) ? $dateArray : ['error_count' => 1];
 306      }
 307  }