PageRenderTime 47ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 0ms

/src/I18n/Time.php

http://github.com/cakephp/cakephp
PHP | 350 lines | 138 code | 28 blank | 184 comment | 13 complexity | fe85a83379e494ece066169cf45c360a MD5 | raw file
Possible License(s): JSON
  1. <?php
  2. declare(strict_types=1);
  3. /**
  4. * CakePHP(tm) : Rapid Development Framework (https://cakephp.org)
  5. * Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  6. *
  7. * Licensed under The MIT License
  8. * For full copyright and license information, please see the LICENSE.txt
  9. * Redistributions of files must retain the above copyright notice.
  10. *
  11. * @copyright Copyright (c) Cake Software Foundation, Inc. (https://cakefoundation.org)
  12. * @link https://cakephp.org CakePHP(tm) Project
  13. * @since 3.0.0
  14. * @license https://opensource.org/licenses/mit-license.php MIT License
  15. */
  16. namespace Cake\I18n;
  17. use Cake\Chronos\MutableDateTime;
  18. use DateTimeInterface;
  19. use DateTimeZone;
  20. use IntlDateFormatter;
  21. /**
  22. * Extends the built-in DateTime class to provide handy methods and locale-aware
  23. * formatting helpers
  24. *
  25. * @deprecated 4.3.0 Use the immutable alternative `FrozenTime` instead.
  26. */
  27. class Time extends MutableDateTime implements I18nDateTimeInterface
  28. {
  29. use DateFormatTrait;
  30. /**
  31. * The format to use when formatting a time using `Cake\I18n\Time::i18nFormat()`
  32. * and `__toString`. This format is also used by `parseDateTime()`.
  33. *
  34. * The format should be either the formatting constants from IntlDateFormatter as
  35. * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern
  36. * as specified in (http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details)
  37. *
  38. * It is possible to provide an array of 2 constants. In this case, the first position
  39. * will be used for formatting the date part of the object and the second position
  40. * will be used to format the time part.
  41. *
  42. * @var array<int>|string|int
  43. * @see \Cake\I18n\Time::i18nFormat()
  44. */
  45. protected static $_toStringFormat = [IntlDateFormatter::SHORT, IntlDateFormatter::SHORT];
  46. /**
  47. * The format to use when converting this object to JSON.
  48. *
  49. * The format should be either the formatting constants from IntlDateFormatter as
  50. * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern
  51. * as specified in (http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details)
  52. *
  53. * It is possible to provide an array of 2 constants. In this case, the first position
  54. * will be used for formatting the date part of the object and the second position
  55. * will be used to format the time part.
  56. *
  57. * @var \Closure|array<int>|string|int
  58. * @see \Cake\I18n\Time::i18nFormat()
  59. */
  60. protected static $_jsonEncodeFormat = "yyyy-MM-dd'T'HH':'mm':'ssxxx";
  61. /**
  62. * The format to use when formatting a time using `Cake\I18n\Time::nice()`
  63. *
  64. * The format should be either the formatting constants from IntlDateFormatter as
  65. * described in (https://secure.php.net/manual/en/class.intldateformatter.php) or a pattern
  66. * as specified in (http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details)
  67. *
  68. * It is possible to provide an array of 2 constants. In this case, the first position
  69. * will be used for formatting the date part of the object and the second position
  70. * will be used to format the time part.
  71. *
  72. * @var array<int>|string|int
  73. * @see \Cake\I18n\Time::nice()
  74. */
  75. public static $niceFormat = [IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT];
  76. /**
  77. * The format to use when formatting a time using `Cake\I18n\Time::timeAgoInWords()`
  78. * and the difference is more than `Cake\I18n\Time::$wordEnd`
  79. *
  80. * @var array<int>|string|int
  81. * @see \Cake\I18n\Time::timeAgoInWords()
  82. */
  83. public static $wordFormat = [IntlDateFormatter::SHORT, IntlDateFormatter::NONE];
  84. /**
  85. * The format to use when formatting a time using `Time::timeAgoInWords()`
  86. * and the difference is less than `Time::$wordEnd`
  87. *
  88. * @var array<string>
  89. * @see \Cake\I18n\Time::timeAgoInWords()
  90. */
  91. public static $wordAccuracy = [
  92. 'year' => 'day',
  93. 'month' => 'day',
  94. 'week' => 'day',
  95. 'day' => 'hour',
  96. 'hour' => 'minute',
  97. 'minute' => 'minute',
  98. 'second' => 'second',
  99. ];
  100. /**
  101. * The end of relative time telling
  102. *
  103. * @var string
  104. * @see \Cake\I18n\Time::timeAgoInWords()
  105. */
  106. public static $wordEnd = '+1 month';
  107. /**
  108. * serialise the value as a Unix Timestamp
  109. *
  110. * @var string
  111. */
  112. public const UNIX_TIMESTAMP_FORMAT = 'unixTimestampFormat';
  113. /**
  114. * Create a new mutable time instance.
  115. *
  116. * @param \DateTimeInterface|string|int|null $time Fixed or relative time
  117. * @param \DateTimeZone|string|null $tz The timezone for the instance
  118. */
  119. public function __construct($time = null, $tz = null)
  120. {
  121. deprecationWarning(
  122. 'The `Time` class has been deprecated. Use the immutable alternative `FrozenTime` instead',
  123. 0
  124. );
  125. if ($time instanceof DateTimeInterface) {
  126. $tz = $time->getTimezone();
  127. $time = $time->format('Y-m-d H:i:s.u');
  128. }
  129. if (is_numeric($time)) {
  130. $time = '@' . $time;
  131. }
  132. parent::__construct($time, $tz);
  133. }
  134. /**
  135. * Returns a nicely formatted date string for this object.
  136. *
  137. * The format to be used is stored in the static property `Time::niceFormat`.
  138. *
  139. * @param \DateTimeZone|string|null $timezone Timezone string or DateTimeZone object
  140. * in which the date will be displayed. The timezone stored for this object will not
  141. * be changed.
  142. * @param string|null $locale The locale name in which the date should be displayed (e.g. pt-BR)
  143. * @return string Formatted date string
  144. */
  145. public function nice($timezone = null, $locale = null): string
  146. {
  147. return (string)$this->i18nFormat(static::$niceFormat, $timezone, $locale);
  148. }
  149. /**
  150. * Returns true if this object represents a date within the current week
  151. *
  152. * @return bool
  153. */
  154. public function isThisWeek(): bool
  155. {
  156. return static::now($this->getTimezone())->format('W o') === $this->format('W o');
  157. }
  158. /**
  159. * Returns true if this object represents a date within the current month
  160. *
  161. * @return bool
  162. */
  163. public function isThisMonth(): bool
  164. {
  165. return static::now($this->getTimezone())->format('m Y') === $this->format('m Y');
  166. }
  167. /**
  168. * Returns true if this object represents a date within the current year
  169. *
  170. * @return bool
  171. */
  172. public function isThisYear(): bool
  173. {
  174. return static::now($this->getTimezone())->format('Y') === $this->format('Y');
  175. }
  176. /**
  177. * Returns the quarter
  178. *
  179. * @param bool $range Range.
  180. * @return array<string>|int 1, 2, 3, or 4 quarter of year, or array if $range true
  181. */
  182. public function toQuarter(bool $range = false)
  183. {
  184. $quarter = (int)ceil((int)$this->format('m') / 3);
  185. if ($range === false) {
  186. return $quarter;
  187. }
  188. $year = $this->format('Y');
  189. switch ($quarter) {
  190. case 1:
  191. return [$year . '-01-01', $year . '-03-31'];
  192. case 2:
  193. return [$year . '-04-01', $year . '-06-30'];
  194. case 3:
  195. return [$year . '-07-01', $year . '-09-30'];
  196. }
  197. // 4th quarter
  198. return [$year . '-10-01', $year . '-12-31'];
  199. }
  200. /**
  201. * Returns a UNIX timestamp.
  202. *
  203. * @return string UNIX timestamp
  204. */
  205. public function toUnixString(): string
  206. {
  207. return $this->format('U');
  208. }
  209. /**
  210. * Returns either a relative or a formatted absolute date depending
  211. * on the difference between the current time and this object.
  212. *
  213. * ### Options:
  214. *
  215. * - `from` => another Time object representing the "now" time
  216. * - `format` => a fall back format if the relative time is longer than the duration specified by end
  217. * - `accuracy` => Specifies how accurate the date should be described (array)
  218. * - year => The format if years > 0 (default "day")
  219. * - month => The format if months > 0 (default "day")
  220. * - week => The format if weeks > 0 (default "day")
  221. * - day => The format if weeks > 0 (default "hour")
  222. * - hour => The format if hours > 0 (default "minute")
  223. * - minute => The format if minutes > 0 (default "minute")
  224. * - second => The format if seconds > 0 (default "second")
  225. * - `end` => The end of relative time telling
  226. * - `relativeString` => The `printf` compatible string when outputting relative time
  227. * - `absoluteString` => The `printf` compatible string when outputting absolute time
  228. * - `timezone` => The user timezone the timestamp should be formatted in.
  229. *
  230. * Relative dates look something like this:
  231. *
  232. * - 3 weeks, 4 days ago
  233. * - 15 seconds ago
  234. *
  235. * Default date formatting is d/M/YY e.g: on 18/2/09. Formatting is done internally using
  236. * `i18nFormat`, see the method for the valid formatting strings
  237. *
  238. * The returned string includes 'ago' or 'on' and assumes you'll properly add a word
  239. * like 'Posted ' before the function output.
  240. *
  241. * NOTE: If the difference is one week or more, the lowest level of accuracy is day
  242. *
  243. * @param array<string, mixed> $options Array of options.
  244. * @return string Relative time string.
  245. */
  246. public function timeAgoInWords(array $options = []): string
  247. {
  248. /** @psalm-suppress UndefinedInterfaceMethod */
  249. return static::getDiffFormatter()->timeAgoInWords($this, $options);
  250. }
  251. /**
  252. * Get list of timezone identifiers
  253. *
  254. * @param string|int|null $filter A regex to filter identifier
  255. * Or one of DateTimeZone class constants
  256. * @param string|null $country A two-letter ISO 3166-1 compatible country code.
  257. * This option is only used when $filter is set to DateTimeZone::PER_COUNTRY
  258. * @param array<string, mixed>|bool $options If true (default value) groups the identifiers list by primary region.
  259. * Otherwise, an array containing `group`, `abbr`, `before`, and `after`
  260. * keys. Setting `group` and `abbr` to true will group results and append
  261. * timezone abbreviation in the display value. Set `before` and `after`
  262. * to customize the abbreviation wrapper.
  263. * @return array List of timezone identifiers
  264. * @since 2.2
  265. */
  266. public static function listTimezones($filter = null, ?string $country = null, $options = []): array
  267. {
  268. if (is_bool($options)) {
  269. $options = [
  270. 'group' => $options,
  271. ];
  272. }
  273. $defaults = [
  274. 'group' => true,
  275. 'abbr' => false,
  276. 'before' => ' - ',
  277. 'after' => null,
  278. ];
  279. $options += $defaults;
  280. $group = $options['group'];
  281. $regex = null;
  282. if (is_string($filter)) {
  283. $regex = $filter;
  284. $filter = null;
  285. }
  286. if ($filter === null) {
  287. $filter = DateTimeZone::ALL;
  288. }
  289. $identifiers = DateTimeZone::listIdentifiers($filter, (string)$country) ?: [];
  290. if ($regex) {
  291. foreach ($identifiers as $key => $tz) {
  292. if (!preg_match($regex, $tz)) {
  293. unset($identifiers[$key]);
  294. }
  295. }
  296. }
  297. if ($group) {
  298. $groupedIdentifiers = [];
  299. $now = time();
  300. $before = $options['before'];
  301. $after = $options['after'];
  302. foreach ($identifiers as $tz) {
  303. $abbr = '';
  304. if ($options['abbr']) {
  305. $dateTimeZone = new DateTimeZone($tz);
  306. $trans = $dateTimeZone->getTransitions($now, $now);
  307. $abbr = isset($trans[0]['abbr']) ?
  308. $before . $trans[0]['abbr'] . $after :
  309. '';
  310. }
  311. $item = explode('/', $tz, 2);
  312. if (isset($item[1])) {
  313. $groupedIdentifiers[$item[0]][$tz] = $item[1] . $abbr;
  314. } else {
  315. $groupedIdentifiers[$item[0]] = [$tz => $item[0] . $abbr];
  316. }
  317. }
  318. return $groupedIdentifiers;
  319. }
  320. return array_combine($identifiers, $identifiers);
  321. }
  322. }