Speeding up Zend_Date

A working lazy proxy approach for Zend_Date

Zend_Date is a mighty class for handling date and time. As for all of such mighty tools, there is a heavy downside: it's really expensive at runtime. Using a Zend_Date instance a few times is no big deal, but using a hundred instances and generating thousands of ISO-style dates and times may take several seconds.

I'm currently working with Zend_Date. It is used to get rid of datasource-dependent date and time calculation. Instead of using MySQL's "SELECT NOW()", I use Zend_Date::now(). Zend_Date enables me to use datetime features on any data source.

In my case, objects contain a property called createTime which originally was a Zend_Date and initialized to Zend_Date::now(). The Zend_Date instance was created no matter if I really wanted to use it. I therefore wrote a simple lazy wrapper (a partial proxy) to reduce the load. And here is the code:

PHP source code:

/**
* xDate wrapps Zend_Date. It adds lazyness, so the many dates
* our objects are using will consume performance only if
* really required. Published first at www.trash-factor.com
*/
class xDate {
	private $_isoDate; // iso date string like 'YYYY-MM-dd HH:mm:ss'
	private $_zendDate; // Zend_Date instantiated on demand
 
	public function __construct($value=null) {
		if (!is_null($value)) {
			$this->set($value); // initialize with $value
		}
	}
 
	public function __toString() {
		// lazyness: create the _isoDate once and cache the result
		if (is_null($this->_isoDate)) {
			// build the ISO date string
			$zendDate = $this->getZendDate();
			$this->_isoDate = $zendDate->toString('YYYY-MM-dd HH:mm:ss');
		}
		return $this->_isoDate;
	}
 
	public function set($value) {
		if ($value instanceof Zend_Date) {
			$this->_zendDate = $value;
			$this->_isoDate = null; // ISO date becomes invalid
		}else{
			$this->_zendDate = null; // Zend_Date becomes invalid
			$this->_isoDate = $value;
		}
	}
 
	public function addSecond($seconds) {
		$this->_isoDate = null;
		$zendDate = $this->getZendDate();
		return $zendDate->addSecond($seconds);
	}
 
	public function subSecond($seconds) {
		$this->_isoDate = null;
		$zendDate = $this->getZendDate();
		return $zendDate->subSecond($seconds);
	}
 
 
	public function compare($value) {
		$zendDate = $this->getZendDate();
		return $zendDate->compare($value);
	}
 
	public function sub($value) {
		$this->_isoDate = null;
		$zendDate = $this->getZendDate();
		return $zendDate->sub($value);
	}
 
	/**
	* Get the current Zend_Date instance.
	* Instantiate the Zend_Date on demand and cache it.
	* So it's created once and maintained for later use.
	*/
	private function getZendDate() {
		if (is_null($this->_zendDate)) {
			if (is_null($this->_isoDate)) {
				$this->_zendDate = Zend_Date::now();
			}else{
				$this->_zendDate = new Zend_Date($this->_isoDate, Zend_Date::ISO_8601);
			}
		}
		return $this->_zendDate;
	}
}

This class can increase, decrease and compare dates. It can easily be extended to support more of the Zend_Date behavior. It may also be optimized in several ways, but for this posting it was more important to keep it as simple as possible.

Feel free to use, adapt and modify the code: it's free of intellectual property.