PageRenderTime 58ms CodeModel.GetById 34ms RepoModel.GetById 0ms app.codeStats 1ms

/src/I18n/Time.php

https://github.com/ceeram/cakephp
PHP | 803 lines | 384 code | 75 blank | 344 comment | 69 complexity | a36788e0021cfce665c20dd5a2e2fe53 MD5 | raw file
  1. <?php
  2. /**
  3. * CakePHP(tm) : Rapid Development Framework (http://cakephp.org)
  4. * Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  5. *
  6. * Licensed under The MIT License
  7. * For full copyright and license information, please see the LICENSE.txt
  8. * Redistributions of files must retain the above copyright notice.
  9. *
  10. * @copyright Copyright (c) Cake Software Foundation, Inc. (http://cakefoundation.org)
  11. * @link http://cakephp.org CakePHP(tm) Project
  12. * @since 3.0.0
  13. * @license http://www.opensource.org/licenses/mit-license.php MIT License
  14. */
  15. namespace Cake\I18n;
  16. use Carbon\Carbon;
  17. use IntlDateFormatter;
  18. use JsonSerializable;
  19. /**
  20. * Extends the built-in DateTime class to provide handy methods and locale-aware
  21. * formatting helpers
  22. *
  23. */
  24. class Time extends Carbon implements JsonSerializable
  25. {
  26. /**
  27. * The format to use when formatting a time using `Cake\I18n\Time::i18nFormat()`
  28. * and `__toString`
  29. *
  30. * The format should be either the formatting constants from IntlDateFormatter as
  31. * described in (http://www.php.net/manual/en/class.intldateformatter.php) or a pattern
  32. * as specified in (http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details)
  33. *
  34. * It is possible to provide an array of 2 constants. In this case, the first position
  35. * will be used for formatting the date part of the object and the second position
  36. * will be used to format the time part.
  37. *
  38. * @var mixed
  39. * @see \Cake\I18n\Time::i18nFormat()
  40. */
  41. protected static $_toStringFormat = [IntlDateFormatter::SHORT, IntlDateFormatter::SHORT];
  42. /**
  43. * The format to use when formatting a time using `Cake\I18n\Time::nice()`
  44. *
  45. * The format should be either the formatting constants from IntlDateFormatter as
  46. * described in (http://www.php.net/manual/en/class.intldateformatter.php) or a pattern
  47. * as specified in (http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details)
  48. *
  49. * It is possible to provide an array of 2 constants. In this case, the first position
  50. * will be used for formatting the date part of the object and the second position
  51. * will be used to format the time part.
  52. *
  53. * @var mixed
  54. * @see \Cake\I18n\Time::nice()
  55. */
  56. public static $niceFormat = [IntlDateFormatter::MEDIUM, IntlDateFormatter::SHORT];
  57. /**
  58. * The default locale to be used for displaying formatted date strings.
  59. *
  60. * @var string
  61. */
  62. public static $defaultLocale;
  63. /**
  64. * The format to use when formatting a time using `Cake\I18n\Time::timeAgoInWords()`
  65. * and the difference is more than `Cake\I18n\Time::$wordEnd`
  66. *
  67. * @var string
  68. * @see \Cake\I18n\Time::timeAgoInWords()
  69. */
  70. public static $wordFormat = [IntlDateFormatter::SHORT, -1];
  71. /**
  72. * The format to use when formatting a time using `Time::timeAgoInWords()`
  73. * and the difference is less than `Time::$wordEnd`
  74. *
  75. * @var array
  76. * @see \Cake\I18n\Time::timeAgoInWords()
  77. */
  78. public static $wordAccuracy = [
  79. 'year' => "day",
  80. 'month' => "day",
  81. 'week' => "day",
  82. 'day' => "hour",
  83. 'hour' => "minute",
  84. 'minute' => "minute",
  85. 'second' => "second",
  86. ];
  87. /**
  88. * The end of relative time telling
  89. *
  90. * @var string
  91. * @see \Cake\I18n\Time::timeAgoInWords()
  92. */
  93. public static $wordEnd = '+1 month';
  94. /**
  95. * In-memory cache of date formatters
  96. *
  97. * @var array
  98. */
  99. protected static $_formatters = [];
  100. /**
  101. * {@inheritDoc}
  102. */
  103. public function __construct($time = null, $tz = null)
  104. {
  105. if ($time instanceof \DateTime) {
  106. $tz = $time->getTimeZone();
  107. $time = $time->format('Y-m-d H:i:s');
  108. }
  109. if (is_numeric($time)) {
  110. $time = '@' . $time;
  111. }
  112. parent::__construct($time, $tz);
  113. }
  114. /**
  115. * Returns a nicely formatted date string for this object.
  116. *
  117. * The format to be used is stored in the static property `Time::niceFormat`.
  118. *
  119. * @param string|\DateTimeZone|null $timezone Timezone string or DateTimeZone object
  120. * in which the date will be displayed. The timezone stored for this object will not
  121. * be changed.
  122. * @param string|null $locale The locale name in which the date should be displayed (e.g. pt-BR)
  123. * @return string Formatted date string
  124. */
  125. public function nice($timezone = null, $locale = null)
  126. {
  127. return $this->i18nFormat(static::$niceFormat, $timezone, $locale);
  128. }
  129. /**
  130. * Returns true if this object represents a date within the current week
  131. *
  132. * @return bool
  133. */
  134. public function isThisWeek()
  135. {
  136. return static::now($this->getTimezone())->format('W o') == $this->format('W o');
  137. }
  138. /**
  139. * Returns true if this object represents a date within the current month
  140. *
  141. * @return bool
  142. */
  143. public function isThisMonth()
  144. {
  145. return static::now($this->getTimezone())->format('m Y') == $this->format('m Y');
  146. }
  147. /**
  148. * Returns true if this object represents a date within the current year
  149. *
  150. * @return bool
  151. */
  152. public function isThisYear()
  153. {
  154. return static::now($this->getTimezone())->format('Y') == $this->format('Y');
  155. }
  156. /**
  157. * Returns the quarter
  158. *
  159. * @param bool $range Range.
  160. * @return mixed 1, 2, 3, or 4 quarter of year or array if $range true
  161. */
  162. public function toQuarter($range = false)
  163. {
  164. $quarter = ceil($this->format('m') / 3);
  165. if ($range === false) {
  166. return $quarter;
  167. }
  168. $year = $this->format('Y');
  169. switch ($quarter) {
  170. case 1:
  171. return [$year . '-01-01', $year . '-03-31'];
  172. case 2:
  173. return [$year . '-04-01', $year . '-06-30'];
  174. case 3:
  175. return [$year . '-07-01', $year . '-09-30'];
  176. case 4:
  177. return [$year . '-10-01', $year . '-12-31'];
  178. }
  179. }
  180. /**
  181. * Returns a UNIX timestamp.
  182. *
  183. * @return string UNIX timestamp
  184. */
  185. public function toUnixString()
  186. {
  187. return $this->format('U');
  188. }
  189. /**
  190. * Returns either a relative or a formatted absolute date depending
  191. * on the difference between the current time and this object.
  192. *
  193. * ### Options:
  194. *
  195. * - `from` => another Time object representing the "now" time
  196. * - `format` => a fall back format if the relative time is longer than the duration specified by end
  197. * - `accuracy` => Specifies how accurate the date should be described (array)
  198. * - year => The format if years > 0 (default "day")
  199. * - month => The format if months > 0 (default "day")
  200. * - week => The format if weeks > 0 (default "day")
  201. * - day => The format if weeks > 0 (default "hour")
  202. * - hour => The format if hours > 0 (default "minute")
  203. * - minute => The format if minutes > 0 (default "minute")
  204. * - second => The format if seconds > 0 (default "second")
  205. * - `end` => The end of relative time telling
  206. * - `relativeString` => The printf compatible string when outputting relative time
  207. * - `absoluteString` => The printf compatible string when outputting absolute time
  208. * - `timezone` => The user timezone the timestamp should be formatted in.
  209. *
  210. * Relative dates look something like this:
  211. *
  212. * - 3 weeks, 4 days ago
  213. * - 15 seconds ago
  214. *
  215. * Default date formatting is d/M/YY e.g: on 18/2/09. Formatting is done internally using
  216. * `i18nFormat`, see the method for the valid formatting strings
  217. *
  218. * The returned string includes 'ago' or 'on' and assumes you'll properly add a word
  219. * like 'Posted ' before the function output.
  220. *
  221. * NOTE: If the difference is one week or more, the lowest level of accuracy is day
  222. *
  223. * @param array $options Array of options.
  224. * @return string Relative time string.
  225. */
  226. public function timeAgoInWords(array $options = [])
  227. {
  228. $timezone = null;
  229. $format = static::$wordFormat;
  230. $end = static::$wordEnd;
  231. $relativeString = __d('cake', '%s ago');
  232. $absoluteString = __d('cake', 'on %s');
  233. $accuracy = static::$wordAccuracy;
  234. $from = static::now();
  235. $opts = ['timezone', 'format', 'end', 'relativeString', 'absoluteString', 'from'];
  236. foreach ($opts as $option) {
  237. if (isset($options[$option])) {
  238. ${$option} = $options[$option];
  239. unset($options[$option]);
  240. }
  241. }
  242. if (isset($options['accuracy'])) {
  243. if (is_array($options['accuracy'])) {
  244. $accuracy = $options['accuracy'] + $accuracy;
  245. } else {
  246. foreach ($accuracy as $key => $level) {
  247. $accuracy[$key] = $options['accuracy'];
  248. }
  249. }
  250. }
  251. $now = $from->format('U');
  252. $inSeconds = $this->format('U');
  253. $backwards = ($inSeconds > $now);
  254. $futureTime = $now;
  255. $pastTime = $inSeconds;
  256. if ($backwards) {
  257. $futureTime = $inSeconds;
  258. $pastTime = $now;
  259. }
  260. $diff = $futureTime - $pastTime;
  261. if (!$diff) {
  262. return __d('cake', 'just now', 'just now');
  263. }
  264. if ($diff > abs($now - (new static($end))->format('U'))) {
  265. return sprintf($absoluteString, $this->i18nFormat($format));
  266. }
  267. // If more than a week, then take into account the length of months
  268. if ($diff >= 604800) {
  269. list($future['H'], $future['i'], $future['s'], $future['d'], $future['m'], $future['Y']) = explode('/', date('H/i/s/d/m/Y', $futureTime));
  270. list($past['H'], $past['i'], $past['s'], $past['d'], $past['m'], $past['Y']) = explode('/', date('H/i/s/d/m/Y', $pastTime));
  271. $weeks = $days = $hours = $minutes = $seconds = 0;
  272. $years = $future['Y'] - $past['Y'];
  273. $months = $future['m'] + ((12 * $years) - $past['m']);
  274. if ($months >= 12) {
  275. $years = floor($months / 12);
  276. $months = $months - ($years * 12);
  277. }
  278. if ($future['m'] < $past['m'] && $future['Y'] - $past['Y'] === 1) {
  279. $years--;
  280. }
  281. if ($future['d'] >= $past['d']) {
  282. $days = $future['d'] - $past['d'];
  283. } else {
  284. $daysInPastMonth = date('t', $pastTime);
  285. $daysInFutureMonth = date('t', mktime(0, 0, 0, $future['m'] - 1, 1, $future['Y']));
  286. if (!$backwards) {
  287. $days = ($daysInPastMonth - $past['d']) + $future['d'];
  288. } else {
  289. $days = ($daysInFutureMonth - $past['d']) + $future['d'];
  290. }
  291. if ($future['m'] != $past['m']) {
  292. $months--;
  293. }
  294. }
  295. if (!$months && $years >= 1 && $diff < ($years * 31536000)) {
  296. $months = 11;
  297. $years--;
  298. }
  299. if ($months >= 12) {
  300. $years = $years + 1;
  301. $months = $months - 12;
  302. }
  303. if ($days >= 7) {
  304. $weeks = floor($days / 7);
  305. $days = $days - ($weeks * 7);
  306. }
  307. } else {
  308. $years = $months = $weeks = 0;
  309. $days = floor($diff / 86400);
  310. $diff = $diff - ($days * 86400);
  311. $hours = floor($diff / 3600);
  312. $diff = $diff - ($hours * 3600);
  313. $minutes = floor($diff / 60);
  314. $diff = $diff - ($minutes * 60);
  315. $seconds = $diff;
  316. }
  317. $fWord = $accuracy['second'];
  318. if ($years > 0) {
  319. $fWord = $accuracy['year'];
  320. } elseif (abs($months) > 0) {
  321. $fWord = $accuracy['month'];
  322. } elseif (abs($weeks) > 0) {
  323. $fWord = $accuracy['week'];
  324. } elseif (abs($days) > 0) {
  325. $fWord = $accuracy['day'];
  326. } elseif (abs($hours) > 0) {
  327. $fWord = $accuracy['hour'];
  328. } elseif (abs($minutes) > 0) {
  329. $fWord = $accuracy['minute'];
  330. }
  331. $fNum = str_replace(['year', 'month', 'week', 'day', 'hour', 'minute', 'second'], [1, 2, 3, 4, 5, 6, 7], $fWord);
  332. $relativeDate = '';
  333. if ($fNum >= 1 && $years > 0) {
  334. $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '{0} year', '{0} years', $years, $years);
  335. }
  336. if ($fNum >= 2 && $months > 0) {
  337. $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '{0} month', '{0} months', $months, $months);
  338. }
  339. if ($fNum >= 3 && $weeks > 0) {
  340. $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '{0} week', '{0} weeks', $weeks, $weeks);
  341. }
  342. if ($fNum >= 4 && $days > 0) {
  343. $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '{0} day', '{0} days', $days, $days);
  344. }
  345. if ($fNum >= 5 && $hours > 0) {
  346. $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '{0} hour', '{0} hours', $hours, $hours);
  347. }
  348. if ($fNum >= 6 && $minutes > 0) {
  349. $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '{0} minute', '{0} minutes', $minutes, $minutes);
  350. }
  351. if ($fNum >= 7 && $seconds > 0) {
  352. $relativeDate .= ($relativeDate ? ', ' : '') . __dn('cake', '{0} second', '{0} seconds', $seconds, $seconds);
  353. }
  354. // When time has passed
  355. if (!$backwards && $relativeDate) {
  356. return sprintf($relativeString, $relativeDate);
  357. }
  358. if (!$backwards) {
  359. $aboutAgo = [
  360. 'second' => __d('cake', 'about a second ago'),
  361. 'minute' => __d('cake', 'about a minute ago'),
  362. 'hour' => __d('cake', 'about an hour ago'),
  363. 'day' => __d('cake', 'about a day ago'),
  364. 'week' => __d('cake', 'about a week ago'),
  365. 'year' => __d('cake', 'about a year ago')
  366. ];
  367. return $aboutAgo[$fWord];
  368. }
  369. // When time is to come
  370. if (!$relativeDate) {
  371. $aboutIn = [
  372. 'second' => __d('cake', 'in about a second'),
  373. 'minute' => __d('cake', 'in about a minute'),
  374. 'hour' => __d('cake', 'in about an hour'),
  375. 'day' => __d('cake', 'in about a day'),
  376. 'week' => __d('cake', 'in about a week'),
  377. 'year' => __d('cake', 'in about a year')
  378. ];
  379. return $aboutIn[$fWord];
  380. }
  381. return $relativeDate;
  382. }
  383. /**
  384. * Returns the difference between this date and the provided one in a human
  385. * readable format.
  386. *
  387. * See `Time::timeAgoInWords()` for a full list of options that can be passed
  388. * to this method.
  389. *
  390. * @param \Carbon\Carbon|null $other the date to diff with
  391. * @param array $options options accepted by timeAgoInWords
  392. * @return string
  393. * @see Time::timeAgoInWords()
  394. */
  395. public function diffForHumans(Carbon $other = null, array $options = [])
  396. {
  397. $options = ['from' => $other] + $options;
  398. return $this->timeAgoInWords($options);
  399. }
  400. /**
  401. * Returns true this instance happened within the specified interval
  402. *
  403. * @param string|int $timeInterval the numeric value with space then time type.
  404. * Example of valid types: 6 hours, 2 days, 1 minute.
  405. * @return bool
  406. */
  407. public function wasWithinLast($timeInterval)
  408. {
  409. $tmp = str_replace(' ', '', $timeInterval);
  410. if (is_numeric($tmp)) {
  411. $timeInterval = $tmp . ' days';
  412. }
  413. $interval = new static('-' . $timeInterval);
  414. $now = new static();
  415. return $this >= $interval && $this <= $now;
  416. }
  417. /**
  418. * Returns true this instance will happen within the specified interval
  419. *
  420. * @param string|int $timeInterval the numeric value with space then time type.
  421. * Example of valid types: 6 hours, 2 days, 1 minute.
  422. * @return bool
  423. */
  424. public function isWithinNext($timeInterval)
  425. {
  426. $tmp = str_replace(' ', '', $timeInterval);
  427. if (is_numeric($tmp)) {
  428. $timeInterval = $tmp . ' days';
  429. }
  430. $interval = new static('+' . $timeInterval);
  431. $now = new static();
  432. return $this <= $interval && $this >= $now;
  433. }
  434. /**
  435. * Returns a formatted string for this time object using the preferred format and
  436. * language for the specified locale.
  437. *
  438. * It is possible to specify the desired format for the string to be displayed.
  439. * You can either pass `IntlDateFormatter` constants as the first argument of this
  440. * function, or pass a full ICU date formatting string as specified in the following
  441. * resource: http://www.icu-project.org/apiref/icu4c/classSimpleDateFormat.html#details.
  442. *
  443. * ### Examples
  444. *
  445. * ```
  446. * $time = new Time('2014-04-20 22:10');
  447. * $time->i18nFormat(); // outputs '4/20/14, 10:10 PM' for the en-US locale
  448. * $time->i18nFormat(\IntlDateFormatter::FULL); // Use the full date and time format
  449. * $time->i18nFormat([\IntlDateFormatter::FULL, \IntlDateFormatter::SHORT]); // Use full date but short time format
  450. * $time->i18nFormat('yyyy-MM-dd HH:mm:ss'); // outputs '2014-04-20 22:10'
  451. * ```
  452. *
  453. * If you wish to control the default format to be used for this method, you can alter
  454. * the value of the static `Time::$defaultLocale` variable and set it to one of the
  455. * possible formats accepted by this function.
  456. *
  457. * You can read about the available IntlDateFormatter constants at
  458. * http://www.php.net/manual/en/class.intldateformatter.php
  459. *
  460. * If you need to display the date in a different timezone than the one being used for
  461. * this Time object without altering its internal state, you can pass a timezone
  462. * string or object as the second parameter.
  463. *
  464. * Finally, should you need to use a different locale for displaying this time object,
  465. * pass a locale string as the third parameter to this function.
  466. *
  467. * ### Examples
  468. *
  469. * ```
  470. * $time = new Time('2014-04-20 22:10');
  471. * $time->i18nFormat(null, null, 'de-DE');
  472. * $time->i18nFormat(\IntlDateFormatter::FULL, 'Europe/Berlin', 'de-DE');
  473. * ```
  474. *
  475. * You can control the default locale to be used by setting the static variable
  476. * `Time::$defaultLocale` to a valid locale string. If empty, the default will be
  477. * taken from the `intl.default_locale` ini config.
  478. *
  479. * @param string|int|null $format Format string.
  480. * @param string|\DateTimeZone|null $timezone Timezone string or DateTimeZone object
  481. * in which the date will be displayed. The timezone stored for this object will not
  482. * be changed.
  483. * @param string|null $locale The locale name in which the date should be displayed (e.g. pt-BR)
  484. * @return string Formatted and translated date string
  485. */
  486. public function i18nFormat($format = null, $timezone = null, $locale = null)
  487. {
  488. $time = $this;
  489. if ($timezone) {
  490. $time = clone $this;
  491. $time->timezone($timezone);
  492. }
  493. $format = $format !== null ? $format : static::$_toStringFormat;
  494. $locale = $locale ?: static::$defaultLocale;
  495. return $this->_formatObject($time, $format, $locale);
  496. }
  497. /**
  498. * Returns a translated and localized date string.
  499. * Implements what IntlDateFormatter::formatObject() is in PHP 5.5+
  500. *
  501. * @param \DateTime $date Date.
  502. * @param string|int|array $format Format.
  503. * @param string $locale The locale name in which the date should be displayed.
  504. * @return string
  505. */
  506. protected function _formatObject($date, $format, $locale)
  507. {
  508. $pattern = $dateFormat = $timeFormat = $calendar = null;
  509. if (is_array($format)) {
  510. list($dateFormat, $timeFormat) = $format;
  511. } elseif (is_numeric($format)) {
  512. $dateFormat = $format;
  513. } else {
  514. $dateFormat = $timeFormat = IntlDateFormatter::FULL;
  515. $pattern = $format;
  516. }
  517. $timezone = $date->getTimezone()->getName();
  518. $key = "{$locale}.{$dateFormat}.{$timeFormat}.{$timezone}.{$calendar}.{$pattern}";
  519. if (!isset(static::$_formatters[$key])) {
  520. static::$_formatters[$key] = datefmt_create(
  521. $locale,
  522. $dateFormat,
  523. $timeFormat,
  524. $timezone === '+00:00' ? 'UTC' : $timezone,
  525. $calendar,
  526. $pattern
  527. );
  528. }
  529. return static::$_formatters[$key]->format($date);
  530. }
  531. /**
  532. * {@inheritDoc}
  533. */
  534. public function __toString()
  535. {
  536. return $this->i18nFormat();
  537. }
  538. /**
  539. * Get list of timezone identifiers
  540. *
  541. * @param int|string $filter A regex to filter identifier
  542. * Or one of DateTimeZone class constants
  543. * @param string $country A two-letter ISO 3166-1 compatible country code.
  544. * This option is only used when $filter is set to DateTimeZone::PER_COUNTRY
  545. * @param bool $group If true (default value) groups the identifiers list by primary region
  546. * @return array List of timezone identifiers
  547. * @since 2.2
  548. */
  549. public static function listTimezones($filter = null, $country = null, $group = true)
  550. {
  551. $regex = null;
  552. if (is_string($filter)) {
  553. $regex = $filter;
  554. $filter = null;
  555. }
  556. if ($filter === null) {
  557. $filter = \DateTimeZone::ALL;
  558. }
  559. $identifiers = \DateTimeZone::listIdentifiers($filter, $country);
  560. if ($regex) {
  561. foreach ($identifiers as $key => $tz) {
  562. if (!preg_match($regex, $tz)) {
  563. unset($identifiers[$key]);
  564. }
  565. }
  566. }
  567. if ($group) {
  568. $groupedIdentifiers = [];
  569. foreach ($identifiers as $key => $tz) {
  570. $item = explode('/', $tz, 2);
  571. if (isset($item[1])) {
  572. $groupedIdentifiers[$item[0]][$tz] = $item[1];
  573. } else {
  574. $groupedIdentifiers[$item[0]] = [$tz => $item[0]];
  575. }
  576. }
  577. return $groupedIdentifiers;
  578. }
  579. return array_combine($identifiers, $identifiers);
  580. }
  581. /**
  582. * Resets the format used to the default when converting an instance of this type to
  583. * a string
  584. *
  585. * @return void
  586. */
  587. public static function resetToStringFormat()
  588. {
  589. static::setToStringFormat([IntlDateFormatter::SHORT, IntlDateFormatter::SHORT]);
  590. }
  591. /**
  592. * Sets the default format used when type converting instances of this type to string
  593. *
  594. * @param string|array|int $format Format.
  595. * @return void
  596. */
  597. public static function setToStringFormat($format)
  598. {
  599. static::$_toStringFormat = $format;
  600. }
  601. /**
  602. * Returns a new Time object after parsing the provided time string based on
  603. * the passed or configured date time format. This method is locale dependent,
  604. * Any string that is passed to this function will be interpreted as a locale
  605. * dependent string.
  606. *
  607. * When no $format is provided, the `toString` format will be used.
  608. *
  609. * If it was impossible to parse the provided time, null will be returned.
  610. *
  611. * Example:
  612. *
  613. * ```
  614. * $time = Time::parseDateTime('10/13/2013 12:54am');
  615. * $time = Time::parseDateTime('13 Oct, 2013 13:54', 'dd MMM, y H:mm');
  616. * $time = Time::parseDateTime('10/10/2015', [IntlDateFormatter::SHORT, -1]);
  617. * ```
  618. *
  619. * @param string $time The time string to parse.
  620. * @param string|array $format Any format accepted by IntlDateFormatter.
  621. * @return static|null
  622. */
  623. public static function parseDateTime($time, $format = null)
  624. {
  625. $dateFormat = $format ?: static::$_toStringFormat;
  626. $timeFormat = $pattern = null;
  627. if (is_array($dateFormat)) {
  628. list($newDateFormat, $timeFormat) = $dateFormat;
  629. $dateFormat = $newDateFormat;
  630. } else {
  631. $pattern = $dateFormat;
  632. $dateFormat = null;
  633. }
  634. $formatter = datefmt_create(
  635. static::$defaultLocale,
  636. $dateFormat,
  637. $timeFormat,
  638. null,
  639. null,
  640. $pattern
  641. );
  642. $time = $formatter->parse($time);
  643. if ($time) {
  644. return new static('@' . $time);
  645. }
  646. return null;
  647. }
  648. /**
  649. * Returns a new Time object after parsing the provided $date string based on
  650. * the passed or configured date time format. This method is locale dependent,
  651. * Any string that is passed to this function will be interpreted as a locale
  652. * dependent string.
  653. *
  654. * When no $format is provided, the `wordFormat` format will be used.
  655. *
  656. * If it was impossible to parse the provided time, null will be returned.
  657. *
  658. * Example:
  659. *
  660. * ```
  661. * $time = Time::parseDate('10/13/2013');
  662. * $time = Time::parseDate('13 Oct, 2013', 'dd MMM, y');
  663. * $time = Time::parseDate('13 Oct, 2013', IntlDateFormatter::SHORT);
  664. * ```
  665. *
  666. * @param string $date The date string to parse.
  667. * @param string|int $format Any format accepted by IntlDateFormatter.
  668. * @return static|null
  669. */
  670. public static function parseDate($date, $format = null)
  671. {
  672. if (is_int($format)) {
  673. $format = [$format, -1];
  674. }
  675. $format = $format ?: static::$wordFormat;
  676. return static::parseDateTime($date, $format);
  677. }
  678. /**
  679. * Returns a new Time object after parsing the provided $time string based on
  680. * the passed or configured date time format. This method is locale dependent,
  681. * Any string that is passed to this function will be interpreted as a locale
  682. * dependent string.
  683. *
  684. * When no $format is provided, the IntlDateFormatter::SHORT format will be used.
  685. *
  686. * If it was impossible to parse the provided time, null will be returned.
  687. *
  688. * Example:
  689. *
  690. * ```
  691. * $time = Time::parseDate('11:23pm');
  692. * ```
  693. *
  694. * @param string $time The time string to parse.
  695. * @param string|int $format Any format accepted by IntlDateFormatter.
  696. * @return static|null
  697. */
  698. public static function parseTime($time, $format = null)
  699. {
  700. if (is_int($format)) {
  701. $format = [-1, $format];
  702. }
  703. $format = $format ?: [-1, IntlDateFormatter::SHORT];
  704. return static::parseDateTime($time, $format);
  705. }
  706. /**
  707. * Returns a string that should be serialized when converting this object to json
  708. *
  709. * @return string
  710. */
  711. public function jsonSerialize()
  712. {
  713. return $this->format(static::ISO8601);
  714. }
  715. /**
  716. * Returns the data that should be displayed when debugging this object
  717. *
  718. * @return array
  719. */
  720. public function __debugInfo()
  721. {
  722. return [
  723. 'time' => $this->format(static::ISO8601),
  724. 'timezone' => $this->getTimezone()->getName(),
  725. 'fixedNowTime' => $this->hasTestNow() ? $this->getTestNow()->format(static::ISO8601) : false
  726. ];
  727. }
  728. }