PageRenderTime 51ms CodeModel.GetById 26ms RepoModel.GetById 1ms app.codeStats 0ms

/system/classes/habaridatetime.php

https://github.com/HabariMag/habarimag-old
PHP | 551 lines | 274 code | 77 blank | 200 comment | 37 complexity | 85d2348e6545097831569bf9a625d343 MD5 | raw file
Possible License(s): Apache-2.0
  1. <?php
  2. /**
  3. * @package Habari
  4. *
  5. */
  6. /**
  7. * HabariDateTime class to wrap dates in.
  8. *
  9. * @property-read HabariDateTime $clone Returns a clonned object.
  10. * @property-read string $sql Returns a unix timestamp for inserting into DB.
  11. * @property-read int $int Returns a unix timestamp as integer.
  12. * @property-read string $time Returns the time formatted according to the blog's settings.
  13. * @property-read string $date Returns the date formatted according to the blog's settings.
  14. * @property-read string $friendly Returns the time as a friendly string (ie: 4 months, 3 days ago, etc.).
  15. * @property-read string $fuzzy Returns the time as a short "fuzzy" string (ie: "just now", "yesterday", "2 weeks ago", etc.).
  16. */
  17. class HabariDateTime extends DateTime
  18. {
  19. private static $default_timezone;
  20. private static $default_datetime_format = 'c';
  21. private static $default_date_format;
  22. private static $default_time_format;
  23. // various time increments in seconds
  24. const YEAR = 31556926;
  25. const MONTH = 2629744;
  26. const WEEK = 604800;
  27. const DAY = 86400;
  28. const HOUR = 3600;
  29. const MINUTE = 60;
  30. /**
  31. * Set default timezone to system default on init.
  32. *
  33. * @static
  34. */
  35. public static function __static()
  36. {
  37. if ( Options::get( 'timezone' ) ) {
  38. self::set_default_timezone( Options::get( 'timezone' ) );
  39. }
  40. self::$default_timezone = date_default_timezone_get();
  41. self::$default_date_format = Options::get( 'dateformat' );
  42. self::$default_time_format = Options::get( 'timeformat' );
  43. if ( self::$default_date_format || self::$default_time_format ) {
  44. self::set_default_datetime_format( self::$default_date_format . ' ' . self::$default_time_format );
  45. }
  46. }
  47. /**
  48. * Set default date/time format. The format is the same as the
  49. * internal php {@link http://ca.php.net/date date() function}.
  50. *
  51. * @static
  52. * @param string $format The date format.
  53. */
  54. public static function set_default_datetime_format( $format )
  55. {
  56. self::$default_datetime_format = $format;
  57. }
  58. /**
  59. * Get the default date/time format set.
  60. *
  61. * @static
  62. * @see set_default_datetime_format()
  63. * @return string The date format set.
  64. */
  65. public static function get_default_datetime_format()
  66. {
  67. return self::$default_datetime_format;
  68. }
  69. /**
  70. * Sets the timezone for Habari and PHP.
  71. *
  72. * @static
  73. * @param string $timezone A timezone name, not an abbreviation, for example 'America/New York'
  74. */
  75. public static function set_default_timezone( $timezone )
  76. {
  77. self::$default_timezone = $timezone;
  78. date_default_timezone_set( self::$default_timezone );
  79. }
  80. /**
  81. * Get the timezone for Habari and PHP.
  82. * Defaults to system timezone if not set.
  83. *
  84. * @static
  85. * @see set_default_timezone()
  86. * @param string The deafult timezone.
  87. */
  88. public static function get_default_timezone()
  89. {
  90. return self::$default_timezone;
  91. }
  92. /**
  93. * Helper function to create a HabariDateTime object for the given
  94. * time and timezone. If no time is given, defaults to 'now'. If no
  95. * timezone given defaults to timezone set in {@link set_default_timezone()}
  96. *
  97. * @static
  98. * @see DateTime::__construct()
  99. * @param string $time String in a format accepted by
  100. * {@link http://ca.php.net/strtotime strtotime()}, defaults to "now".
  101. * @param string $timezone A timezone name, not an abbreviation.
  102. */
  103. public static function date_create( $time = null, $timezone = null )
  104. {
  105. if ( $time instanceOf HabariDateTime ) {
  106. return $time;
  107. }
  108. elseif ( $time instanceOf DateTime ) {
  109. $time = $time->format( 'U' );
  110. }
  111. elseif ( $time == null ) {
  112. $time = 'now';
  113. }
  114. elseif ( is_numeric( $time ) ) {
  115. $time = '@' . $time;
  116. }
  117. if ( $timezone === null ) {
  118. $timezone = self::$default_timezone;
  119. }
  120. // passing the timezone to construct doesn't seem to do anything.
  121. $datetime = new HabariDateTime( $time );
  122. $datetime->set_timezone( $timezone );
  123. return $datetime;
  124. }
  125. /**
  126. * Set the date of this object
  127. *
  128. * @see DateTime::setDate()
  129. * @param int $year Year of the date
  130. * @param int $month Month of the date
  131. * @param int $day Day of the date
  132. */
  133. public function set_date( $year, $month, $day )
  134. {
  135. parent::setDate( $year, $month, $day );
  136. return $this;
  137. }
  138. /**
  139. * Sets the ISO date
  140. *
  141. * @see DateTime::setISODate()
  142. * @param int $year Year of the date
  143. * @param int $month Month of the date
  144. * @param int $day Day of the date
  145. */
  146. public function set_isodate( $year, $week, $day = null )
  147. {
  148. parent::setISODate( $year, $week, $day );
  149. return $this;
  150. }
  151. /**
  152. * Set the time of this object
  153. *
  154. * @see DateTime::setTime()
  155. * @param int $hour Hour of the time
  156. * @param int $minute Minute of the time
  157. * @param int $second Second of the time
  158. */
  159. public function set_time( $hour, $minute, $second = null )
  160. {
  161. parent::setTime( $hour, $minute, $second );
  162. return $this;
  163. }
  164. /**
  165. * Set the timezone for this datetime object. Can be either string
  166. * timezone identifier, or DateTimeZone object.
  167. *
  168. * @see DateTime::setTimezone()
  169. * @param mixed The timezone to use.
  170. * @return HabariDateTime $this object.
  171. */
  172. public function set_timezone( $timezone )
  173. {
  174. if ( ! $timezone instanceof DateTimeZone ) {
  175. $timezone = new DateTimeZone( $timezone );
  176. }
  177. parent::setTimezone( $timezone );
  178. return $this;
  179. }
  180. /**
  181. * Get the timezone identifier that is set for this datetime object.
  182. *
  183. * @return DateTimeZone The timezone object.
  184. */
  185. public function get_timezone()
  186. {
  187. return parent::getTimezone();
  188. }
  189. /**
  190. * Returns date formatted according to given format.
  191. *
  192. * @see DateTime::format()
  193. * @param string $format Format accepted by {@link http://php.net/date date()}.
  194. * @return string The formatted date, false on failure.
  195. */
  196. public function format( $format = null )
  197. {
  198. if ( $format === null ) {
  199. $format = self::$default_datetime_format;
  200. }
  201. return parent::format( $format );
  202. }
  203. /**
  204. * Returns date components inserted into a string
  205. *
  206. * Example:
  207. * echo HabariDateTime::date_create('2010-01-01')->text_format('The year was {Y}.');
  208. * // Expected output: The year was 2010.
  209. *
  210. * @param string $format A string with single-character date format codes {@link http://php.net/date date()} surrounded by braces
  211. * @return string The string with date components inserted
  212. */
  213. public function text_format( $format )
  214. {
  215. return preg_replace_callback( '%\{(\w)\}%iu', array( $this, 'text_format_callback' ), $format );
  216. }
  217. /**
  218. * Callback method for supplying replacements for HabariDatTime::text_format()
  219. *
  220. * @param array $matches The matches found in the regular expression.
  221. * @return string The date component value for the matched character.
  222. */
  223. private function text_format_callback( $matches )
  224. {
  225. return $this->format( $matches[1] );
  226. }
  227. /**
  228. * Alters the timestamp
  229. *
  230. * @param string $format A format accepted by {@link http://php.net/strtotime strtotime()}.
  231. * @return HabariDateTime $this object.
  232. */
  233. public function modify( $args )
  234. {
  235. parent::modify( $args );
  236. return $this;
  237. }
  238. /**
  239. * @see format()
  240. */
  241. public function get( $format = null )
  242. {
  243. return $this->format( $format );
  244. }
  245. /**
  246. * Echos date formatted according to given format.
  247. *
  248. * @see format()
  249. * @param string $format Format accepted by {@link http://php.net/date date()}.
  250. */
  251. public function out( $format = null )
  252. {
  253. echo $this->format( $format );
  254. }
  255. /**
  256. * Magic method called when this object is cast to string. Returns the
  257. * unix timestamp of this object.
  258. *
  259. * @return string The unix timestamp
  260. */
  261. public function __toString()
  262. {
  263. return $this->format( 'U' );
  264. }
  265. /**
  266. * Magic method to get magic ponies... properties, I mean.
  267. */
  268. public function __get( $property )
  269. {
  270. // if you add more cases to this list, please also add the repsective @property to the top of the class so it shows up propertly in IDEs!
  271. switch ( $property ) {
  272. case 'clone':
  273. return clone $this;
  274. case 'sql':
  275. return $this->format( 'U' );
  276. break;
  277. case 'int':
  278. return intval( $this->format( 'U' ) );
  279. break;
  280. case 'time':
  281. return $this->format( self::get_default_time_format() );
  282. break;
  283. case 'date':
  284. return $this->format( self::get_default_date_format() );
  285. break;
  286. case 'friendly':
  287. return $this->friendly();
  288. break;
  289. case 'fuzzy':
  290. return $this->fuzzy();
  291. break;
  292. default:
  293. $info = getdate( $this->format( 'U' ) );
  294. $info['mon0'] = substr( '0' . $info['mon'], -2, 2 );
  295. $info['mday0'] = substr( '0' . $info['mday'], -2, 2 );
  296. if ( isset( $info[$property] ) ) {
  297. return $info[$property];
  298. }
  299. return $this->$property;
  300. }
  301. }
  302. /**
  303. * Return the default date format, as set in the Options table
  304. *
  305. * @return The default date format
  306. **/
  307. public static function get_default_date_format()
  308. {
  309. return self::$default_date_format;
  310. }
  311. /**
  312. * Return the default time format, as set in the Options table
  313. *
  314. * @return The default time format
  315. **/
  316. public static function get_default_time_format()
  317. {
  318. return self::$default_time_format;
  319. }
  320. /**
  321. * Returns an associative array containing the date information for
  322. * this HabariDateTime object, as per {@link http://php.net/getdate getdate()}
  323. *
  324. * @return array Associative array containing the date information
  325. */
  326. public function getdate()
  327. {
  328. $info = getdate( $this->format( 'U' ) );
  329. $info['mon0'] = substr( '0' . $info['mon'], -2, 2 );
  330. $info['mday0'] = substr( '0' . $info['mday'], -2, 2 );
  331. return $info;
  332. }
  333. /**
  334. * Returns a friendlier string version of the time, ie: 3 days, 1 hour, and 5 minutes ago
  335. *
  336. * @param int $precision Only display x intervals. Note that this does not round, it only limits the display length.
  337. * @param boolean $include_suffix Include the 'ago' or 'from now' suffix?
  338. * @return string Time passed in the specified units.
  339. */
  340. public function friendly ( $precision = 7, $include_suffix = true )
  341. {
  342. $difference = self::difference( self::date_create(), $this );
  343. $result = array();
  344. if ( $difference['y'] ) {
  345. $result[] = sprintf( '%d %s', $difference['y'], _n( 'year', 'years', $difference['y'] ) );
  346. }
  347. if ( $difference['m'] ) {
  348. $result[] = sprintf( '%d %s', $difference['m'], _n( 'month', 'months', $difference['m'] ) );
  349. }
  350. if ( $difference['w'] ) {
  351. $result[] = sprintf( '%d %s', $difference['w'], _n( 'week', 'weeks', $difference['w'] ) );
  352. }
  353. if ( $difference['d'] ) {
  354. $result[] = sprintf( '%d %s', $difference['d'], _n( 'day', 'days', $difference['d'] ) );
  355. }
  356. if ( $difference['h'] ) {
  357. $result[] = sprintf( '%d %s', $difference['h'], _n( 'hour', 'hours', $difference['h'] ) );
  358. }
  359. if ( $difference['i'] ) {
  360. $result[] = sprintf( '%d %s', $difference['i'], _n( 'minute', 'minutes', $difference['i'] ) );
  361. }
  362. if ( $difference['s'] ) {
  363. $result[] = sprintf( '%d %s', $difference['s'], _n( 'second', 'seconds', $difference['s'] ) );
  364. }
  365. // limit the precision
  366. $result = array_slice( $result, 0, $precision );
  367. $result = Format::and_list( $result );
  368. if ( $include_suffix ) {
  369. if ( $difference['invert'] == true ) {
  370. $result = _t( '%s from now', array( $result ) );
  371. }
  372. else {
  373. $result = _t( '%s ago', array( $result ) );
  374. }
  375. }
  376. return $result;
  377. }
  378. /**
  379. * Similar to friendly(), but much more... fuzzy.
  380. *
  381. * Returns a very short version of the difference in time between now and the current HDT object.
  382. */
  383. public function fuzzy ()
  384. {
  385. $difference = self::date_create()->int - $this->int;
  386. if ( $difference < self::MINUTE ) {
  387. $result = _t( 'just now' );
  388. }
  389. else if ( $difference < self::HOUR ) {
  390. $minutes = round( $difference / self::MINUTE );
  391. $result = sprintf( _n( '%d minute ago', '%d minutes ago', $minutes ), $minutes );
  392. }
  393. else if ( $difference < self::DAY ) {
  394. $hours = round( $difference / self::HOUR );
  395. $result = sprintf( _n( '%d hour ago', '%d hours ago', $hours ), $hours );
  396. }
  397. else if ( $difference < self::WEEK ) {
  398. $days = round( $difference / self::DAY );
  399. $result = sprintf( _n( 'yesterday', '%d days ago', $days ), $days );
  400. }
  401. else if ( $difference < self::MONTH ) {
  402. $weeks = round( $difference / self::WEEK );
  403. $result = sprintf( _n( 'last week', '%d weeks ago', $weeks ), $weeks );
  404. }
  405. else if ( $difference < self::YEAR ) {
  406. $months = round( $difference / self::MONTH );
  407. $result = sprintf( _n( 'last month', '%d months ago', $months ), $months );
  408. }
  409. else {
  410. $years = round( $difference / self::YEAR );
  411. $result = sprintf( _n( 'last year', '%d years ago', $years ), $years );
  412. }
  413. return $result;
  414. }
  415. /**
  416. * Returns an array representing the difference between two times by interval.
  417. *
  418. * <code>
  419. * print_r( HabariDateTime::difference( 'now', 'January 1, 2010' ) );
  420. * // output (past): Array ( [invert] => [y] => 0 [m] => 9 [w] => 3 [d] => 5 [h] => 22 [i] => 33 [s] => 5 )
  421. * print_r( HabariDateTime::difference( 'now', 'January 1, 2011' ) );
  422. * // output (future): Array ( [invert] => 1 [y] => 0 [m] => 2 [w] => 0 [d] => 3 [h] => 5 [i] => 33 [s] => 11 )
  423. * </code>
  424. *
  425. * If 'invert' is true, the time is in the future (ie: x from now). If it is false, the time is in the past (ie: x ago).
  426. *
  427. * For more information, see PHP's DateInterval class, which this and friendly() attempt to emulate for < PHP 5.3
  428. *
  429. * @todo Add total_days, total_years, etc. values?
  430. *
  431. * @param mixed $start_date The start date, as a HDT object or any format accepted by HabariDateTime::date_create().
  432. * @param mixed $end_date The end date, as a HDT object or any format accepted by HabariDateTime::date_create().
  433. * @return array Array of each interval and whether the interval is inverted or not.
  434. */
  435. public static function difference( $start_date, $end_date )
  436. {
  437. // if the dates aren't HDT objects, try to convert them to one. this lets you pass in just about any format
  438. if ( !$start_date instanceof HabariDateTime ) {
  439. $start_date = HabariDateTime::date_create( $start_date );
  440. }
  441. if ( !$end_date instanceof HabariDateTime ) {
  442. $end_date = HabariDateTime::date_create( $end_date );
  443. }
  444. $result = array();
  445. // calculate the difference, in seconds
  446. $difference = $end_date->int - $start_date->int;
  447. if ( $difference < 0 ) {
  448. // if it's negative, time AGO
  449. $result['invert'] = false;
  450. }
  451. else {
  452. // if it's positive, time UNTIL
  453. $result['invert'] = true;
  454. }
  455. $difference = abs( $difference );
  456. // we'll progressively subtract from the seconds left, so initialize it
  457. $seconds_left = $difference;
  458. $result['y'] = floor( $seconds_left / self::YEAR );
  459. $seconds_left = $seconds_left - ( $result['y'] * self::YEAR );
  460. $result['m'] = floor( $seconds_left / self::MONTH );
  461. $seconds_left = $seconds_left - ( $result['m'] * self::MONTH );
  462. $result['w'] = floor( $seconds_left / self::WEEK );
  463. $seconds_left = $seconds_left - ( $result['w'] * self::WEEK );
  464. $result['d'] = floor( $seconds_left / self::DAY );
  465. $seconds_left = $seconds_left - ( $result['d'] * self::DAY );
  466. $result['h'] = floor( $seconds_left / self::HOUR );
  467. $seconds_left = $seconds_left - ( $result['h'] * self::HOUR );
  468. $result['i'] = floor( $seconds_left / self::MINUTE );
  469. $seconds_left = $seconds_left - ( $result['i'] * self::MINUTE );
  470. $result['s'] = $seconds_left;
  471. return $result;
  472. }
  473. }
  474. ?>