Date.php 9.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. <?php
  2. declare(strict_types=1);
  3. namespace app\common\library;
  4. use DateTime;
  5. use DateTimeZone;
  6. /**
  7. * 日期时间处理类
  8. */
  9. class Date
  10. {
  11. const YEAR = 31536000;
  12. const MONTH = 2592000;
  13. const WEEK = 604800;
  14. const DAY = 86400;
  15. const HOUR = 3600;
  16. const MINUTE = 60;
  17. /**
  18. * 计算两个时区间相差的时长,单位为秒
  19. *
  20. * $seconds = self::offset('America/Chicago', 'GMT');
  21. *
  22. * [!!] A list of time zones that PHP supports can be found at
  23. * <http://php.net/timezones>.
  24. *
  25. * @param string $remote timezone that to find the offset of
  26. * @param string $local timezone used as the baseline
  27. * @param mixed $now UNIX timestamp or date string
  28. * @return integer
  29. */
  30. public static function offset(string $remote, string $local = null, mixed $now = null):int
  31. {
  32. if ($local === null) {
  33. // Use the default timezone
  34. $local = date_default_timezone_get();
  35. }
  36. if (is_int($now)) {
  37. // Convert the timestamp into a string
  38. $now = date(DateTime::RFC2822, $now);
  39. }
  40. // Create timezone objects
  41. $zone_remote = new DateTimeZone($remote);
  42. $zone_local = new DateTimeZone($local);
  43. // Create date objects from timezones
  44. $time_remote = new DateTime($now, $zone_remote);
  45. $time_local = new DateTime($now, $zone_local);
  46. // Find the offset
  47. $offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
  48. return $offset;
  49. }
  50. /**
  51. * 计算两个时间戳之间相差的时间
  52. *
  53. * $span = self::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
  54. * $span = self::span(60, 182, 'minutes'); // 2
  55. *
  56. * @param int $remote timestamp to find the span of
  57. * @param int $local timestamp to use as the baseline
  58. * @param string $output formatting string
  59. * @return string when only a single output is requested
  60. * @return array associative list of all outputs requested
  61. * @from https://github.com/kohana/ohanzee-helpers/blob/master/src/Date.php
  62. */
  63. public static function span(int $remote, int $local = null, string $output = 'years,months,weeks,days,hours,minutes,seconds'):mixed
  64. {
  65. // Normalize output
  66. $output = trim(strtolower((string)$output));
  67. if (!$output) {
  68. // Invalid output
  69. return false;
  70. }
  71. // Array with the output formats
  72. $output = preg_split('/[^a-z]+/', $output);
  73. // Convert the list of outputs to an associative array
  74. $output = array_combine($output, array_fill(0, count($output), 0));
  75. // Make the output values into keys
  76. extract(array_flip($output), EXTR_SKIP);
  77. if ($local === null) {
  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. $timespan -= self::YEAR * ($output['years'] = (int)floor($timespan / self::YEAR));
  85. }
  86. if (isset($output['months'])) {
  87. $timespan -= self::MONTH * ($output['months'] = (int)floor($timespan / self::MONTH));
  88. }
  89. if (isset($output['weeks'])) {
  90. $timespan -= self::WEEK * ($output['weeks'] = (int)floor($timespan / self::WEEK));
  91. }
  92. if (isset($output['days'])) {
  93. $timespan -= self::DAY * ($output['days'] = (int)floor($timespan / self::DAY));
  94. }
  95. if (isset($output['hours'])) {
  96. $timespan -= self::HOUR * ($output['hours'] = (int)floor($timespan / self::HOUR));
  97. }
  98. if (isset($output['minutes'])) {
  99. $timespan -= self::MINUTE * ($output['minutes'] = (int)floor($timespan / self::MINUTE));
  100. }
  101. // Seconds ago, 1
  102. if (isset($output['seconds'])) {
  103. $output['seconds'] = $timespan;
  104. }
  105. if (count($output) === 1) {
  106. // Only a single output was requested, return it
  107. return array_pop($output);
  108. }
  109. // Return array
  110. return $output;
  111. }
  112. /**
  113. * 格式化 UNIX 时间戳为人易读的字符串
  114. *
  115. * @param int Unix 时间戳
  116. * @param mixed $local 本地时间
  117. *
  118. * @return string 格式化的日期字符串
  119. */
  120. public static function human(int $remote, mixed $local = null):string
  121. {
  122. $time_diff = (is_null($local) || $local ? time() : $local) - $remote;
  123. $tense = $time_diff < 0 ? 'after' : 'ago';
  124. $time_diff = abs($time_diff);
  125. $chunks = [
  126. [60 * 60 * 24 * 365, 'year'],
  127. [60 * 60 * 24 * 30, 'month'],
  128. [60 * 60 * 24 * 7, 'week'],
  129. [60 * 60 * 24, 'day'],
  130. [60 * 60, 'hour'],
  131. [60, 'minute'],
  132. [1, 'second']
  133. ];
  134. $name = 'second';
  135. $count = 0;
  136. for ($i = 0, $j = count($chunks); $i < $j; $i++) {
  137. $seconds = $chunks[$i][0];
  138. $name = $chunks[$i][1];
  139. if (($count = floor($time_diff / $seconds)) != 0) {
  140. break;
  141. }
  142. }
  143. return __("%d $name%s $tense", $count, ($count > 1 ? 's' : ''));
  144. }
  145. /**
  146. * 获取一个基于时间偏移的Unix时间戳
  147. *
  148. * @param string $type 时间类型,默认为day,可选minute,hour,day,week,month,quarter,year
  149. * @param int $offset 时间偏移量 默认为0,正数表示当前type之后,负数表示当前type之前
  150. * @param string $position 时间的开始或结束,默认为begin,可选前(begin,start,first,front),end
  151. * @param int $year 基准年,默认为null,即以当前年为基准
  152. * @param int $month 基准月,默认为null,即以当前月为基准
  153. * @param int $day 基准天,默认为null,即以当前天为基准
  154. * @param int $hour 基准小时,默认为null,即以当前年小时基准
  155. * @param int $minute 基准分钟,默认为null,即以当前分钟为基准
  156. * @return int 处理后的Unix时间戳
  157. */
  158. public static function unixtime(string $type = 'day', int $offset = 0, string $position = 'begin', int $year = null, int $month = null, int $day = null, int $hour = null, int $minute = null):int
  159. {
  160. $year = is_null($year) ? intval(date('Y')) : $year;
  161. $month = is_null($month) ? intval(date('m')) : $month;
  162. $day = is_null($day) ? intval(date('d')) : $day;
  163. $hour = is_null($hour) ? intval(date('H')) : $hour;
  164. $minute = is_null($minute) ? intval(date('i')) : $minute;
  165. $position = in_array($position, array('begin', 'start', 'first', 'front'));
  166. $baseTime = mktime(0, 0, 0, $month, $day, $year);
  167. switch ($type) {
  168. case 'minute':
  169. $time = $position ? mktime($hour, $minute + $offset, 0, $month, $day, $year) : mktime($hour, $minute + $offset, 59, $month, $day, $year);
  170. break;
  171. case 'hour':
  172. $time = $position ? mktime($hour + $offset, 0, 0, $month, $day, $year) : mktime($hour + $offset, 59, 59, $month, $day, $year);
  173. break;
  174. case 'day':
  175. $time = $position ? mktime(0, 0, 0, $month, $day + $offset, $year) : mktime(23, 59, 59, $month, $day + $offset, $year);
  176. break;
  177. case 'week':
  178. $weekIndex = date("w", $baseTime);
  179. $time = $position ?
  180. strtotime($offset . " weeks", strtotime(date('Y-m-d', strtotime("-" . ($weekIndex ? $weekIndex - 1 : 6) . " days", $baseTime)))) :
  181. strtotime($offset . " weeks", strtotime(date('Y-m-d 23:59:59', strtotime("+" . (6 - ($weekIndex ? $weekIndex - 1 : 6)) . " days", $baseTime))));
  182. break;
  183. case 'month':
  184. $_timestamp = mktime(0, 0, 0, $month + $offset, 1, $year);
  185. $time = $position ? $_timestamp : mktime(23, 59, 59, $month + $offset, self::days_in_month(intval(date("m", $_timestamp)), intval(date("Y", $_timestamp))), $year);
  186. break;
  187. case 'quarter':
  188. $_month = date("m", mktime(0, 0, 0, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, $day, $year));
  189. $time = $position ?
  190. mktime(0, 0, 0, 1 + ((ceil(date('n', $baseTime) / 3) + $offset) - 1) * 3, 1, $year) :
  191. mktime(23, 59, 59, (ceil(date('n', $baseTime) / 3) + $offset) * 3, self::days_in_month((ceil(date('n', $baseTime) / 3) + $offset) * 3, $year), $year);
  192. break;
  193. case 'year':
  194. $time = $position ? mktime(0, 0, 0, 1, 1, $year + $offset) : mktime(23, 59, 59, 12, 31, $year + $offset);
  195. break;
  196. default:
  197. $time = mktime($hour, $minute, 0, $month, $day, $year);
  198. break;
  199. }
  200. return $time;
  201. }
  202. /**
  203. * 获取指定年月拥有的天数
  204. * @param int $month
  205. * @param int $year
  206. * @return false|int|string
  207. */
  208. public static function days_in_month(int $month, int $year):mixed
  209. {
  210. if (function_exists("cal_days_in_month")) {
  211. return cal_days_in_month(CAL_GREGORIAN, $month, $year);
  212. } else {
  213. return date('t', mktime(0, 0, 0, $month, 1, $year));
  214. }
  215. }
  216. }