PageRenderTime 70ms CodeModel.GetById 21ms RepoModel.GetById 1ms app.codeStats 0ms

/kohana/system/classes/kohana/date.php

https://github.com/alfons56/Kohana
PHP | 557 lines | 288 code | 66 blank | 203 comment | 27 complexity | 8d93f47760d983e6bcd51a2a5cdf9fdf 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. * Default timestamp format for formatted_time
  21. * @var string
  22. */
  23. public static $timestamp_format = 'Y-m-d H:i:s';
  24. /**
  25. * Timezone for formatted_time
  26. * @link http://uk2.php.net/manual/en/timezones.php
  27. * @var string
  28. */
  29. public static $timezone;
  30. /**
  31. * Returns the offset (in seconds) between two time zones. Use this to
  32. * display dates to users in different time zones.
  33. *
  34. * $seconds = Date::offset('America/Chicago', 'GMT');
  35. *
  36. * [!!] A list of time zones that PHP supports can be found at
  37. * <http://php.net/timezones>.
  38. *
  39. * @param string timezone that to find the offset of
  40. * @param string timezone used as the baseline
  41. * @param mixed UNIX timestamp or date string
  42. * @return integer
  43. */
  44. public static function offset($remote, $local = NULL, $now = NULL)
  45. {
  46. if ($local === NULL)
  47. {
  48. // Use the default timezone
  49. $local = date_default_timezone_get();
  50. }
  51. if (is_int($now))
  52. {
  53. // Convert the timestamp into a string
  54. $now = date(DateTime::RFC2822, $now);
  55. }
  56. // Create timezone objects
  57. $zone_remote = new DateTimeZone($remote);
  58. $zone_local = new DateTimeZone($local);
  59. // Create date objects from timezones
  60. $time_remote = new DateTime($now, $zone_remote);
  61. $time_local = new DateTime($now, $zone_local);
  62. // Find the offset
  63. $offset = $zone_remote->getOffset($time_remote) - $zone_local->getOffset($time_local);
  64. return $offset;
  65. }
  66. /**
  67. * Number of seconds in a minute, incrementing by a step. Typically used as
  68. * a shortcut for generating a list that can used in a form.
  69. *
  70. * $seconds = Date::seconds(); // 01, 02, 03, ..., 58, 59, 60
  71. *
  72. * @param integer amount to increment each step by, 1 to 30
  73. * @param integer start value
  74. * @param integer end value
  75. * @return array A mirrored (foo => foo) array from 1-60.
  76. */
  77. public static function seconds($step = 1, $start = 0, $end = 60)
  78. {
  79. // Always integer
  80. $step = (int) $step;
  81. $seconds = array();
  82. for ($i = $start; $i < $end; $i += $step)
  83. {
  84. $seconds[$i] = sprintf('%02d', $i);
  85. }
  86. return $seconds;
  87. }
  88. /**
  89. * Number of minutes in an hour, incrementing by a step. Typically used as
  90. * a shortcut for generating a list that can be used in a form.
  91. *
  92. * $minutes = Date::minutes(); // 05, 10, 15, ..., 50, 55, 60
  93. *
  94. * @uses Date::seconds
  95. * @param integer amount to increment each step by, 1 to 30
  96. * @return array A mirrored (foo => foo) array from 1-60.
  97. */
  98. public static function minutes($step = 5)
  99. {
  100. // Because there are the same number of minutes as seconds in this set,
  101. // we choose to re-use seconds(), rather than creating an entirely new
  102. // function. Shhhh, it's cheating! ;) There are several more of these
  103. // in the following methods.
  104. return Date::seconds($step);
  105. }
  106. /**
  107. * Number of hours in a day. Typically used as a shortcut for generating a
  108. * list that can be used in a form.
  109. *
  110. * $hours = Date::hours(); // 01, 02, 03, ..., 10, 11, 12
  111. *
  112. * @param integer amount to increment each step by
  113. * @param boolean use 24-hour time
  114. * @param integer the hour to start at
  115. * @return array A mirrored (foo => foo) array from start-12 or start-23.
  116. */
  117. public static function hours($step = 1, $long = FALSE, $start = NULL)
  118. {
  119. // Default values
  120. $step = (int) $step;
  121. $long = (bool) $long;
  122. $hours = array();
  123. // Set the default start if none was specified.
  124. if ($start === NULL)
  125. {
  126. $start = ($long === FALSE) ? 1 : 0;
  127. }
  128. $hours = array();
  129. // 24-hour time has 24 hours, instead of 12
  130. $size = ($long === TRUE) ? 23 : 12;
  131. for ($i = $start; $i <= $size; $i += $step)
  132. {
  133. $hours[$i] = (string) $i;
  134. }
  135. return $hours;
  136. }
  137. /**
  138. * Returns AM or PM, based on a given hour (in 24 hour format).
  139. *
  140. * $type = Date::ampm(12); // PM
  141. * $type = Date::ampm(1); // AM
  142. *
  143. * @param integer number of the hour
  144. * @return string
  145. */
  146. public static function ampm($hour)
  147. {
  148. // Always integer
  149. $hour = (int) $hour;
  150. return ($hour > 11) ? 'PM' : 'AM';
  151. }
  152. /**
  153. * Adjusts a non-24-hour number into a 24-hour number.
  154. *
  155. * $hour = Date::adjust(3, 'pm'); // 15
  156. *
  157. * @param integer hour to adjust
  158. * @param string AM or PM
  159. * @return string
  160. */
  161. public static function adjust($hour, $ampm)
  162. {
  163. $hour = (int) $hour;
  164. $ampm = strtolower($ampm);
  165. switch ($ampm)
  166. {
  167. case 'am':
  168. if ($hour == 12)
  169. $hour = 0;
  170. break;
  171. case 'pm':
  172. if ($hour < 12)
  173. $hour += 12;
  174. break;
  175. }
  176. return sprintf('%02d', $hour);
  177. }
  178. /**
  179. * Number of days in a given month and year. Typically used as a shortcut
  180. * for generating a list that can be used in a form.
  181. *
  182. * Date::days(4, 2010); // 1, 2, 3, ..., 28, 29, 30
  183. *
  184. * @param integer number of month
  185. * @param integer number of year to check month, defaults to the current year
  186. * @return array A mirrored (foo => foo) array of the days.
  187. */
  188. public static function days($month, $year = FALSE)
  189. {
  190. static $months;
  191. if ($year === FALSE)
  192. {
  193. // Use the current year by default
  194. $year = date('Y');
  195. }
  196. // Always integers
  197. $month = (int) $month;
  198. $year = (int) $year;
  199. // We use caching for months, because time functions are used
  200. if (empty($months[$year][$month]))
  201. {
  202. $months[$year][$month] = array();
  203. // Use date to find the number of days in the given month
  204. $total = date('t', mktime(1, 0, 0, $month, 1, $year)) + 1;
  205. for ($i = 1; $i < $total; $i++)
  206. {
  207. $months[$year][$month][$i] = (string) $i;
  208. }
  209. }
  210. return $months[$year][$month];
  211. }
  212. /**
  213. * Number of months in a year. Typically used as a shortcut for generating
  214. * a list that can be used in a form.
  215. *
  216. * Date::months(); // 01, 02, 03, ..., 10, 11, 12
  217. *
  218. * @uses Date::hours
  219. * @return array A mirrored (foo => foo) array from 1-12.
  220. */
  221. public static function months()
  222. {
  223. return Date::hours();
  224. }
  225. /**
  226. * Returns an array of years between a starting and ending year. By default,
  227. * the the current year - 5 and current year + 5 will be used. Typically used
  228. * as a shortcut for generating a list that can be used in a form.
  229. *
  230. * $years = Date::years(2000, 2010); // 2000, 2001, ..., 2009, 2010
  231. *
  232. * @param integer starting year (default is current year - 5)
  233. * @param integer ending year (default is current year + 5)
  234. * @return array
  235. */
  236. public static function years($start = FALSE, $end = FALSE)
  237. {
  238. // Default values
  239. $start = ($start === FALSE) ? date('Y') - 5 : (int) $start;
  240. $end = ($end === FALSE) ? date('Y') + 5 : (int) $end;
  241. $years = array();
  242. for ($i = $start; $i <= $end; $i++)
  243. {
  244. $years[$i] = (string) $i;
  245. }
  246. return $years;
  247. }
  248. /**
  249. * Returns time difference between two timestamps, in human readable format.
  250. * If the second timestamp is not given, the current time will be used.
  251. * Also consider using [Date::fuzzy_span] when displaying a span.
  252. *
  253. * $span = Date::span(60, 182, 'minutes,seconds'); // array('minutes' => 2, 'seconds' => 2)
  254. * $span = Date::span(60, 182, 'minutes'); // 2
  255. *
  256. * @param integer timestamp to find the span of
  257. * @param integer timestamp to use as the baseline
  258. * @param string formatting string
  259. * @return string when only a single output is requested
  260. * @return array associative list of all outputs requested
  261. */
  262. public static function span($remote, $local = NULL, $output = 'years,months,weeks,days,hours,minutes,seconds')
  263. {
  264. // Normalize output
  265. $output = trim(strtolower((string) $output));
  266. if ( ! $output)
  267. {
  268. // Invalid output
  269. return FALSE;
  270. }
  271. // Array with the output formats
  272. $output = preg_split('/[^a-z]+/', $output);
  273. // Convert the list of outputs to an associative array
  274. $output = array_combine($output, array_fill(0, count($output), 0));
  275. // Make the output values into keys
  276. extract(array_flip($output), EXTR_SKIP);
  277. if ($local === NULL)
  278. {
  279. // Calculate the span from the current time
  280. $local = time();
  281. }
  282. // Calculate timespan (seconds)
  283. $timespan = abs($remote - $local);
  284. if (isset($output['years']))
  285. {
  286. $timespan -= Date::YEAR * ($output['years'] = (int) floor($timespan / Date::YEAR));
  287. }
  288. if (isset($output['months']))
  289. {
  290. $timespan -= Date::MONTH * ($output['months'] = (int) floor($timespan / Date::MONTH));
  291. }
  292. if (isset($output['weeks']))
  293. {
  294. $timespan -= Date::WEEK * ($output['weeks'] = (int) floor($timespan / Date::WEEK));
  295. }
  296. if (isset($output['days']))
  297. {
  298. $timespan -= Date::DAY * ($output['days'] = (int) floor($timespan / Date::DAY));
  299. }
  300. if (isset($output['hours']))
  301. {
  302. $timespan -= Date::HOUR * ($output['hours'] = (int) floor($timespan / Date::HOUR));
  303. }
  304. if (isset($output['minutes']))
  305. {
  306. $timespan -= Date::MINUTE * ($output['minutes'] = (int) floor($timespan / Date::MINUTE));
  307. }
  308. // Seconds ago, 1
  309. if (isset($output['seconds']))
  310. {
  311. $output['seconds'] = $timespan;
  312. }
  313. if (count($output) === 1)
  314. {
  315. // Only a single output was requested, return it
  316. return array_pop($output);
  317. }
  318. // Return array
  319. return $output;
  320. }
  321. /**
  322. * Returns the difference between a time and now in a "fuzzy" way.
  323. * Note that unlike [Date::span], the "local" timestamp will always be the
  324. * current time. Displaying a fuzzy time instead of a date is usually
  325. * faster to read and understand.
  326. *
  327. * $span = Date::fuzzy_span(time() - 10); // "moments ago"
  328. * $span = Date::fuzzy_span(time() + 20); // "in moments"
  329. *
  330. * @param integer "remote" timestamp
  331. * @return string
  332. */
  333. public static function fuzzy_span($timestamp)
  334. {
  335. // Determine the difference in seconds
  336. $offset = abs(time() - $timestamp);
  337. if ($offset <= Date::MINUTE)
  338. {
  339. $span = 'moments';
  340. }
  341. elseif ($offset < (Date::MINUTE * 20))
  342. {
  343. $span = 'a few minutes';
  344. }
  345. elseif ($offset < Date::HOUR)
  346. {
  347. $span = 'less than an hour';
  348. }
  349. elseif ($offset < (Date::HOUR * 4))
  350. {
  351. $span = 'a couple of hours';
  352. }
  353. elseif ($offset < Date::DAY)
  354. {
  355. $span = 'less than a day';
  356. }
  357. elseif ($offset < (Date::DAY * 2))
  358. {
  359. $span = 'about a day';
  360. }
  361. elseif ($offset < (Date::DAY * 4))
  362. {
  363. $span = 'a couple of days';
  364. }
  365. elseif ($offset < Date::WEEK)
  366. {
  367. $span = 'less than a week';
  368. }
  369. elseif ($offset < (Date::WEEK * 2))
  370. {
  371. $span = 'about a week';
  372. }
  373. elseif ($offset < Date::MONTH)
  374. {
  375. $span = 'less than a month';
  376. }
  377. elseif ($offset < (Date::MONTH * 2))
  378. {
  379. $span = 'about a month';
  380. }
  381. elseif ($offset < (Date::MONTH * 4))
  382. {
  383. $span = 'a couple of months';
  384. }
  385. elseif ($offset < Date::YEAR)
  386. {
  387. $span = 'less than a year';
  388. }
  389. elseif ($offset < (Date::YEAR * 2))
  390. {
  391. $span = 'about a year';
  392. }
  393. elseif ($offset < (Date::YEAR * 4))
  394. {
  395. $span = 'a couple of years';
  396. }
  397. elseif ($offset < (Date::YEAR * 8))
  398. {
  399. $span = 'a few years';
  400. }
  401. elseif ($offset < (Date::YEAR * 12))
  402. {
  403. $span = 'about a decade';
  404. }
  405. elseif ($offset < (Date::YEAR * 24))
  406. {
  407. $span = 'a couple of decades';
  408. }
  409. elseif ($offset < (Date::YEAR * 64))
  410. {
  411. $span = 'several decades';
  412. }
  413. else
  414. {
  415. $span = 'a long time';
  416. }
  417. if ($timestamp <= time())
  418. {
  419. // This is in the past
  420. return $span.' ago';
  421. }
  422. else
  423. {
  424. // This in the future
  425. return 'in '.$span;
  426. }
  427. }
  428. /**
  429. * Converts a UNIX timestamp to DOS format. There are very few cases where
  430. * this is needed, but some binary formats use it (eg: zip files.)
  431. * Converting the other direction is done using {@link Date::dos2unix}.
  432. *
  433. * $dos = Date::unix2dos($unix);
  434. *
  435. * @param integer UNIX timestamp
  436. * @return integer
  437. */
  438. public static function unix2dos($timestamp = FALSE)
  439. {
  440. $timestamp = ($timestamp === FALSE) ? getdate() : getdate($timestamp);
  441. if ($timestamp['year'] < 1980)
  442. {
  443. return (1 << 21 | 1 << 16);
  444. }
  445. $timestamp['year'] -= 1980;
  446. // What voodoo is this? I have no idea... Geert can explain it though,
  447. // and that's good enough for me.
  448. return ($timestamp['year'] << 25 | $timestamp['mon'] << 21 |
  449. $timestamp['mday'] << 16 | $timestamp['hours'] << 11 |
  450. $timestamp['minutes'] << 5 | $timestamp['seconds'] >> 1);
  451. }
  452. /**
  453. * Converts a DOS timestamp to UNIX format.There are very few cases where
  454. * this is needed, but some binary formats use it (eg: zip files.)
  455. * Converting the other direction is done using {@link Date::unix2dos}.
  456. *
  457. * $unix = Date::dos2unix($dos);
  458. *
  459. * @param integer DOS timestamp
  460. * @return integer
  461. */
  462. public static function dos2unix($timestamp = FALSE)
  463. {
  464. $sec = 2 * ($timestamp & 0x1f);
  465. $min = ($timestamp >> 5) & 0x3f;
  466. $hrs = ($timestamp >> 11) & 0x1f;
  467. $day = ($timestamp >> 16) & 0x1f;
  468. $mon = ($timestamp >> 21) & 0x0f;
  469. $year = ($timestamp >> 25) & 0x7f;
  470. return mktime($hrs, $min, $sec, $mon, $day, $year + 1980);
  471. }
  472. /**
  473. * Returns a date/time string with the specified timestamp format
  474. *
  475. * $time = Date::formatted_time('5 minutes ago');
  476. *
  477. * @see http://php.net/manual/en/datetime.construct.php
  478. * @param string datetime_str datetime string
  479. * @param string timestamp_format timestamp format
  480. * @return string
  481. */
  482. public static function formatted_time($datetime_str = 'now', $timestamp_format = NULL, $timezone = NULL)
  483. {
  484. $timestamp_format = $timestamp_format == NULL ? Date::$timestamp_format : $timestamp_format;
  485. $timezone = $timezone === NULL ? Date::$timezone : $timezone;
  486. $time = new DateTime($datetime_str, new DateTimeZone(
  487. $timezone ? $timezone : date_default_timezone_get()
  488. ));
  489. return $time->format($timestamp_format);
  490. }
  491. } // End date