<?php

$tz = new DateTimeZoneEx('America/New_York');

echo "\nTesting isValidTime:\n";

$dates = [
	'2013-03-10 01:59:59',
	'2013-03-10 02:00:00',
	'2013-03-10 02:59:59',
	'2013-03-10 03:00:00',
];

foreach ($dates as $date) {
	$result = $tz->isValidTime(new DateTime($date, $tz));
	echo "isValidTime($date) = ".($result ? "true" : "false")."\n";
}

echo "\nTesting isAmbiguousTime:\n";

$dates = [
	'2013-11-03 00:59:59',
	'2013-11-03 01:00:00',
	'2013-11-03 01:59:59',
	'2013-11-03 02:00:00',
];

foreach ($dates as $date) {
	$result = $tz->isAmbiguousTime(new DateTime($date, $tz));
	echo "isAmbiguousTime($date) = ".($result ? "true" : "false")."\n";
}


class DateTimeZoneEx extends DateTimeZone
{
    const MAX_DST_SHIFT = 7200; // let's be generous

    public function isValidTime(DateTime $date)
    {

        $ts = $date->getTimestamp();
        $transitions = $this->getTransitions($ts - self::MAX_DST_SHIFT, $ts + self::MAX_DST_SHIFT);

        if (count($transitions) == 1) {
            return true;
        }

        $shift = $transitions[1]['offset'] - $transitions[0]['offset'];

        if ($shift < 0) {
            return true;
        }

        $compare = new DateTime($date->format('Y-m-d H:i:s'), $this);

        return $compare->modify("$shift seconds")->getTimestamp() != $ts;
    }

    public function isAmbiguousTime(DateTime $date)
    {
        $ts = $date->getTimestamp();
        $transitions = $this->getTransitions($ts - self::MAX_DST_SHIFT, $ts + self::MAX_DST_SHIFT);

        if (count($transitions) == 1) {
            return false;
        }

        $shift = $transitions[1]['offset'] - $transitions[0]['offset'];

        if ($shift > 0) {
            return false;
        }

        $shift = -$shift;
        $compare = new DateTime($date->format('Y-m-d H:i:s'), $this);
        return $compare->modify("$shift seconds")->getTimestamp() - $ts > $shift;
    }
}
