Date.php 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. <?php
  2. namespace ba;
  3. use DateTime;
  4. use Throwable;
  5. use DateTimeZone;
  6. use DateTimeInterface;
  7. /**
  8. * 日期时间处理类
  9. * @form https://gitee.com/karson/fastadmin/blob/develop/extend/fast/Date.php
  10. */
  11. class Date
  12. {
  13. private const YEAR = 31536000;
  14. private const MONTH = 2592000;
  15. private const WEEK = 604800;
  16. private const DAY = 86400;
  17. private const HOUR = 3600;
  18. private const MINUTE = 60;
  19. /**
  20. * 计算两个时区间相差的时长,单位为秒
  21. *
  22. * [!!] A list of time zones that PHP supports can be found at
  23. * <http://php.net/timezones>.
  24. * @param string $remote timezone that to find the offset of
  25. * @param string|null $local timezone used as the baseline
  26. * @param string|int|null $now UNIX timestamp or date string
  27. * @return int
  28. * @throws Throwable
  29. * @example $seconds = self::offset('America/Chicago', 'GMT');
  30. */
  31. public static function offset(string $remote, string $local = null, string|int $now = null): int
  32. {
  33. if ($local === null) {
  34. // Use the default timezone
  35. $local = date_default_timezone_get();
  36. }
  37. if (is_int($now)) {
  38. // Convert the timestamp into a string
  39. $now = date(DateTimeInterface::RFC2822, $now);
  40. }
  41. // Create timezone objects
  42. $zone_remote = new DateTimeZone($remote);
  43. $zone_local = new DateTimeZone($local);
  44. // Create date objects from timezones
  45. $time_remote = new DateTime($now, $zone_remote);
  46. $time_local = new DateTime($now, $zone_local);
  47. // Find the offset
  48. return $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
  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|null $local timestamp to use as the baseline
  58. * @param string $output formatting string
  59. * @return bool|array|string associative list of all outputs requested|when only a single output is requested
  60. * @from https://github.com/kohana/ohanzee-helpers/blob/master/src/Date.php
  61. */
  62. public static function span(int $remote, int $local = null, string $output = 'years,months,weeks,days,hours,minutes,seconds'): bool|array|string
  63. {
  64. // Normalize output
  65. $output = trim(strtolower($output));
  66. if (!$output) {
  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. // Calculate the span from the current time
  78. $local = time();
  79. }
  80. // Calculate timespan (seconds)
  81. $timespan = abs($remote - $local);
  82. if (isset($output['years'])) {
  83. $timespan -= self::YEAR * ($output['years'] = (int)floor($timespan / self::YEAR));
  84. }
  85. if (isset($output['months'])) {
  86. $timespan -= self::MONTH * ($output['months'] = (int)floor($timespan / self::MONTH));
  87. }
  88. if (isset($output['weeks'])) {
  89. $timespan -= self::WEEK * ($output['weeks'] = (int)floor($timespan / self::WEEK));
  90. }
  91. if (isset($output['days'])) {
  92. $timespan -= self::DAY * ($output['days'] = (int)floor($timespan / self::DAY));
  93. }
  94. if (isset($output['hours'])) {
  95. $timespan -= self::HOUR * ($output['hours'] = (int)floor($timespan / self::HOUR));
  96. }
  97. if (isset($output['minutes'])) {
  98. $timespan -= self::MINUTE * ($output['minutes'] = (int)floor($timespan / self::MINUTE));
  99. }
  100. // Seconds ago, 1
  101. if (isset($output['seconds'])) {
  102. $output['seconds'] = $timespan;
  103. }
  104. if (count($output) === 1) {
  105. // Only a single output was requested, return it
  106. return array_pop($output);
  107. }
  108. // Return array
  109. return $output;
  110. }
  111. /**
  112. * 格式化 UNIX 时间戳为人易读的字符串
  113. *
  114. * @param int $remote Unix 时间戳
  115. * @param ?int $local 本地时间
  116. * @return string 格式化的日期字符串
  117. */
  118. public static function human(int $remote, ?int $local = null): string
  119. {
  120. $timeDiff = (is_null($local) ? time() : $local) - $remote;
  121. $chunks = [
  122. [60 * 60 * 24 * 365, 'year'],
  123. [60 * 60 * 24 * 30, 'month'],
  124. [60 * 60 * 24 * 7, 'week'],
  125. [60 * 60 * 24, 'day'],
  126. [60 * 60, 'hour'],
  127. [60, 'minute'],
  128. [1, 'second'],
  129. ];
  130. $count = 0;
  131. $name = '';
  132. for ($i = 0, $j = count($chunks); $i < $j; $i++) {
  133. $seconds = $chunks[$i][0];
  134. $name = $chunks[$i][1];
  135. if (($count = floor($timeDiff / $seconds)) != 0) {
  136. break;
  137. }
  138. }
  139. return __("%d $name%s ago", [$count, $count > 1 ? 's' : '']);
  140. }
  141. /**
  142. * 获取一个基于时间偏移的Unix时间戳
  143. *
  144. * @param string $type 时间类型,默认为day,可选minute,hour,day,week,month,quarter,year
  145. * @param int $offset 时间偏移量 默认为0,正数表示当前type之后,负数表示当前type之前
  146. * @param string $position 时间的开始或结束,默认为begin,可选前(begin,start,first,front),end
  147. * @param int|null $year 基准年,默认为null,即以当前年为基准
  148. * @param int|null $month 基准月,默认为null,即以当前月为基准
  149. * @param int|null $day 基准天,默认为null,即以当前天为基准
  150. * @param int|null $hour 基准小时,默认为null,即以当前年小时基准
  151. * @param int|null $minute 基准分钟,默认为null,即以当前分钟为基准
  152. * @return int 处理后的Unix时间戳
  153. */
  154. 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
  155. {
  156. $year = is_null($year) ? date('Y') : $year;
  157. $month = is_null($month) ? date('m') : $month;
  158. $day = is_null($day) ? date('d') : $day;
  159. $hour = is_null($hour) ? date('H') : $hour;
  160. $minute = is_null($minute) ? date('i') : $minute;
  161. $position = in_array($position, ['begin', 'start', 'first', 'front']);
  162. return match ($type) {
  163. 'minute' => $position ? mktime($hour, $minute + $offset, 0, $month, $day, $year) : mktime($hour, $minute + $offset, 59, $month, $day, $year),
  164. 'hour' => $position ? mktime($hour + $offset, 0, 0, $month, $day, $year) : mktime($hour + $offset, 59, 59, $month, $day, $year),
  165. 'day' => $position ? mktime(0, 0, 0, $month, $day + $offset, $year) : mktime(23, 59, 59, $month, $day + $offset, $year),
  166. 'week' => $position ?
  167. mktime(0, 0, 0, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 1 - 7 * (-$offset), $year) :
  168. mktime(23, 59, 59, $month, $day - date("w", mktime(0, 0, 0, $month, $day, $year)) + 7 - 7 * (-$offset), $year),
  169. 'month' => $position ? mktime(0, 0, 0, $month + $offset, 1, $year) : mktime(23, 59, 59, $month + $offset, cal_days_in_month(CAL_GREGORIAN, $month + $offset, $year), $year),
  170. 'quarter' => $position ?
  171. mktime(0, 0, 0, 1 + ((ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) - 1) * 3, 1, $year) :
  172. mktime(23, 59, 59, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, cal_days_in_month(CAL_GREGORIAN, (ceil(date('n', mktime(0, 0, 0, $month, $day, $year)) / 3) + $offset) * 3, $year), $year),
  173. 'year' => $position ? mktime(0, 0, 0, 1, 1, $year + $offset) : mktime(23, 59, 59, 12, 31, $year + $offset),
  174. default => mktime($hour, $minute, 0, $month, $day, $year),
  175. };
  176. }
  177. }