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