Date.php 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288
  1. <?php
  2. namespace fast;
  3. /**
  4. * 日期时间处理类
  5. */
  6. class Date
  7. {
  8. const YEAR = 31536000;
  9. const MONTH = 2592000;
  10. const WEEK = 604800;
  11. const DAY = 86400;
  12. const HOUR = 3600;
  13. const MINUTE = 60;
  14. /**
  15. * 计算两个时区间相差的时长,单位为秒
  16. *
  17. * $seconds = self::offset('America/Chicago', 'GMT');
  18. *
  19. * [!!] A list of time zones that PHP supports can be found at
  20. * <http://php.net/timezones>.
  21. *
  22. * @param string $remote timezone that to find the offset of
  23. * @param string $local timezone used as the baseline
  24. * @param mixed $now UNIX timestamp or date string
  25. * @return integer
  26. */
  27. public static function offset($remote, $local = NULL, $now = NULL)
  28. {
  29. if ($local === NULL)
  30. {
  31. // Use the default timezone
  32. $local = date_default_timezone_get();
  33. }
  34. if (is_int($now))
  35. {
  36. // Convert the timestamp into a string
  37. $now = date(DateTime::RFC2822, $now);
  38. }
  39. // Create timezone objects
  40. $zone_remote = new DateTimeZone($remote);
  41. $zone_local = new DateTimeZone($local);
  42. // Create date objects from timezones
  43. $time_remote = new DateTime($now, $zone_remote);
  44. $time_local = new DateTime($now, $zone_local);
  45. // Find the offset
  46. $offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
  47. return $offset;
  48. }
  49. /**
  50. * 计算两个时间戳之间相差的时间
  51. *
  52. * $span = self::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
  53. * $span = self::span(60, 182, 'minutes'); // 2
  54. *
  55. * @param int $remote timestamp to find the span of
  56. * @param int $local timestamp to use as the baseline
  57. * @param string $output formatting string
  58. * @return string when only a single output is requested
  59. * @return array associative list of all outputs requested
  60. */
  61. public static function span($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
  62. {
  63. // Normalize output
  64. $output = trim(strtolower((string) $output));
  65. if (!$output)
  66. {
  67. // Invalid output
  68. return FALSE;
  69. }
  70. // Array with the output formats
  71. $output = preg_split('/[^a-z]+/', $output);
  72. // Convert the list of outputs to an associative array
  73. $output = array_combine($output, array_fill(0, count($output), 0));
  74. // Make the output values into keys
  75. extract(array_flip($output), EXTR_SKIP);
  76. if ($local === NULL)
  77. {
  78. // Calculate the span from the current time
  79. $local = time();
  80. }
  81. // Calculate timespan (seconds)
  82. $timespan = abs($remote - $local);
  83. if (isset($output['years']))
  84. {
  85. $timespan -= self::YEAR * ($output['years'] = (int) floor($timespan / self::YEAR));
  86. }
  87. if (isset($output['months']))
  88. {
  89. $timespan -= self::MONTH * ($output['months'] = (int) floor($timespan / self::MONTH));
  90. }
  91. if (isset($output['weeks']))
  92. {
  93. $timespan -= self::WEEK * ($output['weeks'] = (int) floor($timespan / self::WEEK));
  94. }
  95. if (isset($output['days']))
  96. {
  97. $timespan -= self::DAY * ($output['days'] = (int) floor($timespan / self::DAY));
  98. }
  99. if (isset($output['hours']))
  100. {
  101. $timespan -= self::HOUR * ($output['hours'] = (int) floor($timespan / self::HOUR));
  102. }
  103. if (isset($output['minutes']))
  104. {
  105. $timespan -= self::MINUTE * ($output['minutes'] = (int) floor($timespan / self::MINUTE));
  106. }
  107. // Seconds ago, 1
  108. if (isset($output['seconds']))
  109. {
  110. $output['seconds'] = $timespan;
  111. }
  112. if (count($output) === 1)
  113. {
  114. // Only a single output was requested, return it
  115. return array_pop($output);
  116. }
  117. // Return array
  118. return $output;
  119. }
  120. /**
  121. * 格式化 UNIX 时间戳为人易读的字符串
  122. *
  123. * @param int Unix 时间戳
  124. * @param mixed $local 本地时间
  125. *
  126. * @return string 格式化的日期字符串
  127. */
  128. public static function human($remote, $local = null)
  129. {
  130. $timediff = (is_null($local) || $local ? time() : $local) - $remote;
  131. $chunks = array(
  132. array(60 * 60 * 24 * 365, 'year'),
  133. array(60 * 60 * 24 * 30, 'month'),
  134. array(60 * 60 * 24 * 7, 'week'),
  135. array(60 * 60 * 24, 'day'),
  136. array(60 * 60, 'hour'),
  137. array(60, 'minute'),
  138. array(1, 'second')
  139. );
  140. for ($i = 0, $j = count($chunks); $i < $j; $i++)
  141. {
  142. $seconds = $chunks[$i][0];
  143. $name = $chunks[$i][1];
  144. if (($count = floor($timediff / $seconds)) != 0)
  145. {
  146. break;
  147. }
  148. }
  149. return __("%d {$name}%s ago", $count, ($count > 1 ? 's' : ''));
  150. }
  151. /**
  152. * 判断Unix时间是否满足Cron指定的执行条件
  153. *
  154. * @param string $cron Crontab格式
  155. * @param string $time 时间,默认为当前时间
  156. * @return boolean
  157. */
  158. public static function cron($cron, $time = null)
  159. {
  160. $time = is_null($time) ? time() : $time;
  161. $cron_parts = explode(' ', $cron);
  162. if (count($cron_parts) != 5)
  163. {
  164. return false;
  165. }
  166. list($min, $hour, $day, $mon, $week) = explode(' ', $cron);
  167. $to_check = array('min' => 'i', 'hour' => 'G', 'day' => 'j', 'mon' => 'n', 'week' => 'w');
  168. $ranges = array(
  169. 'min' => '0-59',
  170. 'hour' => '0-23',
  171. 'day' => '1-31',
  172. 'mon' => '1-12',
  173. 'week' => '0-6',
  174. );
  175. foreach ($to_check as $part => $c)
  176. {
  177. $val = $$part;
  178. $values = [];
  179. if (strpos($val, '/') !== false)
  180. {
  181. //Get the range and step
  182. list($range, $steps) = explode('/', $val);
  183. //Now get the start and stop
  184. if ($range == '*')
  185. {
  186. $range = $ranges[$part];
  187. }
  188. list($start, $stop) = explode('-', $range);
  189. for ($i = $start; $i <= $stop; $i = $i + $steps)
  190. {
  191. $values[] = $i;
  192. }
  193. }
  194. else
  195. {
  196. $k = explode(',', $val);
  197. foreach ($k as $v)
  198. {
  199. if (strpos($v, '-') !== false)
  200. {
  201. list($start, $stop) = explode('-', $v);
  202. for ($i = $start; $i <= $stop; $i++)
  203. {
  204. $values[] = $i;
  205. }
  206. }
  207. else
  208. {
  209. $values[] = $v;
  210. }
  211. }
  212. }
  213. if (!in_array(date($c, $time), $values) and ( strval($val) != '*'))
  214. {
  215. return false;
  216. }
  217. }
  218. return true;
  219. }
  220. /**
  221. * 获取一个基于时间偏移的Unix时间戳
  222. *
  223. * @param string $type 时间类型,默认为day,可选minute,hour,day,week,month,quarter,year
  224. * @param int $offset 时间偏移量 默认为0,正数表示当前type之后,负数表示当前type之前
  225. * @param string $position 时间的开始或结束,默认为begin,可选前(begin,start,first,front),end
  226. * @param int $year 基准年,默认为null,即以当前年为基准
  227. * @param int $month 基准月,默认为null,即以当前月为基准
  228. * @param int $day 基准天,默认为null,即以当前天为基准
  229. * @param int $hour 基准小时,默认为null,即以当前年小时基准
  230. * @param int $minute 基准分钟,默认为null,即以当前分钟为基准
  231. * @return int 处理后的Unix时间戳
  232. */
  233. public static function unixtime($type = 'day', $offset = 0, $position = 'begin', $year = null, $month = null, $day = null, $hour = null, $minute = null)
  234. {
  235. $year = is_null($year) ? date('Y') : $year;
  236. $month = is_null($month) ? date('m') : $month;
  237. $day = is_null($day) ? date('d') : $day;
  238. $hour = is_null($hour) ? date('H') : $hour;
  239. $minute = is_null($minute) ? date('i') : $minute;
  240. $position = in_array($position, array('begin', 'start', 'first', 'front'));
  241. switch ($type)
  242. {
  243. case 'minute':
  244. $time = $position ? mktime($hour, $minute + $offset, 0, $month, $day, $year) : mktime($hour, $minute + $offset, 59, $month, $day, $year);
  245. break;
  246. case 'hour':
  247. $time = $position ? mktime($hour + $offset, 0, 0, $month, $day, $year) : mktime($hour + $offset, 59, 59, $month, $day, $year);
  248. break;
  249. case 'day':
  250. $time = $position ? mktime(0, 0, 0, $month, $day + $offset, $year) : mktime(23, 59, 59, $month, $day + $offset, $year);
  251. break;
  252. case 'week':
  253. $time = $position ?
  254. mktime(0, 0, 0, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 1 - 7 * (-$offset), $year) :
  255. mktime(23, 59, 59, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 7 - 7 * (-$offset), $year);
  256. break;
  257. case 'month':
  258. $time = $position ? mktime(0, 0, 0, $month + $offset, 1, $year) : mktime(23, 59, 59, $month + $offset, get_month_days($month + $offset, $year), $year);
  259. break;
  260. case 'quarter':
  261. $time = $position ?
  262. mktime(0, 0, 0, 1 + ((ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) - 1) * 3, 1, $year) :
  263. mktime(23, 59, 59, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, get_month_days((ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, $year), $year);
  264. break;
  265. case 'year':
  266. $time = $position ? mktime(0, 0, 0, 1, 1, $year + $offset) : mktime(23, 59, 59, 12, 31, $year + $offset);
  267. break;
  268. default:
  269. $time = mktime($hour, $minute, 0, $month, $day, $year);
  270. break;
  271. }
  272. return $time;
  273. }
  274. }