For God so loved the world, that He gave His only begotten Son, that all who believe in Him should not perish but have everlasting life
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

1553 lines
68 KiB

  1. <?php
  2. /** PHPExcel root directory */
  3. if (!defined('PHPEXCEL_ROOT')) {
  4. /**
  5. * @ignore
  6. */
  7. define('PHPEXCEL_ROOT', dirname(__FILE__) . '/../../');
  8. require(PHPEXCEL_ROOT . 'PHPExcel/Autoloader.php');
  9. }
  10. /**
  11. * PHPExcel_Calculation_DateTime
  12. *
  13. * Copyright (c) 2006 - 2015 PHPExcel
  14. *
  15. * This library is free software; you can redistribute it and/or
  16. * modify it under the terms of the GNU Lesser General Public
  17. * License as published by the Free Software Foundation; either
  18. * version 2.1 of the License, or (at your option) any later version.
  19. *
  20. * This library is distributed in the hope that it will be useful,
  21. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  22. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  23. * Lesser General Public License for more details.
  24. *
  25. * You should have received a copy of the GNU Lesser General Public
  26. * License along with this library; if not, write to the Free Software
  27. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  28. *
  29. * @category PHPExcel
  30. * @package PHPExcel_Calculation
  31. * @copyright Copyright (c) 2006 - 2015 PHPExcel (http://www.codeplex.com/PHPExcel)
  32. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  33. * @version ##VERSION##, ##DATE##
  34. */
  35. class PHPExcel_Calculation_DateTime
  36. {
  37. /**
  38. * Identify if a year is a leap year or not
  39. *
  40. * @param integer $year The year to test
  41. * @return boolean TRUE if the year is a leap year, otherwise FALSE
  42. */
  43. public static function isLeapYear($year)
  44. {
  45. return ((($year % 4) == 0) && (($year % 100) != 0) || (($year % 400) == 0));
  46. }
  47. /**
  48. * Return the number of days between two dates based on a 360 day calendar
  49. *
  50. * @param integer $startDay Day of month of the start date
  51. * @param integer $startMonth Month of the start date
  52. * @param integer $startYear Year of the start date
  53. * @param integer $endDay Day of month of the start date
  54. * @param integer $endMonth Month of the start date
  55. * @param integer $endYear Year of the start date
  56. * @param boolean $methodUS Whether to use the US method or the European method of calculation
  57. * @return integer Number of days between the start date and the end date
  58. */
  59. private static function dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, $methodUS)
  60. {
  61. if ($startDay == 31) {
  62. --$startDay;
  63. } elseif ($methodUS && ($startMonth == 2 && ($startDay == 29 || ($startDay == 28 && !self::isLeapYear($startYear))))) {
  64. $startDay = 30;
  65. }
  66. if ($endDay == 31) {
  67. if ($methodUS && $startDay != 30) {
  68. $endDay = 1;
  69. if ($endMonth == 12) {
  70. ++$endYear;
  71. $endMonth = 1;
  72. } else {
  73. ++$endMonth;
  74. }
  75. } else {
  76. $endDay = 30;
  77. }
  78. }
  79. return $endDay + $endMonth * 30 + $endYear * 360 - $startDay - $startMonth * 30 - $startYear * 360;
  80. }
  81. /**
  82. * getDateValue
  83. *
  84. * @param string $dateValue
  85. * @return mixed Excel date/time serial value, or string if error
  86. */
  87. public static function getDateValue($dateValue)
  88. {
  89. if (!is_numeric($dateValue)) {
  90. if ((is_string($dateValue)) &&
  91. (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC)) {
  92. return PHPExcel_Calculation_Functions::VALUE();
  93. }
  94. if ((is_object($dateValue)) && ($dateValue instanceof DateTime)) {
  95. $dateValue = PHPExcel_Shared_Date::PHPToExcel($dateValue);
  96. } else {
  97. $saveReturnDateType = PHPExcel_Calculation_Functions::getReturnDateType();
  98. PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL);
  99. $dateValue = self::DATEVALUE($dateValue);
  100. PHPExcel_Calculation_Functions::setReturnDateType($saveReturnDateType);
  101. }
  102. }
  103. return $dateValue;
  104. }
  105. /**
  106. * getTimeValue
  107. *
  108. * @param string $timeValue
  109. * @return mixed Excel date/time serial value, or string if error
  110. */
  111. private static function getTimeValue($timeValue)
  112. {
  113. $saveReturnDateType = PHPExcel_Calculation_Functions::getReturnDateType();
  114. PHPExcel_Calculation_Functions::setReturnDateType(PHPExcel_Calculation_Functions::RETURNDATE_EXCEL);
  115. $timeValue = self::TIMEVALUE($timeValue);
  116. PHPExcel_Calculation_Functions::setReturnDateType($saveReturnDateType);
  117. return $timeValue;
  118. }
  119. private static function adjustDateByMonths($dateValue = 0, $adjustmentMonths = 0)
  120. {
  121. // Execute function
  122. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  123. $oMonth = (int) $PHPDateObject->format('m');
  124. $oYear = (int) $PHPDateObject->format('Y');
  125. $adjustmentMonthsString = (string) $adjustmentMonths;
  126. if ($adjustmentMonths > 0) {
  127. $adjustmentMonthsString = '+'.$adjustmentMonths;
  128. }
  129. if ($adjustmentMonths != 0) {
  130. $PHPDateObject->modify($adjustmentMonthsString.' months');
  131. }
  132. $nMonth = (int) $PHPDateObject->format('m');
  133. $nYear = (int) $PHPDateObject->format('Y');
  134. $monthDiff = ($nMonth - $oMonth) + (($nYear - $oYear) * 12);
  135. if ($monthDiff != $adjustmentMonths) {
  136. $adjustDays = (int) $PHPDateObject->format('d');
  137. $adjustDaysString = '-'.$adjustDays.' days';
  138. $PHPDateObject->modify($adjustDaysString);
  139. }
  140. return $PHPDateObject;
  141. }
  142. /**
  143. * DATETIMENOW
  144. *
  145. * Returns the current date and time.
  146. * The NOW function is useful when you need to display the current date and time on a worksheet or
  147. * calculate a value based on the current date and time, and have that value updated each time you
  148. * open the worksheet.
  149. *
  150. * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
  151. * and time format of your regional settings. PHPExcel does not change cell formatting in this way.
  152. *
  153. * Excel Function:
  154. * NOW()
  155. *
  156. * @access public
  157. * @category Date/Time Functions
  158. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  159. * depending on the value of the ReturnDateType flag
  160. */
  161. public static function DATETIMENOW()
  162. {
  163. $saveTimeZone = date_default_timezone_get();
  164. date_default_timezone_set('UTC');
  165. $retValue = false;
  166. switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
  167. case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
  168. $retValue = (float) PHPExcel_Shared_Date::PHPToExcel(time());
  169. break;
  170. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
  171. $retValue = (integer) time();
  172. break;
  173. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
  174. $retValue = new DateTime();
  175. break;
  176. }
  177. date_default_timezone_set($saveTimeZone);
  178. return $retValue;
  179. }
  180. /**
  181. * DATENOW
  182. *
  183. * Returns the current date.
  184. * The NOW function is useful when you need to display the current date and time on a worksheet or
  185. * calculate a value based on the current date and time, and have that value updated each time you
  186. * open the worksheet.
  187. *
  188. * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
  189. * and time format of your regional settings. PHPExcel does not change cell formatting in this way.
  190. *
  191. * Excel Function:
  192. * TODAY()
  193. *
  194. * @access public
  195. * @category Date/Time Functions
  196. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  197. * depending on the value of the ReturnDateType flag
  198. */
  199. public static function DATENOW()
  200. {
  201. $saveTimeZone = date_default_timezone_get();
  202. date_default_timezone_set('UTC');
  203. $retValue = false;
  204. $excelDateTime = floor(PHPExcel_Shared_Date::PHPToExcel(time()));
  205. switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
  206. case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
  207. $retValue = (float) $excelDateTime;
  208. break;
  209. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
  210. $retValue = (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateTime);
  211. break;
  212. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
  213. $retValue = PHPExcel_Shared_Date::ExcelToPHPObject($excelDateTime);
  214. break;
  215. }
  216. date_default_timezone_set($saveTimeZone);
  217. return $retValue;
  218. }
  219. /**
  220. * DATE
  221. *
  222. * The DATE function returns a value that represents a particular date.
  223. *
  224. * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
  225. * format of your regional settings. PHPExcel does not change cell formatting in this way.
  226. *
  227. * Excel Function:
  228. * DATE(year,month,day)
  229. *
  230. * PHPExcel is a lot more forgiving than MS Excel when passing non numeric values to this function.
  231. * A Month name or abbreviation (English only at this point) such as 'January' or 'Jan' will still be accepted,
  232. * as will a day value with a suffix (e.g. '21st' rather than simply 21); again only English language.
  233. *
  234. * @access public
  235. * @category Date/Time Functions
  236. * @param integer $year The value of the year argument can include one to four digits.
  237. * Excel interprets the year argument according to the configured
  238. * date system: 1900 or 1904.
  239. * If year is between 0 (zero) and 1899 (inclusive), Excel adds that
  240. * value to 1900 to calculate the year. For example, DATE(108,1,2)
  241. * returns January 2, 2008 (1900+108).
  242. * If year is between 1900 and 9999 (inclusive), Excel uses that
  243. * value as the year. For example, DATE(2008,1,2) returns January 2,
  244. * 2008.
  245. * If year is less than 0 or is 10000 or greater, Excel returns the
  246. * #NUM! error value.
  247. * @param integer $month A positive or negative integer representing the month of the year
  248. * from 1 to 12 (January to December).
  249. * If month is greater than 12, month adds that number of months to
  250. * the first month in the year specified. For example, DATE(2008,14,2)
  251. * returns the serial number representing February 2, 2009.
  252. * If month is less than 1, month subtracts the magnitude of that
  253. * number of months, plus 1, from the first month in the year
  254. * specified. For example, DATE(2008,-3,2) returns the serial number
  255. * representing September 2, 2007.
  256. * @param integer $day A positive or negative integer representing the day of the month
  257. * from 1 to 31.
  258. * If day is greater than the number of days in the month specified,
  259. * day adds that number of days to the first day in the month. For
  260. * example, DATE(2008,1,35) returns the serial number representing
  261. * February 4, 2008.
  262. * If day is less than 1, day subtracts the magnitude that number of
  263. * days, plus one, from the first day of the month specified. For
  264. * example, DATE(2008,1,-15) returns the serial number representing
  265. * December 16, 2007.
  266. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  267. * depending on the value of the ReturnDateType flag
  268. */
  269. public static function DATE($year = 0, $month = 1, $day = 1)
  270. {
  271. $year = PHPExcel_Calculation_Functions::flattenSingleValue($year);
  272. $month = PHPExcel_Calculation_Functions::flattenSingleValue($month);
  273. $day = PHPExcel_Calculation_Functions::flattenSingleValue($day);
  274. if (($month !== null) && (!is_numeric($month))) {
  275. $month = PHPExcel_Shared_Date::monthStringToNumber($month);
  276. }
  277. if (($day !== null) && (!is_numeric($day))) {
  278. $day = PHPExcel_Shared_Date::dayStringToNumber($day);
  279. }
  280. $year = ($year !== null) ? PHPExcel_Shared_String::testStringAsNumeric($year) : 0;
  281. $month = ($month !== null) ? PHPExcel_Shared_String::testStringAsNumeric($month) : 0;
  282. $day = ($day !== null) ? PHPExcel_Shared_String::testStringAsNumeric($day) : 0;
  283. if ((!is_numeric($year)) ||
  284. (!is_numeric($month)) ||
  285. (!is_numeric($day))) {
  286. return PHPExcel_Calculation_Functions::VALUE();
  287. }
  288. $year = (integer) $year;
  289. $month = (integer) $month;
  290. $day = (integer) $day;
  291. $baseYear = PHPExcel_Shared_Date::getExcelCalendar();
  292. // Validate parameters
  293. if ($year < ($baseYear-1900)) {
  294. return PHPExcel_Calculation_Functions::NaN();
  295. }
  296. if ((($baseYear-1900) != 0) && ($year < $baseYear) && ($year >= 1900)) {
  297. return PHPExcel_Calculation_Functions::NaN();
  298. }
  299. if (($year < $baseYear) && ($year >= ($baseYear-1900))) {
  300. $year += 1900;
  301. }
  302. if ($month < 1) {
  303. // Handle year/month adjustment if month < 1
  304. --$month;
  305. $year += ceil($month / 12) - 1;
  306. $month = 13 - abs($month % 12);
  307. } elseif ($month > 12) {
  308. // Handle year/month adjustment if month > 12
  309. $year += floor($month / 12);
  310. $month = ($month % 12);
  311. }
  312. // Re-validate the year parameter after adjustments
  313. if (($year < $baseYear) || ($year >= 10000)) {
  314. return PHPExcel_Calculation_Functions::NaN();
  315. }
  316. // Execute function
  317. $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel($year, $month, $day);
  318. switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
  319. case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
  320. return (float) $excelDateValue;
  321. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
  322. return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
  323. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
  324. return PHPExcel_Shared_Date::ExcelToPHPObject($excelDateValue);
  325. }
  326. }
  327. /**
  328. * TIME
  329. *
  330. * The TIME function returns a value that represents a particular time.
  331. *
  332. * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
  333. * format of your regional settings. PHPExcel does not change cell formatting in this way.
  334. *
  335. * Excel Function:
  336. * TIME(hour,minute,second)
  337. *
  338. * @access public
  339. * @category Date/Time Functions
  340. * @param integer $hour A number from 0 (zero) to 32767 representing the hour.
  341. * Any value greater than 23 will be divided by 24 and the remainder
  342. * will be treated as the hour value. For example, TIME(27,0,0) =
  343. * TIME(3,0,0) = .125 or 3:00 AM.
  344. * @param integer $minute A number from 0 to 32767 representing the minute.
  345. * Any value greater than 59 will be converted to hours and minutes.
  346. * For example, TIME(0,750,0) = TIME(12,30,0) = .520833 or 12:30 PM.
  347. * @param integer $second A number from 0 to 32767 representing the second.
  348. * Any value greater than 59 will be converted to hours, minutes,
  349. * and seconds. For example, TIME(0,0,2000) = TIME(0,33,22) = .023148
  350. * or 12:33:20 AM
  351. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  352. * depending on the value of the ReturnDateType flag
  353. */
  354. public static function TIME($hour = 0, $minute = 0, $second = 0)
  355. {
  356. $hour = PHPExcel_Calculation_Functions::flattenSingleValue($hour);
  357. $minute = PHPExcel_Calculation_Functions::flattenSingleValue($minute);
  358. $second = PHPExcel_Calculation_Functions::flattenSingleValue($second);
  359. if ($hour == '') {
  360. $hour = 0;
  361. }
  362. if ($minute == '') {
  363. $minute = 0;
  364. }
  365. if ($second == '') {
  366. $second = 0;
  367. }
  368. if ((!is_numeric($hour)) || (!is_numeric($minute)) || (!is_numeric($second))) {
  369. return PHPExcel_Calculation_Functions::VALUE();
  370. }
  371. $hour = (integer) $hour;
  372. $minute = (integer) $minute;
  373. $second = (integer) $second;
  374. if ($second < 0) {
  375. $minute += floor($second / 60);
  376. $second = 60 - abs($second % 60);
  377. if ($second == 60) {
  378. $second = 0;
  379. }
  380. } elseif ($second >= 60) {
  381. $minute += floor($second / 60);
  382. $second = $second % 60;
  383. }
  384. if ($minute < 0) {
  385. $hour += floor($minute / 60);
  386. $minute = 60 - abs($minute % 60);
  387. if ($minute == 60) {
  388. $minute = 0;
  389. }
  390. } elseif ($minute >= 60) {
  391. $hour += floor($minute / 60);
  392. $minute = $minute % 60;
  393. }
  394. if ($hour > 23) {
  395. $hour = $hour % 24;
  396. } elseif ($hour < 0) {
  397. return PHPExcel_Calculation_Functions::NaN();
  398. }
  399. // Execute function
  400. switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
  401. case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
  402. $date = 0;
  403. $calendar = PHPExcel_Shared_Date::getExcelCalendar();
  404. if ($calendar != PHPExcel_Shared_Date::CALENDAR_WINDOWS_1900) {
  405. $date = 1;
  406. }
  407. return (float) PHPExcel_Shared_Date::FormattedPHPToExcel($calendar, 1, $date, $hour, $minute, $second);
  408. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
  409. return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::FormattedPHPToExcel(1970, 1, 1, $hour, $minute, $second)); // -2147468400; // -2147472000 + 3600
  410. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
  411. $dayAdjust = 0;
  412. if ($hour < 0) {
  413. $dayAdjust = floor($hour / 24);
  414. $hour = 24 - abs($hour % 24);
  415. if ($hour == 24) {
  416. $hour = 0;
  417. }
  418. } elseif ($hour >= 24) {
  419. $dayAdjust = floor($hour / 24);
  420. $hour = $hour % 24;
  421. }
  422. $phpDateObject = new DateTime('1900-01-01 '.$hour.':'.$minute.':'.$second);
  423. if ($dayAdjust != 0) {
  424. $phpDateObject->modify($dayAdjust.' days');
  425. }
  426. return $phpDateObject;
  427. }
  428. }
  429. /**
  430. * DATEVALUE
  431. *
  432. * Returns a value that represents a particular date.
  433. * Use DATEVALUE to convert a date represented by a text string to an Excel or PHP date/time stamp
  434. * value.
  435. *
  436. * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the date
  437. * format of your regional settings. PHPExcel does not change cell formatting in this way.
  438. *
  439. * Excel Function:
  440. * DATEVALUE(dateValue)
  441. *
  442. * @access public
  443. * @category Date/Time Functions
  444. * @param string $dateValue Text that represents a date in a Microsoft Excel date format.
  445. * For example, "1/30/2008" or "30-Jan-2008" are text strings within
  446. * quotation marks that represent dates. Using the default date
  447. * system in Excel for Windows, date_text must represent a date from
  448. * January 1, 1900, to December 31, 9999. Using the default date
  449. * system in Excel for the Macintosh, date_text must represent a date
  450. * from January 1, 1904, to December 31, 9999. DATEVALUE returns the
  451. * #VALUE! error value if date_text is out of this range.
  452. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  453. * depending on the value of the ReturnDateType flag
  454. */
  455. public static function DATEVALUE($dateValue = 1)
  456. {
  457. $dateValue = trim(PHPExcel_Calculation_Functions::flattenSingleValue($dateValue), '"');
  458. // Strip any ordinals because they're allowed in Excel (English only)
  459. $dateValue = preg_replace('/(\d)(st|nd|rd|th)([ -\/])/Ui', '$1$3', $dateValue);
  460. // Convert separators (/ . or space) to hyphens (should also handle dot used for ordinals in some countries, e.g. Denmark, Germany)
  461. $dateValue = str_replace(array('/', '.', '-', ' '), array(' ', ' ', ' ', ' '), $dateValue);
  462. $yearFound = false;
  463. $t1 = explode(' ', $dateValue);
  464. foreach ($t1 as &$t) {
  465. if ((is_numeric($t)) && ($t > 31)) {
  466. if ($yearFound) {
  467. return PHPExcel_Calculation_Functions::VALUE();
  468. } else {
  469. if ($t < 100) {
  470. $t += 1900;
  471. }
  472. $yearFound = true;
  473. }
  474. }
  475. }
  476. if ((count($t1) == 1) && (strpos($t, ':') != false)) {
  477. // We've been fed a time value without any date
  478. return 0.0;
  479. } elseif (count($t1) == 2) {
  480. // We only have two parts of the date: either day/month or month/year
  481. if ($yearFound) {
  482. array_unshift($t1, 1);
  483. } else {
  484. array_push($t1, date('Y'));
  485. }
  486. }
  487. unset($t);
  488. $dateValue = implode(' ', $t1);
  489. $PHPDateArray = date_parse($dateValue);
  490. if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
  491. $testVal1 = strtok($dateValue, '- ');
  492. if ($testVal1 !== false) {
  493. $testVal2 = strtok('- ');
  494. if ($testVal2 !== false) {
  495. $testVal3 = strtok('- ');
  496. if ($testVal3 === false) {
  497. $testVal3 = strftime('%Y');
  498. }
  499. } else {
  500. return PHPExcel_Calculation_Functions::VALUE();
  501. }
  502. } else {
  503. return PHPExcel_Calculation_Functions::VALUE();
  504. }
  505. $PHPDateArray = date_parse($testVal1.'-'.$testVal2.'-'.$testVal3);
  506. if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
  507. $PHPDateArray = date_parse($testVal2.'-'.$testVal1.'-'.$testVal3);
  508. if (($PHPDateArray === false) || ($PHPDateArray['error_count'] > 0)) {
  509. return PHPExcel_Calculation_Functions::VALUE();
  510. }
  511. }
  512. }
  513. if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
  514. // Execute function
  515. if ($PHPDateArray['year'] == '') {
  516. $PHPDateArray['year'] = strftime('%Y');
  517. }
  518. if ($PHPDateArray['year'] < 1900) {
  519. return PHPExcel_Calculation_Functions::VALUE();
  520. }
  521. if ($PHPDateArray['month'] == '') {
  522. $PHPDateArray['month'] = strftime('%m');
  523. }
  524. if ($PHPDateArray['day'] == '') {
  525. $PHPDateArray['day'] = strftime('%d');
  526. }
  527. $excelDateValue = floor(
  528. PHPExcel_Shared_Date::FormattedPHPToExcel(
  529. $PHPDateArray['year'],
  530. $PHPDateArray['month'],
  531. $PHPDateArray['day'],
  532. $PHPDateArray['hour'],
  533. $PHPDateArray['minute'],
  534. $PHPDateArray['second']
  535. )
  536. );
  537. switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
  538. case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
  539. return (float) $excelDateValue;
  540. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
  541. return (integer) PHPExcel_Shared_Date::ExcelToPHP($excelDateValue);
  542. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
  543. return new DateTime($PHPDateArray['year'].'-'.$PHPDateArray['month'].'-'.$PHPDateArray['day'].' 00:00:00');
  544. }
  545. }
  546. return PHPExcel_Calculation_Functions::VALUE();
  547. }
  548. /**
  549. * TIMEVALUE
  550. *
  551. * Returns a value that represents a particular time.
  552. * Use TIMEVALUE to convert a time represented by a text string to an Excel or PHP date/time stamp
  553. * value.
  554. *
  555. * NOTE: When used in a Cell Formula, MS Excel changes the cell format so that it matches the time
  556. * format of your regional settings. PHPExcel does not change cell formatting in this way.
  557. *
  558. * Excel Function:
  559. * TIMEVALUE(timeValue)
  560. *
  561. * @access public
  562. * @category Date/Time Functions
  563. * @param string $timeValue A text string that represents a time in any one of the Microsoft
  564. * Excel time formats; for example, "6:45 PM" and "18:45" text strings
  565. * within quotation marks that represent time.
  566. * Date information in time_text is ignored.
  567. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  568. * depending on the value of the ReturnDateType flag
  569. */
  570. public static function TIMEVALUE($timeValue)
  571. {
  572. $timeValue = trim(PHPExcel_Calculation_Functions::flattenSingleValue($timeValue), '"');
  573. $timeValue = str_replace(array('/', '.'), array('-', '-'), $timeValue);
  574. $PHPDateArray = date_parse($timeValue);
  575. if (($PHPDateArray !== false) && ($PHPDateArray['error_count'] == 0)) {
  576. if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_OPENOFFICE) {
  577. $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel(
  578. $PHPDateArray['year'],
  579. $PHPDateArray['month'],
  580. $PHPDateArray['day'],
  581. $PHPDateArray['hour'],
  582. $PHPDateArray['minute'],
  583. $PHPDateArray['second']
  584. );
  585. } else {
  586. $excelDateValue = PHPExcel_Shared_Date::FormattedPHPToExcel(1900, 1, 1, $PHPDateArray['hour'], $PHPDateArray['minute'], $PHPDateArray['second']) - 1;
  587. }
  588. switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
  589. case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
  590. return (float) $excelDateValue;
  591. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
  592. return (integer) $phpDateValue = PHPExcel_Shared_Date::ExcelToPHP($excelDateValue+25569) - 3600;
  593. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
  594. return new DateTime('1900-01-01 '.$PHPDateArray['hour'].':'.$PHPDateArray['minute'].':'.$PHPDateArray['second']);
  595. }
  596. }
  597. return PHPExcel_Calculation_Functions::VALUE();
  598. }
  599. /**
  600. * DATEDIF
  601. *
  602. * @param mixed $startDate Excel date serial value, PHP date/time stamp, PHP DateTime object
  603. * or a standard date string
  604. * @param mixed $endDate Excel date serial value, PHP date/time stamp, PHP DateTime object
  605. * or a standard date string
  606. * @param string $unit
  607. * @return integer Interval between the dates
  608. */
  609. public static function DATEDIF($startDate = 0, $endDate = 0, $unit = 'D')
  610. {
  611. $startDate = PHPExcel_Calculation_Functions::flattenSingleValue($startDate);
  612. $endDate = PHPExcel_Calculation_Functions::flattenSingleValue($endDate);
  613. $unit = strtoupper(PHPExcel_Calculation_Functions::flattenSingleValue($unit));
  614. if (is_string($startDate = self::getDateValue($startDate))) {
  615. return PHPExcel_Calculation_Functions::VALUE();
  616. }
  617. if (is_string($endDate = self::getDateValue($endDate))) {
  618. return PHPExcel_Calculation_Functions::VALUE();
  619. }
  620. // Validate parameters
  621. if ($startDate >= $endDate) {
  622. return PHPExcel_Calculation_Functions::NaN();
  623. }
  624. // Execute function
  625. $difference = $endDate - $startDate;
  626. $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
  627. $startDays = $PHPStartDateObject->format('j');
  628. $startMonths = $PHPStartDateObject->format('n');
  629. $startYears = $PHPStartDateObject->format('Y');
  630. $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  631. $endDays = $PHPEndDateObject->format('j');
  632. $endMonths = $PHPEndDateObject->format('n');
  633. $endYears = $PHPEndDateObject->format('Y');
  634. $retVal = PHPExcel_Calculation_Functions::NaN();
  635. switch ($unit) {
  636. case 'D':
  637. $retVal = intval($difference);
  638. break;
  639. case 'M':
  640. $retVal = intval($endMonths - $startMonths) + (intval($endYears - $startYears) * 12);
  641. // We're only interested in full months
  642. if ($endDays < $startDays) {
  643. --$retVal;
  644. }
  645. break;
  646. case 'Y':
  647. $retVal = intval($endYears - $startYears);
  648. // We're only interested in full months
  649. if ($endMonths < $startMonths) {
  650. --$retVal;
  651. } elseif (($endMonths == $startMonths) && ($endDays < $startDays)) {
  652. --$retVal;
  653. }
  654. break;
  655. case 'MD':
  656. if ($endDays < $startDays) {
  657. $retVal = $endDays;
  658. $PHPEndDateObject->modify('-'.$endDays.' days');
  659. $adjustDays = $PHPEndDateObject->format('j');
  660. if ($adjustDays > $startDays) {
  661. $retVal += ($adjustDays - $startDays);
  662. }
  663. } else {
  664. $retVal = $endDays - $startDays;
  665. }
  666. break;
  667. case 'YM':
  668. $retVal = intval($endMonths - $startMonths);
  669. if ($retVal < 0) {
  670. $retVal += 12;
  671. }
  672. // We're only interested in full months
  673. if ($endDays < $startDays) {
  674. --$retVal;
  675. }
  676. break;
  677. case 'YD':
  678. $retVal = intval($difference);
  679. if ($endYears > $startYears) {
  680. while ($endYears > $startYears) {
  681. $PHPEndDateObject->modify('-1 year');
  682. $endYears = $PHPEndDateObject->format('Y');
  683. }
  684. $retVal = $PHPEndDateObject->format('z') - $PHPStartDateObject->format('z');
  685. if ($retVal < 0) {
  686. $retVal += 365;
  687. }
  688. }
  689. break;
  690. default:
  691. $retVal = PHPExcel_Calculation_Functions::NaN();
  692. }
  693. return $retVal;
  694. }
  695. /**
  696. * DAYS360
  697. *
  698. * Returns the number of days between two dates based on a 360-day year (twelve 30-day months),
  699. * which is used in some accounting calculations. Use this function to help compute payments if
  700. * your accounting system is based on twelve 30-day months.
  701. *
  702. * Excel Function:
  703. * DAYS360(startDate,endDate[,method])
  704. *
  705. * @access public
  706. * @category Date/Time Functions
  707. * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
  708. * PHP DateTime object, or a standard date string
  709. * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
  710. * PHP DateTime object, or a standard date string
  711. * @param boolean $method US or European Method
  712. * FALSE or omitted: U.S. (NASD) method. If the starting date is
  713. * the last day of a month, it becomes equal to the 30th of the
  714. * same month. If the ending date is the last day of a month and
  715. * the starting date is earlier than the 30th of a month, the
  716. * ending date becomes equal to the 1st of the next month;
  717. * otherwise the ending date becomes equal to the 30th of the
  718. * same month.
  719. * TRUE: European method. Starting dates and ending dates that
  720. * occur on the 31st of a month become equal to the 30th of the
  721. * same month.
  722. * @return integer Number of days between start date and end date
  723. */
  724. public static function DAYS360($startDate = 0, $endDate = 0, $method = false)
  725. {
  726. $startDate = PHPExcel_Calculation_Functions::flattenSingleValue($startDate);
  727. $endDate = PHPExcel_Calculation_Functions::flattenSingleValue($endDate);
  728. if (is_string($startDate = self::getDateValue($startDate))) {
  729. return PHPExcel_Calculation_Functions::VALUE();
  730. }
  731. if (is_string($endDate = self::getDateValue($endDate))) {
  732. return PHPExcel_Calculation_Functions::VALUE();
  733. }
  734. if (!is_bool($method)) {
  735. return PHPExcel_Calculation_Functions::VALUE();
  736. }
  737. // Execute function
  738. $PHPStartDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($startDate);
  739. $startDay = $PHPStartDateObject->format('j');
  740. $startMonth = $PHPStartDateObject->format('n');
  741. $startYear = $PHPStartDateObject->format('Y');
  742. $PHPEndDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  743. $endDay = $PHPEndDateObject->format('j');
  744. $endMonth = $PHPEndDateObject->format('n');
  745. $endYear = $PHPEndDateObject->format('Y');
  746. return self::dateDiff360($startDay, $startMonth, $startYear, $endDay, $endMonth, $endYear, !$method);
  747. }
  748. /**
  749. * YEARFRAC
  750. *
  751. * Calculates the fraction of the year represented by the number of whole days between two dates
  752. * (the start_date and the end_date).
  753. * Use the YEARFRAC worksheet function to identify the proportion of a whole year's benefits or
  754. * obligations to assign to a specific term.
  755. *
  756. * Excel Function:
  757. * YEARFRAC(startDate,endDate[,method])
  758. *
  759. * @access public
  760. * @category Date/Time Functions
  761. * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
  762. * PHP DateTime object, or a standard date string
  763. * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
  764. * PHP DateTime object, or a standard date string
  765. * @param integer $method Method used for the calculation
  766. * 0 or omitted US (NASD) 30/360
  767. * 1 Actual/actual
  768. * 2 Actual/360
  769. * 3 Actual/365
  770. * 4 European 30/360
  771. * @return float fraction of the year
  772. */
  773. public static function YEARFRAC($startDate = 0, $endDate = 0, $method = 0)
  774. {
  775. $startDate = PHPExcel_Calculation_Functions::flattenSingleValue($startDate);
  776. $endDate = PHPExcel_Calculation_Functions::flattenSingleValue($endDate);
  777. $method = PHPExcel_Calculation_Functions::flattenSingleValue($method);
  778. if (is_string($startDate = self::getDateValue($startDate))) {
  779. return PHPExcel_Calculation_Functions::VALUE();
  780. }
  781. if (is_string($endDate = self::getDateValue($endDate))) {
  782. return PHPExcel_Calculation_Functions::VALUE();
  783. }
  784. if (((is_numeric($method)) && (!is_string($method))) || ($method == '')) {
  785. switch ($method) {
  786. case 0:
  787. return self::DAYS360($startDate, $endDate) / 360;
  788. case 1:
  789. $days = self::DATEDIF($startDate, $endDate);
  790. $startYear = self::YEAR($startDate);
  791. $endYear = self::YEAR($endDate);
  792. $years = $endYear - $startYear + 1;
  793. $leapDays = 0;
  794. if ($years == 1) {
  795. if (self::isLeapYear($endYear)) {
  796. $startMonth = self::MONTHOFYEAR($startDate);
  797. $endMonth = self::MONTHOFYEAR($endDate);
  798. $endDay = self::DAYOFMONTH($endDate);
  799. if (($startMonth < 3) ||
  800. (($endMonth * 100 + $endDay) >= (2 * 100 + 29))) {
  801. $leapDays += 1;
  802. }
  803. }
  804. } else {
  805. for ($year = $startYear; $year <= $endYear; ++$year) {
  806. if ($year == $startYear) {
  807. $startMonth = self::MONTHOFYEAR($startDate);
  808. $startDay = self::DAYOFMONTH($startDate);
  809. if ($startMonth < 3) {
  810. $leapDays += (self::isLeapYear($year)) ? 1 : 0;
  811. }
  812. } elseif ($year == $endYear) {
  813. $endMonth = self::MONTHOFYEAR($endDate);
  814. $endDay = self::DAYOFMONTH($endDate);
  815. if (($endMonth * 100 + $endDay) >= (2 * 100 + 29)) {
  816. $leapDays += (self::isLeapYear($year)) ? 1 : 0;
  817. }
  818. } else {
  819. $leapDays += (self::isLeapYear($year)) ? 1 : 0;
  820. }
  821. }
  822. if ($years == 2) {
  823. if (($leapDays == 0) && (self::isLeapYear($startYear)) && ($days > 365)) {
  824. $leapDays = 1;
  825. } elseif ($days < 366) {
  826. $years = 1;
  827. }
  828. }
  829. $leapDays /= $years;
  830. }
  831. return $days / (365 + $leapDays);
  832. case 2:
  833. return self::DATEDIF($startDate, $endDate) / 360;
  834. case 3:
  835. return self::DATEDIF($startDate, $endDate) / 365;
  836. case 4:
  837. return self::DAYS360($startDate, $endDate, true) / 360;
  838. }
  839. }
  840. return PHPExcel_Calculation_Functions::VALUE();
  841. }
  842. /**
  843. * NETWORKDAYS
  844. *
  845. * Returns the number of whole working days between start_date and end_date. Working days
  846. * exclude weekends and any dates identified in holidays.
  847. * Use NETWORKDAYS to calculate employee benefits that accrue based on the number of days
  848. * worked during a specific term.
  849. *
  850. * Excel Function:
  851. * NETWORKDAYS(startDate,endDate[,holidays[,holiday[,...]]])
  852. *
  853. * @access public
  854. * @category Date/Time Functions
  855. * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
  856. * PHP DateTime object, or a standard date string
  857. * @param mixed $endDate Excel date serial value (float), PHP date timestamp (integer),
  858. * PHP DateTime object, or a standard date string
  859. * @param mixed $holidays,... Optional series of Excel date serial value (float), PHP date
  860. * timestamp (integer), PHP DateTime object, or a standard date
  861. * strings that will be excluded from the working calendar, such
  862. * as state and federal holidays and floating holidays.
  863. * @return integer Interval between the dates
  864. */
  865. public static function NETWORKDAYS($startDate, $endDate)
  866. {
  867. // Retrieve the mandatory start and end date that are referenced in the function definition
  868. $startDate = PHPExcel_Calculation_Functions::flattenSingleValue($startDate);
  869. $endDate = PHPExcel_Calculation_Functions::flattenSingleValue($endDate);
  870. // Flush the mandatory start and end date that are referenced in the function definition, and get the optional days
  871. $dateArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  872. array_shift($dateArgs);
  873. array_shift($dateArgs);
  874. // Validate the start and end dates
  875. if (is_string($startDate = $sDate = self::getDateValue($startDate))) {
  876. return PHPExcel_Calculation_Functions::VALUE();
  877. }
  878. $startDate = (float) floor($startDate);
  879. if (is_string($endDate = $eDate = self::getDateValue($endDate))) {
  880. return PHPExcel_Calculation_Functions::VALUE();
  881. }
  882. $endDate = (float) floor($endDate);
  883. if ($sDate > $eDate) {
  884. $startDate = $eDate;
  885. $endDate = $sDate;
  886. }
  887. // Execute function
  888. $startDoW = 6 - self::DAYOFWEEK($startDate, 2);
  889. if ($startDoW < 0) {
  890. $startDoW = 0;
  891. }
  892. $endDoW = self::DAYOFWEEK($endDate, 2);
  893. if ($endDoW >= 6) {
  894. $endDoW = 0;
  895. }
  896. $wholeWeekDays = floor(($endDate - $startDate) / 7) * 5;
  897. $partWeekDays = $endDoW + $startDoW;
  898. if ($partWeekDays > 5) {
  899. $partWeekDays -= 5;
  900. }
  901. // Test any extra holiday parameters
  902. $holidayCountedArray = array();
  903. foreach ($dateArgs as $holidayDate) {
  904. if (is_string($holidayDate = self::getDateValue($holidayDate))) {
  905. return PHPExcel_Calculation_Functions::VALUE();
  906. }
  907. if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
  908. if ((self::DAYOFWEEK($holidayDate, 2) < 6) && (!in_array($holidayDate, $holidayCountedArray))) {
  909. --$partWeekDays;
  910. $holidayCountedArray[] = $holidayDate;
  911. }
  912. }
  913. }
  914. if ($sDate > $eDate) {
  915. return 0 - ($wholeWeekDays + $partWeekDays);
  916. }
  917. return $wholeWeekDays + $partWeekDays;
  918. }
  919. /**
  920. * WORKDAY
  921. *
  922. * Returns the date that is the indicated number of working days before or after a date (the
  923. * starting date). Working days exclude weekends and any dates identified as holidays.
  924. * Use WORKDAY to exclude weekends or holidays when you calculate invoice due dates, expected
  925. * delivery times, or the number of days of work performed.
  926. *
  927. * Excel Function:
  928. * WORKDAY(startDate,endDays[,holidays[,holiday[,...]]])
  929. *
  930. * @access public
  931. * @category Date/Time Functions
  932. * @param mixed $startDate Excel date serial value (float), PHP date timestamp (integer),
  933. * PHP DateTime object, or a standard date string
  934. * @param integer $endDays The number of nonweekend and nonholiday days before or after
  935. * startDate. A positive value for days yields a future date; a
  936. * negative value yields a past date.
  937. * @param mixed $holidays,... Optional series of Excel date serial value (float), PHP date
  938. * timestamp (integer), PHP DateTime object, or a standard date
  939. * strings that will be excluded from the working calendar, such
  940. * as state and federal holidays and floating holidays.
  941. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  942. * depending on the value of the ReturnDateType flag
  943. */
  944. public static function WORKDAY($startDate, $endDays)
  945. {
  946. // Retrieve the mandatory start date and days that are referenced in the function definition
  947. $startDate = PHPExcel_Calculation_Functions::flattenSingleValue($startDate);
  948. $endDays = PHPExcel_Calculation_Functions::flattenSingleValue($endDays);
  949. // Flush the mandatory start date and days that are referenced in the function definition, and get the optional days
  950. $dateArgs = PHPExcel_Calculation_Functions::flattenArray(func_get_args());
  951. array_shift($dateArgs);
  952. array_shift($dateArgs);
  953. if ((is_string($startDate = self::getDateValue($startDate))) || (!is_numeric($endDays))) {
  954. return PHPExcel_Calculation_Functions::VALUE();
  955. }
  956. $startDate = (float) floor($startDate);
  957. $endDays = (int) floor($endDays);
  958. // If endDays is 0, we always return startDate
  959. if ($endDays == 0) {
  960. return $startDate;
  961. }
  962. $decrementing = ($endDays < 0) ? true : false;
  963. // Adjust the start date if it falls over a weekend
  964. $startDoW = self::DAYOFWEEK($startDate, 3);
  965. if (self::DAYOFWEEK($startDate, 3) >= 5) {
  966. $startDate += ($decrementing) ? -$startDoW + 4: 7 - $startDoW;
  967. ($decrementing) ? $endDays++ : $endDays--;
  968. }
  969. // Add endDays
  970. $endDate = (float) $startDate + (intval($endDays / 5) * 7) + ($endDays % 5);
  971. // Adjust the calculated end date if it falls over a weekend
  972. $endDoW = self::DAYOFWEEK($endDate, 3);
  973. if ($endDoW >= 5) {
  974. $endDate += ($decrementing) ? -$endDoW + 4: 7 - $endDoW;
  975. }
  976. // Test any extra holiday parameters
  977. if (!empty($dateArgs)) {
  978. $holidayCountedArray = $holidayDates = array();
  979. foreach ($dateArgs as $holidayDate) {
  980. if (($holidayDate !== null) && (trim($holidayDate) > '')) {
  981. if (is_string($holidayDate = self::getDateValue($holidayDate))) {
  982. return PHPExcel_Calculation_Functions::VALUE();
  983. }
  984. if (self::DAYOFWEEK($holidayDate, 3) < 5) {
  985. $holidayDates[] = $holidayDate;
  986. }
  987. }
  988. }
  989. if ($decrementing) {
  990. rsort($holidayDates, SORT_NUMERIC);
  991. } else {
  992. sort($holidayDates, SORT_NUMERIC);
  993. }
  994. foreach ($holidayDates as $holidayDate) {
  995. if ($decrementing) {
  996. if (($holidayDate <= $startDate) && ($holidayDate >= $endDate)) {
  997. if (!in_array($holidayDate, $holidayCountedArray)) {
  998. --$endDate;
  999. $holidayCountedArray[] = $holidayDate;
  1000. }
  1001. }
  1002. } else {
  1003. if (($holidayDate >= $startDate) && ($holidayDate <= $endDate)) {
  1004. if (!in_array($holidayDate, $holidayCountedArray)) {
  1005. ++$endDate;
  1006. $holidayCountedArray[] = $holidayDate;
  1007. }
  1008. }
  1009. }
  1010. // Adjust the calculated end date if it falls over a weekend
  1011. $endDoW = self::DAYOFWEEK($endDate, 3);
  1012. if ($endDoW >= 5) {
  1013. $endDate += ($decrementing) ? -$endDoW + 4 : 7 - $endDoW;
  1014. }
  1015. }
  1016. }
  1017. switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
  1018. case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
  1019. return (float) $endDate;
  1020. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
  1021. return (integer) PHPExcel_Shared_Date::ExcelToPHP($endDate);
  1022. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
  1023. return PHPExcel_Shared_Date::ExcelToPHPObject($endDate);
  1024. }
  1025. }
  1026. /**
  1027. * DAYOFMONTH
  1028. *
  1029. * Returns the day of the month, for a specified date. The day is given as an integer
  1030. * ranging from 1 to 31.
  1031. *
  1032. * Excel Function:
  1033. * DAY(dateValue)
  1034. *
  1035. * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
  1036. * PHP DateTime object, or a standard date string
  1037. * @return int Day of the month
  1038. */
  1039. public static function DAYOFMONTH($dateValue = 1)
  1040. {
  1041. $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
  1042. if ($dateValue === null) {
  1043. $dateValue = 1;
  1044. } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
  1045. return PHPExcel_Calculation_Functions::VALUE();
  1046. } elseif ($dateValue == 0.0) {
  1047. return 0;
  1048. } elseif ($dateValue < 0.0) {
  1049. return PHPExcel_Calculation_Functions::NaN();
  1050. }
  1051. // Execute function
  1052. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  1053. return (int) $PHPDateObject->format('j');
  1054. }
  1055. /**
  1056. * DAYOFWEEK
  1057. *
  1058. * Returns the day of the week for a specified date. The day is given as an integer
  1059. * ranging from 0 to 7 (dependent on the requested style).
  1060. *
  1061. * Excel Function:
  1062. * WEEKDAY(dateValue[,style])
  1063. *
  1064. * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
  1065. * PHP DateTime object, or a standard date string
  1066. * @param int $style A number that determines the type of return value
  1067. * 1 or omitted Numbers 1 (Sunday) through 7 (Saturday).
  1068. * 2 Numbers 1 (Monday) through 7 (Sunday).
  1069. * 3 Numbers 0 (Monday) through 6 (Sunday).
  1070. * @return int Day of the week value
  1071. */
  1072. public static function DAYOFWEEK($dateValue = 1, $style = 1)
  1073. {
  1074. $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
  1075. $style = PHPExcel_Calculation_Functions::flattenSingleValue($style);
  1076. if (!is_numeric($style)) {
  1077. return PHPExcel_Calculation_Functions::VALUE();
  1078. } elseif (($style < 1) || ($style > 3)) {
  1079. return PHPExcel_Calculation_Functions::NaN();
  1080. }
  1081. $style = floor($style);
  1082. if ($dateValue === null) {
  1083. $dateValue = 1;
  1084. } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
  1085. return PHPExcel_Calculation_Functions::VALUE();
  1086. } elseif ($dateValue < 0.0) {
  1087. return PHPExcel_Calculation_Functions::NaN();
  1088. }
  1089. // Execute function
  1090. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  1091. $DoW = $PHPDateObject->format('w');
  1092. $firstDay = 1;
  1093. switch ($style) {
  1094. case 1:
  1095. ++$DoW;
  1096. break;
  1097. case 2:
  1098. if ($DoW == 0) {
  1099. $DoW = 7;
  1100. }
  1101. break;
  1102. case 3:
  1103. if ($DoW == 0) {
  1104. $DoW = 7;
  1105. }
  1106. $firstDay = 0;
  1107. --$DoW;
  1108. break;
  1109. }
  1110. if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_EXCEL) {
  1111. // Test for Excel's 1900 leap year, and introduce the error as required
  1112. if (($PHPDateObject->format('Y') == 1900) && ($PHPDateObject->format('n') <= 2)) {
  1113. --$DoW;
  1114. if ($DoW < $firstDay) {
  1115. $DoW += 7;
  1116. }
  1117. }
  1118. }
  1119. return (int) $DoW;
  1120. }
  1121. /**
  1122. * WEEKOFYEAR
  1123. *
  1124. * Returns the week of the year for a specified date.
  1125. * The WEEKNUM function considers the week containing January 1 to be the first week of the year.
  1126. * However, there is a European standard that defines the first week as the one with the majority
  1127. * of days (four or more) falling in the new year. This means that for years in which there are
  1128. * three days or less in the first week of January, the WEEKNUM function returns week numbers
  1129. * that are incorrect according to the European standard.
  1130. *
  1131. * Excel Function:
  1132. * WEEKNUM(dateValue[,style])
  1133. *
  1134. * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
  1135. * PHP DateTime object, or a standard date string
  1136. * @param boolean $method Week begins on Sunday or Monday
  1137. * 1 or omitted Week begins on Sunday.
  1138. * 2 Week begins on Monday.
  1139. * @return int Week Number
  1140. */
  1141. public static function WEEKOFYEAR($dateValue = 1, $method = 1)
  1142. {
  1143. $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
  1144. $method = PHPExcel_Calculation_Functions::flattenSingleValue($method);
  1145. if (!is_numeric($method)) {
  1146. return PHPExcel_Calculation_Functions::VALUE();
  1147. } elseif (($method < 1) || ($method > 2)) {
  1148. return PHPExcel_Calculation_Functions::NaN();
  1149. }
  1150. $method = floor($method);
  1151. if ($dateValue === null) {
  1152. $dateValue = 1;
  1153. } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
  1154. return PHPExcel_Calculation_Functions::VALUE();
  1155. } elseif ($dateValue < 0.0) {
  1156. return PHPExcel_Calculation_Functions::NaN();
  1157. }
  1158. // Execute function
  1159. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  1160. $dayOfYear = $PHPDateObject->format('z');
  1161. $dow = $PHPDateObject->format('w');
  1162. $PHPDateObject->modify('-' . $dayOfYear . ' days');
  1163. $dow = $PHPDateObject->format('w');
  1164. $daysInFirstWeek = 7 - (($dow + (2 - $method)) % 7);
  1165. $dayOfYear -= $daysInFirstWeek;
  1166. $weekOfYear = ceil($dayOfYear / 7) + 1;
  1167. return (int) $weekOfYear;
  1168. }
  1169. /**
  1170. * MONTHOFYEAR
  1171. *
  1172. * Returns the month of a date represented by a serial number.
  1173. * The month is given as an integer, ranging from 1 (January) to 12 (December).
  1174. *
  1175. * Excel Function:
  1176. * MONTH(dateValue)
  1177. *
  1178. * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
  1179. * PHP DateTime object, or a standard date string
  1180. * @return int Month of the year
  1181. */
  1182. public static function MONTHOFYEAR($dateValue = 1)
  1183. {
  1184. $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
  1185. if ($dateValue === null) {
  1186. $dateValue = 1;
  1187. } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
  1188. return PHPExcel_Calculation_Functions::VALUE();
  1189. } elseif ($dateValue < 0.0) {
  1190. return PHPExcel_Calculation_Functions::NaN();
  1191. }
  1192. // Execute function
  1193. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  1194. return (int) $PHPDateObject->format('n');
  1195. }
  1196. /**
  1197. * YEAR
  1198. *
  1199. * Returns the year corresponding to a date.
  1200. * The year is returned as an integer in the range 1900-9999.
  1201. *
  1202. * Excel Function:
  1203. * YEAR(dateValue)
  1204. *
  1205. * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
  1206. * PHP DateTime object, or a standard date string
  1207. * @return int Year
  1208. */
  1209. public static function YEAR($dateValue = 1)
  1210. {
  1211. $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
  1212. if ($dateValue === null) {
  1213. $dateValue = 1;
  1214. } elseif (is_string($dateValue = self::getDateValue($dateValue))) {
  1215. return PHPExcel_Calculation_Functions::VALUE();
  1216. } elseif ($dateValue < 0.0) {
  1217. return PHPExcel_Calculation_Functions::NaN();
  1218. }
  1219. // Execute function
  1220. $PHPDateObject = PHPExcel_Shared_Date::ExcelToPHPObject($dateValue);
  1221. return (int) $PHPDateObject->format('Y');
  1222. }
  1223. /**
  1224. * HOUROFDAY
  1225. *
  1226. * Returns the hour of a time value.
  1227. * The hour is given as an integer, ranging from 0 (12:00 A.M.) to 23 (11:00 P.M.).
  1228. *
  1229. * Excel Function:
  1230. * HOUR(timeValue)
  1231. *
  1232. * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
  1233. * PHP DateTime object, or a standard time string
  1234. * @return int Hour
  1235. */
  1236. public static function HOUROFDAY($timeValue = 0)
  1237. {
  1238. $timeValue = PHPExcel_Calculation_Functions::flattenSingleValue($timeValue);
  1239. if (!is_numeric($timeValue)) {
  1240. if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
  1241. $testVal = strtok($timeValue, '/-: ');
  1242. if (strlen($testVal) < strlen($timeValue)) {
  1243. return PHPExcel_Calculation_Functions::VALUE();
  1244. }
  1245. }
  1246. $timeValue = self::getTimeValue($timeValue);
  1247. if (is_string($timeValue)) {
  1248. return PHPExcel_Calculation_Functions::VALUE();
  1249. }
  1250. }
  1251. // Execute function
  1252. if ($timeValue >= 1) {
  1253. $timeValue = fmod($timeValue, 1);
  1254. } elseif ($timeValue < 0.0) {
  1255. return PHPExcel_Calculation_Functions::NaN();
  1256. }
  1257. $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  1258. return (int) gmdate('G', $timeValue);
  1259. }
  1260. /**
  1261. * MINUTEOFHOUR
  1262. *
  1263. * Returns the minutes of a time value.
  1264. * The minute is given as an integer, ranging from 0 to 59.
  1265. *
  1266. * Excel Function:
  1267. * MINUTE(timeValue)
  1268. *
  1269. * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
  1270. * PHP DateTime object, or a standard time string
  1271. * @return int Minute
  1272. */
  1273. public static function MINUTEOFHOUR($timeValue = 0)
  1274. {
  1275. $timeValue = $timeTester = PHPExcel_Calculation_Functions::flattenSingleValue($timeValue);
  1276. if (!is_numeric($timeValue)) {
  1277. if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
  1278. $testVal = strtok($timeValue, '/-: ');
  1279. if (strlen($testVal) < strlen($timeValue)) {
  1280. return PHPExcel_Calculation_Functions::VALUE();
  1281. }
  1282. }
  1283. $timeValue = self::getTimeValue($timeValue);
  1284. if (is_string($timeValue)) {
  1285. return PHPExcel_Calculation_Functions::VALUE();
  1286. }
  1287. }
  1288. // Execute function
  1289. if ($timeValue >= 1) {
  1290. $timeValue = fmod($timeValue, 1);
  1291. } elseif ($timeValue < 0.0) {
  1292. return PHPExcel_Calculation_Functions::NaN();
  1293. }
  1294. $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  1295. return (int) gmdate('i', $timeValue);
  1296. }
  1297. /**
  1298. * SECONDOFMINUTE
  1299. *
  1300. * Returns the seconds of a time value.
  1301. * The second is given as an integer in the range 0 (zero) to 59.
  1302. *
  1303. * Excel Function:
  1304. * SECOND(timeValue)
  1305. *
  1306. * @param mixed $timeValue Excel date serial value (float), PHP date timestamp (integer),
  1307. * PHP DateTime object, or a standard time string
  1308. * @return int Second
  1309. */
  1310. public static function SECONDOFMINUTE($timeValue = 0)
  1311. {
  1312. $timeValue = PHPExcel_Calculation_Functions::flattenSingleValue($timeValue);
  1313. if (!is_numeric($timeValue)) {
  1314. if (PHPExcel_Calculation_Functions::getCompatibilityMode() == PHPExcel_Calculation_Functions::COMPATIBILITY_GNUMERIC) {
  1315. $testVal = strtok($timeValue, '/-: ');
  1316. if (strlen($testVal) < strlen($timeValue)) {
  1317. return PHPExcel_Calculation_Functions::VALUE();
  1318. }
  1319. }
  1320. $timeValue = self::getTimeValue($timeValue);
  1321. if (is_string($timeValue)) {
  1322. return PHPExcel_Calculation_Functions::VALUE();
  1323. }
  1324. }
  1325. // Execute function
  1326. if ($timeValue >= 1) {
  1327. $timeValue = fmod($timeValue, 1);
  1328. } elseif ($timeValue < 0.0) {
  1329. return PHPExcel_Calculation_Functions::NaN();
  1330. }
  1331. $timeValue = PHPExcel_Shared_Date::ExcelToPHP($timeValue);
  1332. return (int) gmdate('s', $timeValue);
  1333. }
  1334. /**
  1335. * EDATE
  1336. *
  1337. * Returns the serial number that represents the date that is the indicated number of months
  1338. * before or after a specified date (the start_date).
  1339. * Use EDATE to calculate maturity dates or due dates that fall on the same day of the month
  1340. * as the date of issue.
  1341. *
  1342. * Excel Function:
  1343. * EDATE(dateValue,adjustmentMonths)
  1344. *
  1345. * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
  1346. * PHP DateTime object, or a standard date string
  1347. * @param int $adjustmentMonths The number of months before or after start_date.
  1348. * A positive value for months yields a future date;
  1349. * a negative value yields a past date.
  1350. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  1351. * depending on the value of the ReturnDateType flag
  1352. */
  1353. public static function EDATE($dateValue = 1, $adjustmentMonths = 0)
  1354. {
  1355. $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
  1356. $adjustmentMonths = PHPExcel_Calculation_Functions::flattenSingleValue($adjustmentMonths);
  1357. if (!is_numeric($adjustmentMonths)) {
  1358. return PHPExcel_Calculation_Functions::VALUE();
  1359. }
  1360. $adjustmentMonths = floor($adjustmentMonths);
  1361. if (is_string($dateValue = self::getDateValue($dateValue))) {
  1362. return PHPExcel_Calculation_Functions::VALUE();
  1363. }
  1364. // Execute function
  1365. $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths);
  1366. switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
  1367. case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
  1368. return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
  1369. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
  1370. return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
  1371. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
  1372. return $PHPDateObject;
  1373. }
  1374. }
  1375. /**
  1376. * EOMONTH
  1377. *
  1378. * Returns the date value for the last day of the month that is the indicated number of months
  1379. * before or after start_date.
  1380. * Use EOMONTH to calculate maturity dates or due dates that fall on the last day of the month.
  1381. *
  1382. * Excel Function:
  1383. * EOMONTH(dateValue,adjustmentMonths)
  1384. *
  1385. * @param mixed $dateValue Excel date serial value (float), PHP date timestamp (integer),
  1386. * PHP DateTime object, or a standard date string
  1387. * @param int $adjustmentMonths The number of months before or after start_date.
  1388. * A positive value for months yields a future date;
  1389. * a negative value yields a past date.
  1390. * @return mixed Excel date/time serial value, PHP date/time serial value or PHP date/time object,
  1391. * depending on the value of the ReturnDateType flag
  1392. */
  1393. public static function EOMONTH($dateValue = 1, $adjustmentMonths = 0)
  1394. {
  1395. $dateValue = PHPExcel_Calculation_Functions::flattenSingleValue($dateValue);
  1396. $adjustmentMonths = PHPExcel_Calculation_Functions::flattenSingleValue($adjustmentMonths);
  1397. if (!is_numeric($adjustmentMonths)) {
  1398. return PHPExcel_Calculation_Functions::VALUE();
  1399. }
  1400. $adjustmentMonths = floor($adjustmentMonths);
  1401. if (is_string($dateValue = self::getDateValue($dateValue))) {
  1402. return PHPExcel_Calculation_Functions::VALUE();
  1403. }
  1404. // Execute function
  1405. $PHPDateObject = self::adjustDateByMonths($dateValue, $adjustmentMonths+1);
  1406. $adjustDays = (int) $PHPDateObject->format('d');
  1407. $adjustDaysString = '-' . $adjustDays . ' days';
  1408. $PHPDateObject->modify($adjustDaysString);
  1409. switch (PHPExcel_Calculation_Functions::getReturnDateType()) {
  1410. case PHPExcel_Calculation_Functions::RETURNDATE_EXCEL:
  1411. return (float) PHPExcel_Shared_Date::PHPToExcel($PHPDateObject);
  1412. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_NUMERIC:
  1413. return (integer) PHPExcel_Shared_Date::ExcelToPHP(PHPExcel_Shared_Date::PHPToExcel($PHPDateObject));
  1414. case PHPExcel_Calculation_Functions::RETURNDATE_PHP_OBJECT:
  1415. return $PHPDateObject;
  1416. }
  1417. }
  1418. }