Is Zend_Date slow?
Well, it’s always easy to state that something is slow, so let me be a bit more precise: If you need to do a lot of complex date/time operations in you applications, Zend_Date might turn out to be the major bottleneck. If you just need localization for some date’s Zend_Date presumably won’t be your problem.
In the Calendar app of Tine 2.0 it turned out, that Zend_Date consumes 66% of the time of a Month view Request in a realistic szenario.
Just bashing Zend_Date would be to easy, as it has a remarkable feature set. Most notably in my view:
- solves the year 2038 problem on 32 bit systems
- full timezone support
- full localization support
- full support for ISO 8601 date string identifiers
As Date/Time is a really complex topic, here my full acknowledgement for this feature set at a time when PHP couldn’t help on all this.
On the other hand you don’t always need all the nice features it has, but you might want to use Zend_Date as generic date/time abstraction all over your applications. In this case Zend_Date is slow and has a lot of potential for improvements.
Patching Zend_Date
Zend_Date::__construct($date, $format, $locale)
Zend_Dates could be created from a date string in an arbitrary format and locale. This is great if you need to parse custom date strings.
When dealing with Zend_Date in backend operations, you normally only use very few date/time representation formats. e.g. ‘yyyy-MM-dd HH:mm:ss’ which is the mysql date/time format.
For this kind of input Zend_Date has no optimization. An easy performance patch for this problem could be:
if ($format === 'yyyy-MM-dd HH:mm:ss') {
$matches = array();
preg_match("/^(\d{4})-(\d{2})-(\d{2})[T ]{1}(\d{2}):(\d{2}):(\d{2})/", $_ISO, $matches);
if (count($matches) == 7) {
list($match, $year, $month, $day, $hour, $minute, $second) = $matches;
// NOTE: PHP5 timestamp support is 32 bit and ends on 2038-01-19 03:14:07
if ($year < 2038) {
$date = mktime($hour, $minute, $second, $month, $day, $year);
$format = Zend_Date::TIMESTAMP
}
}
}
Zend_Date::get($part, $locale)
When converting Zend_Dates to strings, the same as above applies. Here an easy patch could be:
if (! $part) {
$part = self::TIMESTAMP;
}
if (array_key_exists($part, self::$_dateMap)) {
$dt = new DateTime('@' . $this->getUnixTimestamp());
$dt->setTimezone(new DateTimeZone($this->getTimezone()));
$s = $dt->format(self::$_dateMap[$part]);
switch($part) {
case 'm':
$s = (int) preg_replace('/^0/', '', $s);
break;
}
return $s;
}
with this dateMap
private static $_dateMap = array(
'yyyy-MM-dd HH:mm:ss' => 'Y-m-d H:i:s',
'MM' => 'm',
'm' => 'i', // spechial handling required!
'M' => 'n',
'd' => 'j',
'h' => 'g',
'H' => 'G',
'HH' => 'H',
's' => 's',
'I' => 'I',
'z' => 'T',
'U' => 'U',
'eee' => 'N',
'D' => 'z',
'e' => 'w',
'X' => 'Z'
);
Zend_Date_DateObject::setTimezone($zone)
As ZF requires PHP 5.2.4 and above, we can use native PHP functions to improve setTimezone:
public function setTimezone($zone = null)
{
try {
$dtz = new DateTimeZone($zone);
$this->_offset = $dtz->getOffset(new DateTime('1970-02-01 00:00:00'));
$this->_timezone = $zone;
} catch (Exception $e) {
require_once 'Zend/Date/Exception.php';
throw new Zend_Date_Exception("timezone ($zone) is not a known timezone", $zone);
}
if (($zone == 'UTC') or ($zone == 'GMT')) {
$this->_dst = false;
} else {
$this->_dst = true;
}
return $this;
}
This are just 3 very easy patches, but for a Tine 2.0 Calendar month view request it saves 41% of the request time!
Avoiding Zend_Date
Derick Rethans contributed a great DateTime class to PHP which is in included beginning with version 5.2 which got even more improved in version 5.3. This class solves the year 2038 problem, has full timezone support.

So my first idea was to write a new Zend_Date class which requires PHP version >= 5.3 to be used as a drop in replacement. Unfortunately it turned out, that the design of Zend_Date is not compatible with the DateTime, or to be more precise, the new class would also be slow due to two base restrictions in Zend_Date:
- ISO representation for date string identifiers
- Zend_Date also represents date intervals which have a separate class in PHP.
At the end I implemented on own wrapper class around DateTime which supports some old Zend_Date signatures, so that we don’t had to edit all our code. Zend_Date is only used when Server side locale handling is needed, like in exports.
Switching from Zend_Date to native DateTime brought us a speed up of 66% in a calendar month view request.