DateTimeParser.php 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242
  1. <?php
  2. /**
  3. * Link : http://www.phpcorner.net
  4. * User : qingbing<780042175@qq.com>
  5. * Date : 2018-11-07
  6. * Version : 1.0
  7. */
  8. namespace Helper;
  9. defined('APP_CHARSET') or define('APP_CHARSET', 'utf-8');
  10. class DateTimeParser
  11. {
  12. /* @var bool 标志 php 的 "mbstring" 扩展是否为可用状态 */
  13. static private $_mbstringAvailable;
  14. /**
  15. * 将日期转化成时间戳
  16. * @param string $value
  17. * @param string $pattern
  18. * @param array $defaults 年,月,日,小时,分钟和秒的默认值
  19. * @return int|false 如果解析失败,则返回False
  20. */
  21. static public function parse($value, $pattern = 'MM/dd/yyyy', $defaults = [])
  22. {
  23. if (null === self::$_mbstringAvailable) {
  24. self::$_mbstringAvailable = extension_loaded('mbstring');
  25. }
  26. $ps = self::analysisPattern($pattern);
  27. $i = 0;
  28. $n = self::$_mbstringAvailable ? mb_strlen($value, APP_CHARSET) : strlen($value);
  29. foreach ($ps as $p) {
  30. switch ($p) {
  31. case 'yyyy':
  32. {
  33. if (false === ($year = self::parseInteger($value, $i, 4, 4)))
  34. return false;
  35. $i += 4;
  36. break;
  37. }
  38. case 'yy':
  39. {
  40. if (false === ($year = self::parseInteger($value, $i, 1, 2)))
  41. return false;
  42. $i += strlen($year);
  43. break;
  44. }
  45. case 'MM':
  46. {
  47. if (false === ($month = self::parseInteger($value, $i, 2, 2)))
  48. return false;
  49. $i += 2;
  50. break;
  51. }
  52. case 'M':
  53. {
  54. if (false === ($month = self::parseInteger($value, $i, 1, 2)))
  55. return false;
  56. $i += strlen($month);
  57. break;
  58. }
  59. case 'dd':
  60. {
  61. if (false === ($day = self::parseInteger($value, $i, 2, 2)))
  62. return false;
  63. $i += 2;
  64. break;
  65. }
  66. case 'd':
  67. {
  68. if (false === ($day = self::parseInteger($value, $i, 1, 2)))
  69. return false;
  70. $i += strlen($day);
  71. break;
  72. }
  73. case 'h':
  74. case 'H':
  75. {
  76. if (false === ($hour = self::parseInteger($value, $i, 1, 2)))
  77. return false;
  78. $i += strlen($hour);
  79. break;
  80. }
  81. case 'hh':
  82. case 'HH':
  83. {
  84. if (false === ($hour = self::parseInteger($value, $i, 2, 2)))
  85. return false;
  86. $i += 2;
  87. break;
  88. }
  89. case 'm':
  90. {
  91. if (false === ($minute = self::parseInteger($value, $i, 1, 2)))
  92. return false;
  93. $i += strlen($minute);
  94. break;
  95. }
  96. case 'mm':
  97. {
  98. if (false === ($minute = self::parseInteger($value, $i, 2, 2)))
  99. return false;
  100. $i += 2;
  101. break;
  102. }
  103. case 's':
  104. {
  105. if (false === ($second = self::parseInteger($value, $i, 1, 2)))
  106. return false;
  107. $i += strlen($second);
  108. break;
  109. }
  110. case 'ss':
  111. {
  112. if (false === ($second = self::parseInteger($value, $i, 2, 2)))
  113. return false;
  114. $i += 2;
  115. break;
  116. }
  117. case 'a':
  118. {
  119. if (false === ($ampm = self::parseAmPm($value, $i)))
  120. return false;
  121. if (isset($hour)) {
  122. if ($hour == 12 && $ampm === 'am')
  123. $hour = 0;
  124. else if ($hour < 12 && $ampm === 'pm')
  125. $hour += 12;
  126. }
  127. $i += 2;
  128. break;
  129. }
  130. default:
  131. {
  132. $tn = self::$_mbstringAvailable ? mb_strlen($p, APP_CHARSET) : strlen($p);
  133. if ($i >= $n || ($p{0} != '?' && (self::$_mbstringAvailable ? mb_substr($value, $i, $tn, APP_CHARSET) : substr($value, $i, $tn)) !== $p))
  134. return false;
  135. $i += $tn;
  136. break;
  137. }
  138. }
  139. }
  140. if ($i < $n)
  141. return false;
  142. if (!isset($year))
  143. $year = isset($defaults['year']) ? $defaults['year'] : date('Y');
  144. if (!isset($month))
  145. $month = isset($defaults['month']) ? $defaults['month'] : date('n');
  146. if (!isset($day))
  147. $day = isset($defaults['day']) ? $defaults['day'] : date('j');
  148. if (strlen($year) === 2) {
  149. $year = Unit::getFullYear($year);
  150. }
  151. $year = (int)$year;
  152. $month = (int)$month;
  153. $day = (int)$day;
  154. if (
  155. !isset($hour) && !isset($minute)
  156. && !isset($second) && !isset($defaults['hour'])
  157. && !isset($defaults['minute']) && !isset($defaults['second'])
  158. ) {
  159. $hour = $minute = $second = 0;
  160. } else {
  161. if (!isset($hour)) {
  162. $hour = isset($defaults['hour']) ? $defaults['hour'] : date('H');
  163. }
  164. if (!isset($minute)) {
  165. $minute = isset($defaults['minute']) ? $defaults['minute'] : date('i');
  166. }
  167. if (!isset($second)) {
  168. $second = isset($defaults['second']) ? $defaults['second'] : date('s');
  169. }
  170. $hour = (int)$hour;
  171. $minute = (int)$minute;
  172. $second = (int)$second;
  173. }
  174. if (Unit::isValidDate($year, $month, $day) && Unit::isValidTime($hour, $minute, $second)) {
  175. return Unit::getTimestamp($hour, $minute, $second, $month, $day, $year);
  176. }
  177. return false;
  178. }
  179. /**
  180. * 解析日期或时间的格式串
  181. * @param string $pattern
  182. * @return array
  183. */
  184. static protected function analysisPattern($pattern)
  185. {
  186. if (!($n = self::$_mbstringAvailable ? mb_strlen($pattern, APP_CHARSET) : strlen($pattern))) {
  187. return [];
  188. }
  189. $ps = [];
  190. $c0 = self::$_mbstringAvailable ? mb_substr($pattern, 0, 1, APP_CHARSET) : substr($pattern, 0, 1);
  191. for ($start = 0, $i = 1; $i < $n; ++$i) {
  192. $c = self::$_mbstringAvailable ? mb_substr($pattern, $i, 1, APP_CHARSET) : substr($pattern, $i, 1);
  193. if ($c !== $c0) {
  194. $ps[] = self::$_mbstringAvailable ? mb_substr($pattern, $start, $i - $start, APP_CHARSET) : substr($pattern, $start, $i - $start);
  195. $c0 = $c;
  196. $start = $i;
  197. }
  198. }
  199. $ps[] = self::$_mbstringAvailable ? mb_substr($pattern, $start, $n - $start, APP_CHARSET) : substr($pattern, $start, $n - $start);
  200. return $ps;
  201. }
  202. /**
  203. * 将相应的字符串转换成数字
  204. * @param string $value the date string to be parsed
  205. * @param int $offset starting offset
  206. * @param int $minLength minimum length
  207. * @param int $maxLength maximum length
  208. * @return string parsed integer value
  209. */
  210. static protected function parseInteger($value, $offset, $minLength, $maxLength)
  211. {
  212. for ($len = $maxLength; $len >= $minLength; --$len) {
  213. $v = self::$_mbstringAvailable ? mb_substr($value, $offset, $len, APP_CHARSET) : substr($value, $offset, $len);
  214. if (ctype_digit($v) && (self::$_mbstringAvailable ? mb_strlen($v, APP_CHARSET) : strlen($v)) >= $minLength) {
  215. return $v;
  216. }
  217. }
  218. return false;
  219. }
  220. /**
  221. * 解析上午"am"和下午"pm"
  222. * @param string $value the date string to be parsed
  223. * @param int $offset starting offset
  224. * @return string parsed day period value
  225. */
  226. static protected function parseAmPm($value, $offset)
  227. {
  228. $v = strtolower(self::$_mbstringAvailable ? mb_substr($value, $offset, 2, APP_CHARSET) : substr($value, $offset, 2));
  229. return $v === 'am' || $v === 'pm' ? $v : false;
  230. }
  231. }