Date.php 15 KB

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