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