timestamp = $time; $this->valid = $time !== false; } // read the object's timestamp in one of a variety of formats public function __get ($prop) { switch ($prop) { // unix timestamp (ie: 10000001) case 'timestamp': case 'valid': return $this->$prop; // date in mysql '2007-12-25' format case 'mysql_date': return $this->valid ? date('Y-m-d', $this->timestamp) : false; // time in mysql '13:14:15' format case 'mysql_time': return $this->valid ? date('H:i:s', $this->timestamp) : false; // date/time in mysql '2007-12-25 13:14:15' format case 'mysql_datetime': return $this->valid ? date('Y-m-d H:i:s', $this->timestamp) : false; default: return false; } } // modify the timestamp using a delta // ie: $date->modify('+12 hours') public function modify ($delta) { // if delta starts with "now"/"today", force delta to be from NOW, not the existing timestamp if (preg_match('/^now|today/', $delta)) $this->timestamp = strtotime($delta, time()); // otherwise, delta applies to the existing timestamp else $this->timestamp = strtotime($delta, $this->timestamp); $this->valid = $this->timestamp !== false; } // diff this object with another Date object public function diff (Date $time) { return new DateDiff($this, $time); } } /** * diff's two timestamps, providing access to the diff info */ class DateDiff { private $yrs = 0, $mnths = 0, $days = 0, $hrs = 0, $mins = 0, $secs = 0; // pass in 2 Date objects to run the diff; object properties populated with diff values public function __construct (Date $t1, Date $t2) { if (! $t1->valid || ! $t2->valid) return; $t1 = $t1->timestamp; $t2 = $t2->timestamp; // make sure that $t2 > $t1 if ($t1 > $t2) list($t1, $t2) = array($t2, $t1); // secs $s2 = (int)date('s', $t2); while ( ($s1 = (int)date('s', $t1)) !== $s2 ) { $t1 = strtotime('+1 second', $t1); $this->secs++; } // mins $s2 = (int)date('i', $t2); while ( ($s1 = (int)date('i', $t1)) !== $s2) { $t1 = strtotime('+1 minute', $t1); $this->mins++; } // hrs $s2 = date('G', $t2); while ( ($s1 = date('G', $t1)) !== $s2 ) { $t1 = strtotime('+1 hour', $t1); $this->hrs++; } // days $s2 = date('j', $t2); while ( ($s1 = date('j', $t1)) !== $s2 ) { $t1 = strtotime('+1 day', $t1); $this->days++; } // mnths $s2 = date('n', $t2); while ( ($s1 = date('n', $t1)) !== $s2 ) { $t1 = strtotime('+1 month', $t1); $this->mnths++; } // yrs $s2 = date('Y', $t2); while ( ($s1 = date('Y', $t1)) < $s2 ) { $t1 = strtotime('+1 year', $t1); $this->yrs++; } } /** * format the diff according to preference; returns a string like "4 hrs, 3 mins, 2 secs" * * precision is number of time pieces to display (ie: 2 = yrs, mnths or mins, secs) * when abbr=true, identifiers are short (yrs/hrs) rather than full (years/hours) */ public function format_diff ($precision = 6, $abbr = false) { $stack = array( array($this->yrs, ($abbr ? 'yr' : 'year') . ($this->yrs == 1 ? '' : 's')), array($this->mnths, ($abbr ? 'mnth' : 'month') . ($this->mnths == 1 ? '' : 's')), array($this->days, 'day' . ($this->days == 1 ? '' : 's')), array($this->hrs, ($abbr ? 'hr' : 'hour') . ($this->hrs == 1 ? '' : 's')), array($this->mins, ($abbr ? 'min' : 'minute') . ($this->mins == 1 ? '' : 's')), array($this->secs, ($abbr ? 'sec' : 'second') . ($this->secs == 1 ? '' : 's')) ); while (count($stack) > 0 && $stack[0][0] == 0) array_splice($stack, 0, 1); array_splice($stack, $precision); $ret = array(); foreach ($stack as $item) $ret[] = "{$item[0]} {$item[1]}"; return implode(', ', $ret); } } $t1 = new Date('2008-01-29 18:09:06'); $t2 = new Date(); $diff = $t1->diff($t2); echo $diff->format_diff(3), "\n"; ?>