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] [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
   4  
   5  use DateInterval;
   6  use DateTime;
   7  use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
   8  use PhpOffice\PhpSpreadsheet\Calculation\Exception;
   9  use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
  10  use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
  11  
  12  class Difference
  13  {
  14      use ArrayEnabled;
  15  
  16      /**
  17       * DATEDIF.
  18       *
  19       * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
  20       *                                    or a standard date string
  21       *                         Or can be an array of date values
  22       * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
  23       *                                    or a standard date string
  24       *                         Or can be an array of date values
  25       * @param array|string $unit
  26       *                         Or can be an array of unit values
  27       *
  28       * @return array|int|string Interval between the dates
  29       *         If an array of values is passed for the $startDate or $endDays,arguments, then the returned result
  30       *            will also be an array with matching dimensions
  31       */
  32      public static function interval($startDate, $endDate, $unit = 'D')
  33      {
  34          if (is_array($startDate) || is_array($endDate) || is_array($unit)) {
  35              return self::evaluateArrayArguments([self::class, __FUNCTION__], $startDate, $endDate, $unit);
  36          }
  37  
  38          try {
  39              $startDate = Helpers::getDateValue($startDate);
  40              $endDate = Helpers::getDateValue($endDate);
  41              $difference = self::initialDiff($startDate, $endDate);
  42              $unit = strtoupper($unit);
  43          } catch (Exception $e) {
  44              return $e->getMessage();
  45          }
  46  
  47          // Execute function
  48          $PHPStartDateObject = SharedDateHelper::excelToDateTimeObject($startDate);
  49          $startDays = (int) $PHPStartDateObject->format('j');
  50          $startMonths = (int) $PHPStartDateObject->format('n');
  51          $startYears = (int) $PHPStartDateObject->format('Y');
  52  
  53          $PHPEndDateObject = SharedDateHelper::excelToDateTimeObject($endDate);
  54          $endDays = (int) $PHPEndDateObject->format('j');
  55          $endMonths = (int) $PHPEndDateObject->format('n');
  56          $endYears = (int) $PHPEndDateObject->format('Y');
  57  
  58          $PHPDiffDateObject = $PHPEndDateObject->diff($PHPStartDateObject);
  59  
  60          $retVal = false;
  61          $retVal = self::replaceRetValue($retVal, $unit, 'D') ?? self::datedifD($difference);
  62          $retVal = self::replaceRetValue($retVal, $unit, 'M') ?? self::datedifM($PHPDiffDateObject);
  63          $retVal = self::replaceRetValue($retVal, $unit, 'MD') ?? self::datedifMD($startDays, $endDays, $PHPEndDateObject, $PHPDiffDateObject);
  64          $retVal = self::replaceRetValue($retVal, $unit, 'Y') ?? self::datedifY($PHPDiffDateObject);
  65          $retVal = self::replaceRetValue($retVal, $unit, 'YD') ?? self::datedifYD($difference, $startYears, $endYears, $PHPStartDateObject, $PHPEndDateObject);
  66          $retVal = self::replaceRetValue($retVal, $unit, 'YM') ?? self::datedifYM($PHPDiffDateObject);
  67  
  68          return is_bool($retVal) ? ExcelError::VALUE() : $retVal;
  69      }
  70  
  71      private static function initialDiff(float $startDate, float $endDate): float
  72      {
  73          // Validate parameters
  74          if ($startDate > $endDate) {
  75              throw new Exception(ExcelError::NAN());
  76          }
  77  
  78          return $endDate - $startDate;
  79      }
  80  
  81      /**
  82       * Decide whether it's time to set retVal.
  83       *
  84       * @param bool|int $retVal
  85       *
  86       * @return null|bool|int
  87       */
  88      private static function replaceRetValue($retVal, string $unit, string $compare)
  89      {
  90          if ($retVal !== false || $unit !== $compare) {
  91              return $retVal;
  92          }
  93  
  94          return null;
  95      }
  96  
  97      private static function datedifD(float $difference): int
  98      {
  99          return (int) $difference;
 100      }
 101  
 102      private static function datedifM(DateInterval $PHPDiffDateObject): int
 103      {
 104          return 12 * (int) $PHPDiffDateObject->format('%y') + (int) $PHPDiffDateObject->format('%m');
 105      }
 106  
 107      private static function datedifMD(int $startDays, int $endDays, DateTime $PHPEndDateObject, DateInterval $PHPDiffDateObject): int
 108      {
 109          if ($endDays < $startDays) {
 110              $retVal = $endDays;
 111              $PHPEndDateObject->modify('-' . $endDays . ' days');
 112              $adjustDays = (int) $PHPEndDateObject->format('j');
 113              $retVal += ($adjustDays - $startDays);
 114          } else {
 115              $retVal = (int) $PHPDiffDateObject->format('%d');
 116          }
 117  
 118          return $retVal;
 119      }
 120  
 121      private static function datedifY(DateInterval $PHPDiffDateObject): int
 122      {
 123          return (int) $PHPDiffDateObject->format('%y');
 124      }
 125  
 126      private static function datedifYD(float $difference, int $startYears, int $endYears, DateTime $PHPStartDateObject, DateTime $PHPEndDateObject): int
 127      {
 128          $retVal = (int) $difference;
 129          if ($endYears > $startYears) {
 130              $isLeapStartYear = $PHPStartDateObject->format('L');
 131              $wasLeapEndYear = $PHPEndDateObject->format('L');
 132  
 133              // Adjust end year to be as close as possible as start year
 134              while ($PHPEndDateObject >= $PHPStartDateObject) {
 135                  $PHPEndDateObject->modify('-1 year');
 136                  $endYears = $PHPEndDateObject->format('Y');
 137              }
 138              $PHPEndDateObject->modify('+1 year');
 139  
 140              // Get the result
 141              $retVal = $PHPEndDateObject->diff($PHPStartDateObject)->days;
 142  
 143              // Adjust for leap years cases
 144              $isLeapEndYear = $PHPEndDateObject->format('L');
 145              $limit = new DateTime($PHPEndDateObject->format('Y-02-29'));
 146              if (!$isLeapStartYear && !$wasLeapEndYear && $isLeapEndYear && $PHPEndDateObject >= $limit) {
 147                  --$retVal;
 148              }
 149          }
 150  
 151          return (int) $retVal;
 152      }
 153  
 154      private static function datedifYM(DateInterval $PHPDiffDateObject): int
 155      {
 156          return (int) $PHPDiffDateObject->format('%m');
 157      }
 158  }