Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.0.x will end 8 May 2023 (12 months).
  • Bug fixes for security issues in 4.0.x will end 13 November 2023 (18 months).
  • PHP version: minimum PHP 7.3.0 Note: the minimum PHP version has increased since Moodle 3.10. PHP 7.4.x is also supported.

Differences Between: [Versions 400 and 401] [Versions 400 and 402] [Versions 400 and 403]

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