如何在PHP中检测不明确和无效的日期时间?

如何在PHP中检测不明确和无效的日期时间?,php,datetime,timezone,dst,Php,Datetime,Timezone,Dst,当处理用户提供的本地DateTime值时,由于夏令时转换,很可能存在无效或不明确的时间 在其他语言和框架中,对于时区的某些表示,通常有一些方法,例如isAmbiguous和isValid。例如,在.NET中,有和 许多其他时区实现都有类似的方法或功能来解决这个问题。例如,在Python中,将抛出一个可以捕获的模糊性MeError或InvalidTimeError异常 PHP有很好的时区支持,但我似乎找不到任何东西来解决这个问题。我能找到的最接近的东西是。这提供了原始数据,所以我可以看到一些方法可

当处理用户提供的本地
DateTime
值时,由于夏令时转换,很可能存在无效或不明确的时间

在其他语言和框架中,对于时区的某些表示,通常有一些方法,例如
isAmbiguous
isValid
。例如,在.NET中,有和

许多其他时区实现都有类似的方法或功能来解决这个问题。例如,在Python中,将抛出一个可以捕获的
模糊性MeError
InvalidTimeError
异常

PHP有很好的时区支持,但我似乎找不到任何东西来解决这个问题。我能找到的最接近的东西是。这提供了原始数据,所以我可以看到一些方法可以写在这上面。但它们是否已经存在于某个地方?如果没有,谁能提供一个好的实现?我希望他们能像这样工作:

$tz=新日期时区(“美国/纽约”);
echo$tz->isValidTime(新日期时间('2013-03-10 02:00:00'))#假的
echo$tz->isambiguustime(新日期时间('2013-11-03 01:00:00'))#真的

我不知道有任何现有的实现,我还没有理由使用这些高级日期/时间功能,所以这里是一个干净的房间实现

为了启用问题中说明的语法,我们将扩展
DateTimeZone
,如下所示:

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

    // DateTime instead of DateTimeInterface for PHP < 5.5
    public function isValidTime(DateTimeInterface $date);

    public function isAmbiguousTime(DateTimeInterface $date);
}
但是,相反地:

$tz = new DateTimeZoneEx('America/New_York');
echo $tz->isValidTime(new DateTime('2013-03-10 02:00:00'));
$tz = new DateTimeZoneEx('America/New_York');
echo $tz->isValidTime(new DateTime('2013-03-10 02:00:00', $tz));
当然,由于对象已经知道
$tz
$this
,因此应该很容易扩展这些方法,以便删除此要求。在任何情况下,使界面超级用户友好都超出了本回答的范围;接下来,我将重点介绍技术细节

isValidTime
这里的想法是用来查看我们感兴趣的日期/时间是否有任何转换
getTransitions
将返回包含一个或两个元素的数组;“begin”时间戳的时区情况将始终存在,如果在它之后不久发生转换,则另一个元素将存在。
maxdst_SHIFT
的值足够小,不可能获得第二个转换/第三个元素

让我们看看代码:

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) {
        // No DST changes around here, so obviously $date is valid
        return true;
    }

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

    if ($shift < 0) {
        // The clock moved backward, so obviously $date is valid
        // (although it might be ambiguous)
        return true;
    }

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

    return $compare->modify("$shift seconds")->getTimestamp() != $ts;
}
最后一点取决于PHP日期函数的另一个实现细节:当要求为不明确的日期/时间生成时间戳时,PHP会生成第一次(以绝对时间表示)出现的时间戳。这意味着给定DST更改的最新模糊时间和最早非模糊时间的时间戳的差值将大于DST偏移量(具体而言,差值将在[
offset+1
2*offset
]范围内,
offset
是绝对值)

该实现利用了这一点,再次向前执行“挂钟移位”,并检查结果和输入之间的时间戳差异
$date


我不知道有任何现有的实现,而且我还没有理由使用这些高级日期/时间功能,所以这里是一个无尘室实现

为了启用问题中说明的语法,我们将扩展
DateTimeZone
,如下所示:

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

    // DateTime instead of DateTimeInterface for PHP < 5.5
    public function isValidTime(DateTimeInterface $date);

    public function isAmbiguousTime(DateTimeInterface $date);
}
但是,相反地:

$tz = new DateTimeZoneEx('America/New_York');
echo $tz->isValidTime(new DateTime('2013-03-10 02:00:00'));
$tz = new DateTimeZoneEx('America/New_York');
echo $tz->isValidTime(new DateTime('2013-03-10 02:00:00', $tz));
当然,由于对象已经知道
$tz
$this
,因此应该很容易扩展这些方法,以便删除此要求。在任何情况下,使界面超级用户友好都超出了本回答的范围;接下来,我将重点介绍技术细节

isValidTime
这里的想法是用来查看我们感兴趣的日期/时间是否有任何转换
getTransitions
将返回包含一个或两个元素的数组;“begin”时间戳的时区情况将始终存在,如果在它之后不久发生转换,则另一个元素将存在。
maxdst_SHIFT
的值足够小,不可能获得第二个转换/第三个元素

让我们看看代码:

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) {
        // No DST changes around here, so obviously $date is valid
        return true;
    }

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

    if ($shift < 0) {
        // The clock moved backward, so obviously $date is valid
        // (although it might be ambiguous)
        return true;
    }

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

    return $compare->modify("$shift seconds")->getTimestamp() != $ts;
}
最后一点取决于PHP日期函数的另一个实现细节:当要求为不明确的日期/时间生成时间戳时,PHP会生成第一次(以绝对时间表示)出现的时间戳。这意味着给定DST更改的最新模糊时间和最早非模糊时间的时间戳的差值将大于DST偏移量(具体而言,差值将在[
offset+1
2*offset
]范围内,
offset
是绝对值)

该实现利用了这一点,再次向前执行“挂钟移位”,并检查结果和输入之间的时间戳差异
$date


非常好而且完整的答案!我希望所有的答案都是经过深思熟虑和描述的!只要StackOverflow允许我,我会立即奖励赏金。这正是我想要的。赏金。再次感谢@马特森:谢谢,很高兴能帮上忙。回答得很好,很完整!我希望所有的答案都是经过深思熟虑和描述的!只要StackOverflow允许我,我会立即奖励赏金。这正是我想要的。赏金。再次感谢@马特森:谢谢,很高兴能帮忙。