Date.php 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390
  1. <?php
  2. /**
  3. * PHPExcel
  4. *
  5. * Copyright (c) 2006 - 2013 PHPExcel
  6. *
  7. * This library is free software; you can redistribute it and/or
  8. * modify it under the terms of the GNU Lesser General Public
  9. * License as published by the Free Software Foundation; either
  10. * version 2.1 of the License, or (at your option) any later version.
  11. *
  12. * This library is distributed in the hope that it will be useful,
  13. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  14. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  15. * Lesser General Public License for more details.
  16. *
  17. * You should have received a copy of the GNU Lesser General Public
  18. * License along with this library; if not, write to the Free Software
  19. * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  20. *
  21. * @category PHPExcel
  22. * @package PHPExcel_Shared
  23. * @copyright Copyright (c) 2006 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel)
  24. * @license http://www.gnu.org/licenses/old-licenses/lgpl-2.1.txt LGPL
  25. * @version 1.7.9, 2013-06-02
  26. */
  27. /**
  28. * PHPExcel_Shared_Date
  29. *
  30. * @category PHPExcel
  31. * @package PHPExcel_Shared
  32. * @copyright Copyright (c) 2006 - 2013 PHPExcel (http://www.codeplex.com/PHPExcel)
  33. */
  34. class PHPExcel_Shared_Date
  35. {
  36. /** constants */
  37. const CALENDAR_WINDOWS_1900 = 1900; // Base date of 1st Jan 1900 = 1.0
  38. const CALENDAR_MAC_1904 = 1904; // Base date of 2nd Jan 1904 = 1.0
  39. /*
  40. * Names of the months of the year, indexed by shortname
  41. * Planned usage for locale settings
  42. *
  43. * @public
  44. * @var string[]
  45. */
  46. public static $_monthNames = array( 'Jan' => 'January',
  47. 'Feb' => 'February',
  48. 'Mar' => 'March',
  49. 'Apr' => 'April',
  50. 'May' => 'May',
  51. 'Jun' => 'June',
  52. 'Jul' => 'July',
  53. 'Aug' => 'August',
  54. 'Sep' => 'September',
  55. 'Oct' => 'October',
  56. 'Nov' => 'November',
  57. 'Dec' => 'December',
  58. );
  59. /*
  60. * Names of the months of the year, indexed by shortname
  61. * Planned usage for locale settings
  62. *
  63. * @public
  64. * @var string[]
  65. */
  66. public static $_numberSuffixes = array( 'st',
  67. 'nd',
  68. 'rd',
  69. 'th',
  70. );
  71. /*
  72. * Base calendar year to use for calculations
  73. *
  74. * @private
  75. * @var int
  76. */
  77. protected static $_excelBaseDate = self::CALENDAR_WINDOWS_1900;
  78. /**
  79. * Set the Excel calendar (Windows 1900 or Mac 1904)
  80. *
  81. * @param integer $baseDate Excel base date (1900 or 1904)
  82. * @return boolean Success or failure
  83. */
  84. public static function setExcelCalendar($baseDate) {
  85. if (($baseDate == self::CALENDAR_WINDOWS_1900) ||
  86. ($baseDate == self::CALENDAR_MAC_1904)) {
  87. self::$_excelBaseDate = $baseDate;
  88. return TRUE;
  89. }
  90. return FALSE;
  91. } // function setExcelCalendar()
  92. /**
  93. * Return the Excel calendar (Windows 1900 or Mac 1904)
  94. *
  95. * @return integer Excel base date (1900 or 1904)
  96. */
  97. public static function getExcelCalendar() {
  98. return self::$_excelBaseDate;
  99. } // function getExcelCalendar()
  100. /**
  101. * Convert a date from Excel to PHP
  102. *
  103. * @param long $dateValue Excel date/time value
  104. * @param boolean $adjustToTimezone Flag indicating whether $dateValue should be treated as
  105. * a UST timestamp, or adjusted to UST
  106. * @param string $timezone The timezone for finding the adjustment from UST
  107. * @return long PHP serialized date/time
  108. */
  109. public static function ExcelToPHP($dateValue = 0, $adjustToTimezone = FALSE, $timezone = NULL) {
  110. if (self::$_excelBaseDate == self::CALENDAR_WINDOWS_1900) {
  111. $my_excelBaseDate = 25569;
  112. // Adjust for the spurious 29-Feb-1900 (Day 60)
  113. if ($dateValue < 60) {
  114. --$my_excelBaseDate;
  115. }
  116. } else {
  117. $my_excelBaseDate = 24107;
  118. }
  119. // Perform conversion
  120. if ($dateValue >= 1) {
  121. $utcDays = $dateValue - $my_excelBaseDate;
  122. $returnValue = round($utcDays * 86400);
  123. if (($returnValue <= PHP_INT_MAX) && ($returnValue >= -PHP_INT_MAX)) {
  124. $returnValue = (integer) $returnValue;
  125. }
  126. } else {
  127. $hours = round($dateValue * 24);
  128. $mins = round($dateValue * 1440) - round($hours * 60);
  129. $secs = round($dateValue * 86400) - round($hours * 3600) - round($mins * 60);
  130. $returnValue = (integer) gmmktime($hours, $mins, $secs);
  131. }
  132. $timezoneAdjustment = ($adjustToTimezone) ?
  133. PHPExcel_Shared_TimeZone::getTimezoneAdjustment($timezone, $returnValue) :
  134. 0;
  135. // Return
  136. return $returnValue + $timezoneAdjustment;
  137. } // function ExcelToPHP()
  138. /**
  139. * Convert a date from Excel to a PHP Date/Time object
  140. *
  141. * @param integer $dateValue Excel date/time value
  142. * @return integer PHP date/time object
  143. */
  144. public static function ExcelToPHPObject($dateValue = 0) {
  145. $dateTime = self::ExcelToPHP($dateValue);
  146. $days = floor($dateTime / 86400);
  147. $time = round((($dateTime / 86400) - $days) * 86400);
  148. $hours = round($time / 3600);
  149. $minutes = round($time / 60) - ($hours * 60);
  150. $seconds = round($time) - ($hours * 3600) - ($minutes * 60);
  151. $dateObj = date_create('1-Jan-1970+'.$days.' days');
  152. $dateObj->setTime($hours,$minutes,$seconds);
  153. return $dateObj;
  154. } // function ExcelToPHPObject()
  155. /**
  156. * Convert a date from PHP to Excel
  157. *
  158. * @param mixed $dateValue PHP serialized date/time or date object
  159. * @param boolean $adjustToTimezone Flag indicating whether $dateValue should be treated as
  160. * a UST timestamp, or adjusted to UST
  161. * @param string $timezone The timezone for finding the adjustment from UST
  162. * @return mixed Excel date/time value
  163. * or boolean FALSE on failure
  164. */
  165. public static function PHPToExcel($dateValue = 0, $adjustToTimezone = FALSE, $timezone = NULL) {
  166. $saveTimeZone = date_default_timezone_get();
  167. date_default_timezone_set('UTC');
  168. $retValue = FALSE;
  169. if ((is_object($dateValue)) && ($dateValue instanceof DateTime)) {
  170. $retValue = self::FormattedPHPToExcel( $dateValue->format('Y'), $dateValue->format('m'), $dateValue->format('d'),
  171. $dateValue->format('H'), $dateValue->format('i'), $dateValue->format('s')
  172. );
  173. } elseif (is_numeric($dateValue)) {
  174. $retValue = self::FormattedPHPToExcel( date('Y',$dateValue), date('m',$dateValue), date('d',$dateValue),
  175. date('H',$dateValue), date('i',$dateValue), date('s',$dateValue)
  176. );
  177. }
  178. date_default_timezone_set($saveTimeZone);
  179. return $retValue;
  180. } // function PHPToExcel()
  181. /**
  182. * FormattedPHPToExcel
  183. *
  184. * @param long $year
  185. * @param long $month
  186. * @param long $day
  187. * @param long $hours
  188. * @param long $minutes
  189. * @param long $seconds
  190. * @return long Excel date/time value
  191. */
  192. public static function FormattedPHPToExcel($year, $month, $day, $hours=0, $minutes=0, $seconds=0) {
  193. if (self::$_excelBaseDate == self::CALENDAR_WINDOWS_1900) {
  194. //
  195. // Fudge factor for the erroneous fact that the year 1900 is treated as a Leap Year in MS Excel
  196. // This affects every date following 28th February 1900
  197. //
  198. $excel1900isLeapYear = TRUE;
  199. if (($year == 1900) && ($month <= 2)) { $excel1900isLeapYear = FALSE; }
  200. $my_excelBaseDate = 2415020;
  201. } else {
  202. $my_excelBaseDate = 2416481;
  203. $excel1900isLeapYear = FALSE;
  204. }
  205. // Julian base date Adjustment
  206. if ($month > 2) {
  207. $month -= 3;
  208. } else {
  209. $month += 9;
  210. --$year;
  211. }
  212. // Calculate the Julian Date, then subtract the Excel base date (JD 2415020 = 31-Dec-1899 Giving Excel Date of 0)
  213. $century = substr($year,0,2);
  214. $decade = substr($year,2,2);
  215. $excelDate = floor((146097 * $century) / 4) + floor((1461 * $decade) / 4) + floor((153 * $month + 2) / 5) + $day + 1721119 - $my_excelBaseDate + $excel1900isLeapYear;
  216. $excelTime = (($hours * 3600) + ($minutes * 60) + $seconds) / 86400;
  217. return (float) $excelDate + $excelTime;
  218. } // function FormattedPHPToExcel()
  219. /**
  220. * Is a given cell a date/time?
  221. *
  222. * @param PHPExcel_Cell $pCell
  223. * @return boolean
  224. */
  225. public static function isDateTime(PHPExcel_Cell $pCell) {
  226. return self::isDateTimeFormat(
  227. $pCell->getWorksheet()->getStyle(
  228. $pCell->getCoordinate()
  229. )->getNumberFormat()
  230. );
  231. } // function isDateTime()
  232. /**
  233. * Is a given number format a date/time?
  234. *
  235. * @param PHPExcel_Style_NumberFormat $pFormat
  236. * @return boolean
  237. */
  238. public static function isDateTimeFormat(PHPExcel_Style_NumberFormat $pFormat) {
  239. return self::isDateTimeFormatCode($pFormat->getFormatCode());
  240. } // function isDateTimeFormat()
  241. private static $possibleDateFormatCharacters = 'eymdHs';
  242. /**
  243. * Is a given number format code a date/time?
  244. *
  245. * @param string $pFormatCode
  246. * @return boolean
  247. */
  248. public static function isDateTimeFormatCode($pFormatCode = '') {
  249. // Switch on formatcode
  250. switch ($pFormatCode) {
  251. // General contains an epoch letter 'e', so we trap for it explicitly here
  252. case PHPExcel_Style_NumberFormat::FORMAT_GENERAL:
  253. return FALSE;
  254. // Explicitly defined date formats
  255. case PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD:
  256. case PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDD2:
  257. case PHPExcel_Style_NumberFormat::FORMAT_DATE_DDMMYYYY:
  258. case PHPExcel_Style_NumberFormat::FORMAT_DATE_DMYSLASH:
  259. case PHPExcel_Style_NumberFormat::FORMAT_DATE_DMYMINUS:
  260. case PHPExcel_Style_NumberFormat::FORMAT_DATE_DMMINUS:
  261. case PHPExcel_Style_NumberFormat::FORMAT_DATE_MYMINUS:
  262. case PHPExcel_Style_NumberFormat::FORMAT_DATE_DATETIME:
  263. case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME1:
  264. case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME2:
  265. case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME3:
  266. case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME4:
  267. case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME5:
  268. case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME6:
  269. case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME7:
  270. case PHPExcel_Style_NumberFormat::FORMAT_DATE_TIME8:
  271. case PHPExcel_Style_NumberFormat::FORMAT_DATE_YYYYMMDDSLASH:
  272. case PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX14:
  273. case PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX15:
  274. case PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX16:
  275. case PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX17:
  276. case PHPExcel_Style_NumberFormat::FORMAT_DATE_XLSX22:
  277. return TRUE;
  278. }
  279. // Typically number, currency or accounting (or occasionally fraction) formats
  280. if ((substr($pFormatCode,0,1) == '_') || (substr($pFormatCode,0,2) == '0 ')) {
  281. return FALSE;
  282. }
  283. // Try checking for any of the date formatting characters that don't appear within square braces
  284. if (preg_match('/(^|\])[^\[]*['.self::$possibleDateFormatCharacters.']/i',$pFormatCode)) {
  285. // We might also have a format mask containing quoted strings...
  286. // we don't want to test for any of our characters within the quoted blocks
  287. if (strpos($pFormatCode,'"') !== FALSE) {
  288. $segMatcher = FALSE;
  289. foreach(explode('"',$pFormatCode) as $subVal) {
  290. // Only test in alternate array entries (the non-quoted blocks)
  291. if (($segMatcher = !$segMatcher) &&
  292. (preg_match('/(^|\])[^\[]*['.self::$possibleDateFormatCharacters.']/i',$subVal))) {
  293. return TRUE;
  294. }
  295. }
  296. return FALSE;
  297. }
  298. return TRUE;
  299. }
  300. // No date...
  301. return FALSE;
  302. } // function isDateTimeFormatCode()
  303. /**
  304. * Convert a date/time string to Excel time
  305. *
  306. * @param string $dateValue Examples: '2009-12-31', '2009-12-31 15:59', '2009-12-31 15:59:10'
  307. * @return float|FALSE Excel date/time serial value
  308. */
  309. public static function stringToExcel($dateValue = '') {
  310. if (strlen($dateValue) < 2)
  311. return FALSE;
  312. if (!preg_match('/^(\d{1,4}[ \.\/\-][A-Z]{3,9}([ \.\/\-]\d{1,4})?|[A-Z]{3,9}[ \.\/\-]\d{1,4}([ \.\/\-]\d{1,4})?|\d{1,4}[ \.\/\-]\d{1,4}([ \.\/\-]\d{1,4})?)( \d{1,2}:\d{1,2}(:\d{1,2})?)?$/iu', $dateValue))
  313. return FALSE;
  314. $dateValueNew = PHPExcel_Calculation_DateTime::DATEVALUE($dateValue);
  315. if ($dateValueNew === PHPExcel_Calculation_Functions::VALUE()) {
  316. return FALSE;
  317. } else {
  318. if (strpos($dateValue, ':') !== FALSE) {
  319. $timeValue = PHPExcel_Calculation_DateTime::TIMEVALUE($dateValue);
  320. if ($timeValue === PHPExcel_Calculation_Functions::VALUE()) {
  321. return FALSE;
  322. }
  323. $dateValueNew += $timeValue;
  324. }
  325. return $dateValueNew;
  326. }
  327. }
  328. public static function monthStringToNumber($month) {
  329. $monthIndex = 1;
  330. foreach(self::$_monthNames as $shortMonthName => $longMonthName) {
  331. if (($month === $longMonthName) || ($month === $shortMonthName)) {
  332. return $monthIndex;
  333. }
  334. ++$monthIndex;
  335. }
  336. return $month;
  337. }
  338. public static function dayStringToNumber($day) {
  339. $strippedDayValue = (str_replace(self::$_numberSuffixes,'',$day));
  340. if (is_numeric($strippedDayValue)) {
  341. return $strippedDayValue;
  342. }
  343. return $day;
  344. }
  345. }