PageRenderTime 54ms CodeModel.GetById 24ms RepoModel.GetById 1ms app.codeStats 0ms

/system/classes/kohana/date.php

https://bitbucket.org/alvinpd/monsterninja
PHP | 737 lines | 421 code | 75 blank | 241 comment | 61 complexity | 65657cb891f49f4171bb54ebafa5b46a MD5 | raw file
  1. <?php defined('SYSPATH') or die('No direct script access.');
  2. /**
  3. * Date helper.
  4. *
  5. * @package Kohana
  6. * @category Helpers
  7. * @author Kohana Team
  8. * @copyright (c) 2007-2009 Kohana Team
  9. * @license http://kohanaphp.com/license
  10. */
  11. class Kohana_Date {
  12. // Second amounts for various time increments
  13. const YEAR = 31556926;
  14. const MONTH = 2629744;
  15. const WEEK = 604800;
  16. const DAY = 86400;
  17. const HOUR = 3600;
  18. const MINUTE = 60;
  19. /**
  20. * Returns the offset (in seconds) between two time zones. Use this to
  21. * display dates to users in different time zones.
  22. *
  23. * $seconds = Date::offset('America/Chicago', 'GMT');
  24. *
  25. * [!!] A list of time zones that PHP supports can be found at
  26. * <http://php.net/timezones>.
  27. *
  28. * @param string timezone that to find the offset of
  29. * @param string timezone used as the baseline
  30. * @param mixed UNIX timestamp or date string
  31. * @return integer
  32. */
  33. public static function offset($remote, $local = NULL, $now = NULL)
  34. {
  35. if ($local === NULL)
  36. {
  37. // Use the default timezone
  38. $local = date_default_timezone_get();
  39. }
  40. if (is_int($now))
  41. {
  42. // Convert the timestamp into a string
  43. $now = date(DateTime::RFC2822, $now);
  44. }
  45. // Create timezone objects
  46. $zone_remote = new DateTimeZone($remote);
  47. $zone_local = new DateTimeZone($local);
  48. // Create date objects from timezones
  49. $time_remote = new DateTime($now, $zone_remote);
  50. $time_local = new DateTime($now, $zone_local);
  51. // Find the offset
  52. $offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
  53. return $offset;
  54. }
  55. /**
  56. * Number of seconds in a minute, incrementing by a step. Typically used as
  57. * a shortcut for generating a list that can used in a form.
  58. *
  59. * $seconds = Date::seconds(); // 01, 02, 03, ..., 58, 59, 60
  60. *
  61. * @param integer amount to increment each step by, 1 to 30
  62. * @param integer start value
  63. * @param integer end value
  64. * @return array A mirrored (foo => foo) array from 1-60.
  65. */
  66. public static function seconds($step = 1, $start = 0, $end = 60)
  67. {
  68. // Always integer
  69. $step = (int) $step;
  70. $seconds = array();
  71. for ($i = $start; $i < $end; $i += $step)
  72. {
  73. $seconds[$i] = sprintf('%02d', $i);
  74. }
  75. return $seconds;
  76. }
  77. /**
  78. * Number of minutes in an hour, incrementing by a step. Typically used as
  79. * a shortcut for generating a list that can be used in a form.
  80. *
  81. * $minutes = Date::minutes(); // 05, 10, 15, ..., 50, 55, 60
  82. *
  83. * @uses Date::seconds
  84. * @param integer amount to increment each step by, 1 to 30
  85. * @return array A mirrored (foo => foo) array from 1-60.
  86. */
  87. public static function minutes($step = 5)
  88. {
  89. // Because there are the same number of minutes as seconds in this set,
  90. // we choose to re-use seconds(), rather than creating an entirely new
  91. // function. Shhhh, it's cheating! ;) There are several more of these
  92. // in the following methods.
  93. return Date::seconds($step);
  94. }
  95. /**
  96. * Number of hours in a day. Typically used as a shortcut for generating a
  97. * list that can be used in a form.
  98. *
  99. * $hours = Date::hours(); // 01, 02, 03, ..., 10, 11, 12
  100. *
  101. * @param integer amount to increment each step by
  102. * @param boolean use 24-hour time
  103. * @param integer the hour to start at
  104. * @return array A mirrored (foo => foo) array from start-12 or start-23.
  105. */
  106. public static function hours($step = 1, $long = FALSE, $start = NULL)
  107. {
  108. // Default values
  109. $step = (int) $step;
  110. $long = (bool) $long;
  111. $hours = array();
  112. // Set the default start if none was specified.
  113. if ($start === NULL)
  114. {
  115. $start = ($long === FALSE) ? 1 : 0;
  116. }
  117. $hours = array();
  118. // 24-hour time has 24 hours, instead of 12
  119. $size = ($long === TRUE) ? 23 : 12;
  120. for ($i = $start; $i <= $size; $i += $step)
  121. {
  122. $hours[$i] = (string) $i;
  123. }
  124. return $hours;
  125. }
  126. /**
  127. * Returns AM or PM, based on a given hour (in 24 hour format).
  128. *
  129. * $type = Date::ampm(12); // PM
  130. * $type = Date::ampm(1); // AM
  131. *
  132. * @param integer number of the hour
  133. * @return string
  134. */
  135. public static function ampm($hour)
  136. {
  137. // Always integer
  138. $hour = (int) $hour;
  139. return ($hour > 11) ? 'PM' : 'AM';
  140. }
  141. /**
  142. * Adjusts a non-24-hour number into a 24-hour number.
  143. *
  144. * $hour = Date::adjust(3, 'pm'); // 15
  145. *
  146. * @param integer hour to adjust
  147. * @param string AM or PM
  148. * @return string
  149. */
  150. public static function adjust($hour, $ampm)
  151. {
  152. $hour = (int) $hour;
  153. $ampm = strtolower($ampm);
  154. switch ($ampm)
  155. {
  156. case 'am':
  157. if ($hour == 12)
  158. $hour = 0;
  159. break;
  160. case 'pm':
  161. if ($hour < 12)
  162. $hour += 12;
  163. break;
  164. }
  165. return sprintf('%02d', $hour);
  166. }
  167. /**
  168. * Number of days in a given month and year. Typically used as a shortcut
  169. * for generating a list that can be used in a form.
  170. *
  171. * Date::days(4, 2010); // 1, 2, 3, ..., 28, 29, 30
  172. *
  173. * @param integer number of month
  174. * @param integer number of year to check month, defaults to the current year
  175. * @return array A mirrored (foo => foo) array of the days.
  176. */
  177. public static function days($month, $year = FALSE)
  178. {
  179. static $months;
  180. if ($year === FALSE)
  181. {
  182. // Use the current year by default
  183. $year = date('Y');
  184. }
  185. // Always integers
  186. $month = (int) $month;
  187. $year = (int) $year;
  188. // We use caching for months, because time functions are used
  189. if (empty($months[$year][$month]))
  190. {
  191. $months[$year][$month] = array();
  192. // Use date to find the number of days in the given month
  193. $total = date('t', mktime(1, 0, 0, $month, 1, $year)) + 1;
  194. for ($i = 1; $i < $total; $i++)
  195. {
  196. $months[$year][$month][$i] = (string) $i;
  197. }
  198. }
  199. return $months[$year][$month];
  200. }
  201. /**
  202. * Number of months in a year. Typically used as a shortcut for generating
  203. * a list that can be used in a form.
  204. *
  205. * Date::months(); // 01, 02, 03, ..., 10, 11, 12
  206. *
  207. * @uses Date::hours
  208. * @return array A mirrored (foo => foo) array from 1-12.
  209. */
  210. public static function months()
  211. {
  212. return Date::hours();
  213. }
  214. /**
  215. * Returns an array of years between a starting and ending year. By default,
  216. * the the current year - 5 and current year + 5 will be used. Typically used
  217. * as a shortcut for generating a list that can be used in a form.
  218. *
  219. * $years = Date::years(2000, 2010); // 2000, 2001, ..., 2009, 2010
  220. *
  221. * @param integer starting year (default is current year - 5)
  222. * @param integer ending year (default is current year + 5)
  223. * @return array
  224. */
  225. public static function years($start = FALSE, $end = FALSE)
  226. {
  227. // Default values
  228. $start = ($start === FALSE) ? date('Y') - 5 : (int) $start;
  229. $end = ($end === FALSE) ? date('Y') + 5 : (int) $end;
  230. $years = array();
  231. for ($i = $start; $i <= $end; $i++)
  232. {
  233. $years[$i] = (string) $i;
  234. }
  235. return $years;
  236. }
  237. /**
  238. * Returns time difference between two timestamps, in human readable format.
  239. * If the second timestamp is not given, the current time will be used.
  240. * Also consider using [Date::fuzzy_span] when displaying a span.
  241. *
  242. * $span = Date::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
  243. * $span = Date::span(60, 182, 'minutes'); // 2
  244. *
  245. * @param integer timestamp to find the span of
  246. * @param integer timestamp to use as the baseline
  247. * @param string formatting string
  248. * @return string when only a single output is requested
  249. * @return array associative list of all outputs requested
  250. */
  251. public static function span($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
  252. {
  253. // Normalize output
  254. $output = trim(strtolower((string) $output));
  255. if ( ! $output)
  256. {
  257. // Invalid output
  258. return FALSE;
  259. }
  260. // Array with the output formats
  261. $output = preg_split('/[^a-z]+/', $output);
  262. // Convert the list of outputs to an associative array
  263. $output = array_combine($output, array_fill(0, count($output), 0));
  264. // Make the output values into keys
  265. extract(array_flip($output), EXTR_SKIP);
  266. if ($local === NULL)
  267. {
  268. // Calculate the span from the current time
  269. $local = time();
  270. }
  271. // Calculate timespan (seconds)
  272. $timespan = abs($remote - $local);
  273. if (isset($output['years']))
  274. {
  275. $timespan -= Date::YEAR * ($output['years'] = (int) floor($timespan / Date::YEAR));
  276. }
  277. if (isset($output['months']))
  278. {
  279. $timespan -= Date::MONTH * ($output['months'] = (int) floor($timespan / Date::MONTH));
  280. }
  281. if (isset($output['weeks']))
  282. {
  283. $timespan -= Date::WEEK * ($output['weeks'] = (int) floor($timespan / Date::WEEK));
  284. }
  285. if (isset($output['days']))
  286. {
  287. $timespan -= Date::DAY * ($output['days'] = (int) floor($timespan / Date::DAY));
  288. }
  289. if (isset($output['hours']))
  290. {
  291. $timespan -= Date::HOUR * ($output['hours'] = (int) floor($timespan / Date::HOUR));
  292. }
  293. if (isset($output['minutes']))
  294. {
  295. $timespan -= Date::MINUTE * ($output['minutes'] = (int) floor($timespan / Date::MINUTE));
  296. }
  297. // Seconds ago, 1
  298. if (isset($output['seconds']))
  299. {
  300. $output['seconds'] = $timespan;
  301. }
  302. if (count($output) === 1)
  303. {
  304. // Only a single output was requested, return it
  305. return array_pop($output);
  306. }
  307. // Return array
  308. return $output;
  309. }
  310. /**
  311. * Returns the difference between a time and now in a "fuzzy" way.
  312. * Note that unlike [Date::span], the "local" timestamp will always be the
  313. * current time. Displaying a fuzzy time instead of a date is usually
  314. * faster to read and understand.
  315. *
  316. * $span = Date::fuzzy_span(time() - 10); // "moments ago"
  317. * $span = Date::fuzzy_span(time() + 20); // "in moments"
  318. *
  319. * @param integer "remote" timestamp
  320. * @return string
  321. */
  322. public static function fuzzy_span($timestamp)
  323. {
  324. // Determine the difference in seconds
  325. $offset = abs(time() - $timestamp);
  326. if ($offset <= Date::MINUTE)
  327. {
  328. $span = 'moments';
  329. }
  330. elseif ($offset < (Date::MINUTE * 20))
  331. {
  332. $span = 'a few minutes';
  333. }
  334. elseif ($offset < Date::HOUR)
  335. {
  336. $span = 'less than an hour';
  337. }
  338. elseif ($offset < (Date::HOUR * 4))
  339. {
  340. $span = 'a couple of hours';
  341. }
  342. elseif ($offset < Date::DAY)
  343. {
  344. $span = 'less than a day';
  345. }
  346. elseif ($offset < (Date::DAY * 2))
  347. {
  348. $span = 'about a day';
  349. }
  350. elseif ($offset < (Date::DAY * 4))
  351. {
  352. $span = 'a couple of days';
  353. }
  354. elseif ($offset < Date::WEEK)
  355. {
  356. $span = 'less than a week';
  357. }
  358. elseif ($offset < (Date::WEEK * 2))
  359. {
  360. $span = 'about a week';
  361. }
  362. elseif ($offset < Date::MONTH)
  363. {
  364. $span = 'less than a month';
  365. }
  366. elseif ($offset < (Date::MONTH * 2))
  367. {
  368. $span = 'about a month';
  369. }
  370. elseif ($offset < (Date::MONTH * 4))
  371. {
  372. $span = 'a couple of months';
  373. }
  374. elseif ($offset < Date::YEAR)
  375. {
  376. $span = 'less than a year';
  377. }
  378. elseif ($offset < (Date::YEAR * 2))
  379. {
  380. $span = 'about a year';
  381. }
  382. elseif ($offset < (Date::YEAR * 4))
  383. {
  384. $span = 'a couple of years';
  385. }
  386. elseif ($offset < (Date::YEAR * 8))
  387. {
  388. $span = 'a few years';
  389. }
  390. elseif ($offset < (Date::YEAR * 12))
  391. {
  392. $span = 'about a decade';
  393. }
  394. elseif ($offset < (Date::YEAR * 24))
  395. {
  396. $span = 'a couple of decades';
  397. }
  398. elseif ($offset < (Date::YEAR * 64))
  399. {
  400. $span = 'several decades';
  401. }
  402. else
  403. {
  404. $span = 'a long time';
  405. }
  406. if ($timestamp <= time())
  407. {
  408. // This is in the past
  409. return $span.' ago';
  410. }
  411. else
  412. {
  413. // This in the future
  414. return 'in '.$span;
  415. }
  416. }
  417. /**
  418. * Converts a UNIX timestamp to DOS format. There are very few cases where
  419. * this is needed, but some binary formats use it (eg: zip files.)
  420. * Converting the other direction is done using {@link Date::dos2unix}.
  421. *
  422. * $dos = Date::unix2dos($unix);
  423. *
  424. * @param integer UNIX timestamp
  425. * @return integer
  426. */
  427. public static function unix2dos($timestamp = FALSE)
  428. {
  429. $timestamp = ($timestamp === FALSE) ? getdate() : getdate($timestamp);
  430. if ($timestamp['year'] < 1980)
  431. {
  432. return (1 << 21 | 1 << 16);
  433. }
  434. $timestamp['year'] -= 1980;
  435. // What voodoo is this? I have no idea... Geert can explain it though,
  436. // and that's good enough for me.
  437. return ($timestamp['year'] << 25 | $timestamp['mon'] << 21 |
  438. $timestamp['mday'] << 16 | $timestamp['hours'] << 11 |
  439. $timestamp['minutes'] << 5 | $timestamp['seconds'] >> 1);
  440. }
  441. /**
  442. * Converts a DOS timestamp to UNIX format.There are very few cases where
  443. * this is needed, but some binary formats use it (eg: zip files.)
  444. * Converting the other direction is done using {@link Date::unix2dos}.
  445. *
  446. * $unix = Date::dos2unix($dos);
  447. *
  448. * @param integer DOS timestamp
  449. * @return integer
  450. */
  451. public static function dos2unix($timestamp = FALSE)
  452. {
  453. $sec = 2 * ($timestamp & 0x1f);
  454. $min = ($timestamp >> 5) & 0x3f;
  455. $hrs = ($timestamp >> 11) & 0x1f;
  456. $day = ($timestamp >> 16) & 0x1f;
  457. $mon = ($timestamp >> 21) & 0x0f;
  458. $year = ($timestamp >> 25) & 0x7f;
  459. return mktime($hrs, $min, $sec, $mon, $day, $year + 1980);
  460. }
  461. public static function factory( $date = 'now', $tzOffset = 0)
  462. {
  463. return new Kohana_Date($date, $tzOffset);
  464. }
  465. /**
  466. * Unix timestamp
  467. *
  468. * @var int|boolean
  469. * @access protected
  470. */
  471. var $_date = false;
  472. /**
  473. * Time offset (in seconds)
  474. *
  475. * @var string
  476. * @access protected
  477. */
  478. var $_offset = 0;
  479. final private function __construct($date = 'now', $tzOffset = 0)
  480. {
  481. if ($date == 'now' || empty($date))
  482. {
  483. $this->_date = strtotime(gmdate("M d Y H:i:s", time()));
  484. return;
  485. }
  486. $tzOffset *= 3600;
  487. if (is_numeric($date))
  488. {
  489. $this->_date = $date - $tzOffset;
  490. return;
  491. }
  492. if (preg_match('~(?:(?:Mon|Tue|Wed|Thu|Fri|Sat|Sun),\\s+)?(\\d{1,2})\\s+([a-zA-Z]{3})\\s+(\\d{4})\\s+(\\d{2}):(\\d{2}):(\\d{2})\\s+(.*)~i',$date,$matches))
  493. {
  494. $months = Array(
  495. 'jan' => 1, 'feb' => 2, 'mar' => 3, 'apr' => 4,
  496. 'may' => 5, 'jun' => 6, 'jul' => 7, 'aug' => 8,
  497. 'sep' => 9, 'oct' => 10, 'nov' => 11, 'dec' => 12
  498. );
  499. $matches[2] = strtolower($matches[2]);
  500. if (! isset($months[$matches[2]])) {
  501. return;
  502. }
  503. $this->_date = mktime(
  504. $matches[4], $matches[5], $matches[6],
  505. $months[$matches[2]], $matches[1], $matches[3]
  506. );
  507. if ($this->_date === false) {
  508. return;
  509. }
  510. if ($matches[7][0] == '+') {
  511. $tzOffset = 3600 * substr($matches[7], 1, 2)
  512. + 60 * substr($matches[7], -2);
  513. } elseif ($matches[7][0] == '-') {
  514. $tzOffset = -3600 * substr($matches[7], 1, 2)
  515. - 60 * substr($matches[7], -2);
  516. } else {
  517. if (strlen($matches[7]) == 1) {
  518. $oneHour = 3600;
  519. $ord = ord($matches[7]);
  520. if ($ord < ord('M')) {
  521. $tzOffset = (ord('A') - $ord - 1) * $oneHour;
  522. } elseif ($ord >= ord('M') && $matches[7] != 'Z') {
  523. $tzOffset = ($ord - ord('M')) * $oneHour;
  524. } elseif ($matches[7] == 'Z') {
  525. $tzOffset = 0;
  526. }
  527. }
  528. switch ($matches[7]) {
  529. case 'UT':
  530. case 'GMT': $tzOffset = 0;
  531. }
  532. }
  533. $this->_date -= $tzOffset;
  534. return;
  535. }
  536. if (preg_match('~(\\d{4})-(\\d{2})-(\\d{2})[T\s](\\d{2}):(\\d{2}):(\\d{2})(.*)~', $date, $matches))
  537. {
  538. $this->_date = mktime(
  539. $matches[4], $matches[5], $matches[6],
  540. $matches[2], $matches[3], $matches[1]
  541. );
  542. if ($this->_date == false) {
  543. return;
  544. }
  545. if (isset($matches[7][0])) {
  546. if ($matches[7][0] == '+' || $matches[7][0] == '-') {
  547. $tzOffset = 60 * (
  548. substr($matches[7], 0, 3) * 60 + substr($matches[7], -2)
  549. );
  550. } elseif ($matches[7] == 'Z') {
  551. $tzOffset = 0;
  552. }
  553. }
  554. $this->_date -= $tzOffset;
  555. return;
  556. }
  557. $this->_date = (strtotime($date) == -1) ? false : strtotime($date);
  558. if ($this->_date) {
  559. $this->_date -= $tzOffset;
  560. }
  561. }
  562. /**
  563. * Set the date offset (in hours)
  564. *
  565. * @access public
  566. * @param float The offset in hours
  567. */
  568. function setOffset($offset) {
  569. $this->_offset = 3600 * $offset;
  570. }
  571. /**
  572. * Get the date offset (in hours)
  573. *
  574. * @access public
  575. * @return integer
  576. */
  577. function getOffset() {
  578. return ((float) $this->_offset) / 3600.0;
  579. }
  580. /**
  581. * Gets the date in a specific format
  582. *
  583. * Returns a string formatted according to the given format. Month and weekday names and
  584. * other language dependent strings respect the current locale
  585. *
  586. * @param string $format The date format specification string (see {@link PHP_MANUAL#strftime})
  587. * @return a date in a specific format
  588. */
  589. function toFormat($format = '%Y-%m-%d %H:%M:%S')
  590. {
  591. $date = ($this->_date !== false) ? $this->_strftime($format, $this->_date + $this->_offset) : null;
  592. return $date;
  593. }
  594. /**
  595. * Translates needed strings in for JDate::toFormat (see {@link PHP_MANUAL#strftime})
  596. *
  597. * @access protected
  598. * @param string $format The date format specification string (see {@link PHP_MANUAL#strftime})
  599. * @param int $time Unix timestamp
  600. * @return string a date in the specified format
  601. */
  602. function _strftime($format, $time)
  603. {
  604. if(strpos($format, '%a') !== false)
  605. $format = str_replace('%a', $this->_dayToString(date('w', $time), true), $format);
  606. if(strpos($format, '%A') !== false)
  607. $format = str_replace('%A', $this->_dayToString(date('w', $time)), $format);
  608. if(strpos($format, '%b') !== false)
  609. $format = str_replace('%b', $this->_monthToString(date('n', $time), true), $format);
  610. if(strpos($format, '%B') !== false)
  611. $format = str_replace('%B', $this->_monthToString(date('n', $time)), $format);
  612. $date = strftime($format, $time);
  613. return $date;
  614. }
  615. /**
  616. * Translates month number to string
  617. *
  618. * @access protected
  619. * @param int $month The numeric month of the year
  620. * @param bool $abbr Return the abreviated month string?
  621. * @return string month string
  622. */
  623. function _monthToString($month, $abbr = false)
  624. {
  625. switch ($month)
  626. {
  627. case 1: return $abbr ? __('Jan') : __('January');
  628. case 2: return $abbr ? __('Feb') : __('February');
  629. case 3: return $abbr ? __('Mar') : __('March');
  630. case 4: return $abbr ? __('Apr') : __('April');
  631. case 5: return $abbr ? __('May') : __('May');
  632. case 6: return $abbr ? __('Jun') : __('June');
  633. case 7: return $abbr ? __('Jul') : __('July');
  634. case 8: return $abbr ? __('Aug') : __('August');
  635. case 9: return $abbr ? __('Sep') : __('September');
  636. case 10: return $abbr ? __('Oct') : __('October');
  637. case 11: return $abbr ? __('Nov') : __('November');
  638. case 12: return $abbr ? __('Dec') : __('December');
  639. }
  640. }
  641. /**
  642. * Translates day of week number to string
  643. *
  644. * @access protected
  645. * @param int $day The numeric day of the week
  646. * @param bool $abbr Return the abreviated day string?
  647. * @return string day string
  648. */
  649. function _dayToString($day, $abbr = false)
  650. {
  651. switch ($day)
  652. {
  653. case 0: return $abbr ? __('Sun') : __('Sunday');
  654. case 1: return $abbr ? __('Mon') : __('Monday');
  655. case 2: return $abbr ? __('Tue') : __('Tuesday');
  656. case 3: return $abbr ? __('Wed') : __('Wednesday');
  657. case 4: return $abbr ? __('Thu') : __('Thursday');
  658. case 5: return $abbr ? __('Fri') : __('Friday');
  659. case 6: return $abbr ? __('Sat') : __('Saturday');
  660. }
  661. }
  662. } // End date