Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.2.x will end 22 April 2024 (12 months).
  • Bug fixes for security issues in 4.2.x will end 7 October 2024 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.1.x is supported too.

Differences Between: [Versions 400 and 402]

   1  <?php
   2  
   3  namespace PhpOffice\PhpSpreadsheet\Calculation\DateTimeExcel;
   4  
   5  use DateTime;
   6  use PhpOffice\PhpSpreadsheet\Calculation\ArrayEnabled;
   7  use PhpOffice\PhpSpreadsheet\Calculation\Exception;
   8  use PhpOffice\PhpSpreadsheet\Calculation\Information\ExcelError;
   9  use PhpOffice\PhpSpreadsheet\Shared\Date as SharedDateHelper;
  10  
  11  class Week
  12  {
  13      use ArrayEnabled;
  14  
  15      /**
  16       * WEEKNUM.
  17       *
  18       * Returns the week of the year for a specified date.
  19       * The WEEKNUM function considers the week containing January 1 to be the first week of the year.
  20       * However, there is a European standard that defines the first week as the one with the majority
  21       * of days (four or more) falling in the new year. This means that for years in which there are
  22       * three days or less in the first week of January, the WEEKNUM function returns week numbers
  23       * that are incorrect according to the European standard.
  24       *
  25       * Excel Function:
  26       *        WEEKNUM(dateValue[,style])
  27       *
  28       * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
  29       *                                    PHP DateTime object, or a standard date string
  30       *                         Or can be an array of date values
  31       * @param array|int $method Week begins on Sunday or Monday
  32       *                                        1 or omitted    Week begins on Sunday.
  33       *                                        2                Week begins on Monday.
  34       *                                        11               Week begins on Monday.
  35       *                                        12               Week begins on Tuesday.
  36       *                                        13               Week begins on Wednesday.
  37       *                                        14               Week begins on Thursday.
  38       *                                        15               Week begins on Friday.
  39       *                                        16               Week begins on Saturday.
  40       *                                        17               Week begins on Sunday.
  41       *                                        21               ISO (Jan. 4 is week 1, begins on Monday).
  42       *                         Or can be an array of methods
  43       *
  44       * @return array|int|string Week Number
  45       *         If an array of values is passed as the argument, then the returned result will also be an array
  46       *            with the same dimensions
  47       */
  48      public static function number($dateValue, $method = Constants::STARTWEEK_SUNDAY)
  49      {
  50          if (is_array($dateValue) || is_array($method)) {
  51              return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $method);
  52          }
  53  
  54          $origDateValueNull = empty($dateValue);
  55  
  56          try {
  57              $method = self::validateMethod($method);
  58              if ($dateValue === null) { // boolean not allowed
  59                  $dateValue = (SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 || $method === Constants::DOW_SUNDAY) ? 0 : 1;
  60              }
  61              $dateValue = self::validateDateValue($dateValue);
  62              if (!$dateValue && self::buggyWeekNum1900($method)) {
  63                  // This seems to be an additional Excel bug.
  64                  return 0;
  65              }
  66          } catch (Exception $e) {
  67              return $e->getMessage();
  68          }
  69  
  70          // Execute function
  71          $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
  72          if ($method == Constants::STARTWEEK_MONDAY_ISO) {
  73              Helpers::silly1900($PHPDateObject);
  74  
  75              return (int) $PHPDateObject->format('W');
  76          }
  77          if (self::buggyWeekNum1904($method, $origDateValueNull, $PHPDateObject)) {
  78              return 0;
  79          }
  80          Helpers::silly1900($PHPDateObject, '+ 5 years'); // 1905 calendar matches
  81          $dayOfYear = (int) $PHPDateObject->format('z');
  82          $PHPDateObject->modify('-' . $dayOfYear . ' days');
  83          $firstDayOfFirstWeek = (int) $PHPDateObject->format('w');
  84          $daysInFirstWeek = (6 - $firstDayOfFirstWeek + $method) % 7;
  85          $daysInFirstWeek += 7 * !$daysInFirstWeek;
  86          $endFirstWeek = $daysInFirstWeek - 1;
  87          $weekOfYear = floor(($dayOfYear - $endFirstWeek + 13) / 7);
  88  
  89          return (int) $weekOfYear;
  90      }
  91  
  92      /**
  93       * ISOWEEKNUM.
  94       *
  95       * Returns the ISO 8601 week number of the year for a specified date.
  96       *
  97       * Excel Function:
  98       *        ISOWEEKNUM(dateValue)
  99       *
 100       * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
 101       *                                    PHP DateTime object, or a standard date string
 102       *                         Or can be an array of date values
 103       *
 104       * @return array|int|string Week Number
 105       *         If an array of numbers is passed as the argument, then the returned result will also be an array
 106       *            with the same dimensions
 107       */
 108      public static function isoWeekNumber($dateValue)
 109      {
 110          if (is_array($dateValue)) {
 111              return self::evaluateSingleArgumentArray([self::class, __FUNCTION__], $dateValue);
 112          }
 113  
 114          if (self::apparentBug($dateValue)) {
 115              return 52;
 116          }
 117  
 118          try {
 119              $dateValue = Helpers::getDateValue($dateValue);
 120          } catch (Exception $e) {
 121              return $e->getMessage();
 122          }
 123  
 124          // Execute function
 125          $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
 126          Helpers::silly1900($PHPDateObject);
 127  
 128          return (int) $PHPDateObject->format('W');
 129      }
 130  
 131      /**
 132       * WEEKDAY.
 133       *
 134       * Returns the day of the week for a specified date. The day is given as an integer
 135       * ranging from 0 to 7 (dependent on the requested style).
 136       *
 137       * Excel Function:
 138       *        WEEKDAY(dateValue[,style])
 139       *
 140       * @param null|array|float|int|string $dateValue Excel date serial value (float), PHP date timestamp (integer),
 141       *                                    PHP DateTime object, or a standard date string
 142       *                         Or can be an array of date values
 143       * @param mixed $style A number that determines the type of return value
 144       *                                        1 or omitted    Numbers 1 (Sunday) through 7 (Saturday).
 145       *                                        2                Numbers 1 (Monday) through 7 (Sunday).
 146       *                                        3                Numbers 0 (Monday) through 6 (Sunday).
 147       *                         Or can be an array of styles
 148       *
 149       * @return array|int|string Day of the week value
 150       *         If an array of values is passed as the argument, then the returned result will also be an array
 151       *            with the same dimensions
 152       */
 153      public static function day($dateValue, $style = 1)
 154      {
 155          if (is_array($dateValue) || is_array($style)) {
 156              return self::evaluateArrayArguments([self::class, __FUNCTION__], $dateValue, $style);
 157          }
 158  
 159          try {
 160              $dateValue = Helpers::getDateValue($dateValue);
 161              $style = self::validateStyle($style);
 162          } catch (Exception $e) {
 163              return $e->getMessage();
 164          }
 165  
 166          // Execute function
 167          $PHPDateObject = SharedDateHelper::excelToDateTimeObject($dateValue);
 168          Helpers::silly1900($PHPDateObject);
 169          $DoW = (int) $PHPDateObject->format('w');
 170  
 171          switch ($style) {
 172              case 1:
 173                  ++$DoW;
 174  
 175                  break;
 176              case 2:
 177                  $DoW = self::dow0Becomes7($DoW);
 178  
 179                  break;
 180              case 3:
 181                  $DoW = self::dow0Becomes7($DoW) - 1;
 182  
 183                  break;
 184          }
 185  
 186          return $DoW;
 187      }
 188  
 189      /**
 190       * @param mixed $style expect int
 191       */
 192      private static function validateStyle($style): int
 193      {
 194          if (!is_numeric($style)) {
 195              throw new Exception(ExcelError::VALUE());
 196          }
 197          $style = (int) $style;
 198          if (($style < 1) || ($style > 3)) {
 199              throw new Exception(ExcelError::NAN());
 200          }
 201  
 202          return $style;
 203      }
 204  
 205      private static function dow0Becomes7(int $DoW): int
 206      {
 207          return ($DoW === 0) ? 7 : $DoW;
 208      }
 209  
 210      /**
 211       * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
 212       *                                    PHP DateTime object, or a standard date string
 213       */
 214      private static function apparentBug($dateValue): bool
 215      {
 216          if (SharedDateHelper::getExcelCalendar() !== SharedDateHelper::CALENDAR_MAC_1904) {
 217              if (is_bool($dateValue)) {
 218                  return true;
 219              }
 220              if (is_numeric($dateValue) && !((int) $dateValue)) {
 221                  return true;
 222              }
 223          }
 224  
 225          return false;
 226      }
 227  
 228      /**
 229       * Validate dateValue parameter.
 230       *
 231       * @param mixed $dateValue
 232       */
 233      private static function validateDateValue($dateValue): float
 234      {
 235          if (is_bool($dateValue)) {
 236              throw new Exception(ExcelError::VALUE());
 237          }
 238  
 239          return Helpers::getDateValue($dateValue);
 240      }
 241  
 242      /**
 243       * Validate method parameter.
 244       *
 245       * @param mixed $method
 246       */
 247      private static function validateMethod($method): int
 248      {
 249          if ($method === null) {
 250              $method = Constants::STARTWEEK_SUNDAY;
 251          }
 252  
 253          if (!is_numeric($method)) {
 254              throw new Exception(ExcelError::VALUE());
 255          }
 256  
 257          $method = (int) $method;
 258          if (!array_key_exists($method, Constants::METHODARR)) {
 259              throw new Exception(ExcelError::NAN());
 260          }
 261          $method = Constants::METHODARR[$method];
 262  
 263          return $method;
 264      }
 265  
 266      private static function buggyWeekNum1900(int $method): bool
 267      {
 268          return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_WINDOWS_1900;
 269      }
 270  
 271      private static function buggyWeekNum1904(int $method, bool $origNull, DateTime $dateObject): bool
 272      {
 273          // This appears to be another Excel bug.
 274  
 275          return $method === Constants::DOW_SUNDAY && SharedDateHelper::getExcelCalendar() === SharedDateHelper::CALENDAR_MAC_1904 &&
 276              !$origNull && $dateObject->format('Y-m-d') === '1904-01-01';
 277      }
 278  }