<?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;
}
}
PD9waHAKCiR0eiA9IG5ldyBEYXRlVGltZVpvbmVFeCgnQW1lcmljYS9OZXdfWW9yaycpOwoKZWNobyAiXG5UZXN0aW5nIGlzVmFsaWRUaW1lOlxuIjsKCiRkYXRlcyA9IFsKCScyMDEzLTAzLTEwIDAxOjU5OjU5JywKCScyMDEzLTAzLTEwIDAyOjAwOjAwJywKCScyMDEzLTAzLTEwIDAyOjU5OjU5JywKCScyMDEzLTAzLTEwIDAzOjAwOjAwJywKXTsKCmZvcmVhY2ggKCRkYXRlcyBhcyAkZGF0ZSkgewoJJHJlc3VsdCA9ICR0ei0+aXNWYWxpZFRpbWUobmV3IERhdGVUaW1lKCRkYXRlLCAkdHopKTsKCWVjaG8gImlzVmFsaWRUaW1lKCRkYXRlKSA9ICIuKCRyZXN1bHQgPyAidHJ1ZSIgOiAiZmFsc2UiKS4iXG4iOwp9CgplY2hvICJcblRlc3RpbmcgaXNBbWJpZ3VvdXNUaW1lOlxuIjsKCiRkYXRlcyA9IFsKCScyMDEzLTExLTAzIDAwOjU5OjU5JywKCScyMDEzLTExLTAzIDAxOjAwOjAwJywKCScyMDEzLTExLTAzIDAxOjU5OjU5JywKCScyMDEzLTExLTAzIDAyOjAwOjAwJywKXTsKCmZvcmVhY2ggKCRkYXRlcyBhcyAkZGF0ZSkgewoJJHJlc3VsdCA9ICR0ei0+aXNBbWJpZ3VvdXNUaW1lKG5ldyBEYXRlVGltZSgkZGF0ZSwgJHR6KSk7CgllY2hvICJpc0FtYmlndW91c1RpbWUoJGRhdGUpID0gIi4oJHJlc3VsdCA/ICJ0cnVlIiA6ICJmYWxzZSIpLiJcbiI7Cn0KCgpjbGFzcyBEYXRlVGltZVpvbmVFeCBleHRlbmRzIERhdGVUaW1lWm9uZQp7CiAgICBjb25zdCBNQVhfRFNUX1NISUZUID0gNzIwMDsgLy8gbGV0J3MgYmUgZ2VuZXJvdXMKCiAgICBwdWJsaWMgZnVuY3Rpb24gaXNWYWxpZFRpbWUoRGF0ZVRpbWUgJGRhdGUpCiAgICB7CgogICAgICAgICR0cyA9ICRkYXRlLT5nZXRUaW1lc3RhbXAoKTsKICAgICAgICAkdHJhbnNpdGlvbnMgPSAkdGhpcy0+Z2V0VHJhbnNpdGlvbnMoJHRzIC0gc2VsZjo6TUFYX0RTVF9TSElGVCwgJHRzICsgc2VsZjo6TUFYX0RTVF9TSElGVCk7CgogICAgICAgIGlmIChjb3VudCgkdHJhbnNpdGlvbnMpID09IDEpIHsKICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQoKICAgICAgICAkc2hpZnQgPSAkdHJhbnNpdGlvbnNbMV1bJ29mZnNldCddIC0gJHRyYW5zaXRpb25zWzBdWydvZmZzZXQnXTsKCiAgICAgICAgaWYgKCRzaGlmdCA8IDApIHsKICAgICAgICAgICAgcmV0dXJuIHRydWU7CiAgICAgICAgfQoKICAgICAgICAkY29tcGFyZSA9IG5ldyBEYXRlVGltZSgkZGF0ZS0+Zm9ybWF0KCdZLW0tZCBIOmk6cycpLCAkdGhpcyk7CgogICAgICAgIHJldHVybiAkY29tcGFyZS0+bW9kaWZ5KCIkc2hpZnQgc2Vjb25kcyIpLT5nZXRUaW1lc3RhbXAoKSAhPSAkdHM7CiAgICB9CgogICAgcHVibGljIGZ1bmN0aW9uIGlzQW1iaWd1b3VzVGltZShEYXRlVGltZSAkZGF0ZSkKICAgIHsKICAgICAgICAkdHMgPSAkZGF0ZS0+Z2V0VGltZXN0YW1wKCk7CiAgICAgICAgJHRyYW5zaXRpb25zID0gJHRoaXMtPmdldFRyYW5zaXRpb25zKCR0cyAtIHNlbGY6Ok1BWF9EU1RfU0hJRlQsICR0cyArIHNlbGY6Ok1BWF9EU1RfU0hJRlQpOwoKICAgICAgICBpZiAoY291bnQoJHRyYW5zaXRpb25zKSA9PSAxKSB7CiAgICAgICAgICAgIHJldHVybiBmYWxzZTsKICAgICAgICB9CgogICAgICAgICRzaGlmdCA9ICR0cmFuc2l0aW9uc1sxXVsnb2Zmc2V0J10gLSAkdHJhbnNpdGlvbnNbMF1bJ29mZnNldCddOwoKICAgICAgICBpZiAoJHNoaWZ0ID4gMCkgewogICAgICAgICAgICByZXR1cm4gZmFsc2U7CiAgICAgICAgfQoKICAgICAgICAkc2hpZnQgPSAtJHNoaWZ0OwogICAgICAgICRjb21wYXJlID0gbmV3IERhdGVUaW1lKCRkYXRlLT5mb3JtYXQoJ1ktbS1kIEg6aTpzJyksICR0aGlzKTsKICAgICAgICByZXR1cm4gJGNvbXBhcmUtPm1vZGlmeSgiJHNoaWZ0IHNlY29uZHMiKS0+Z2V0VGltZXN0YW1wKCkgLSAkdHMgPiAkc2hpZnQ7CiAgICB9Cn0K