gpt4 book ai didi

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

转载 作者:可可西里 更新时间:2023-11-01 12:17:56 25 4
gpt4 key购买 nike

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

在其他语言和框架中,时区的一些表示通常有isAmbiguousisValid 等方法。例如在 .NET 中,有 TimeZoneInfo.IsAmbiguousTimeTimeZoneInfo.IsInvalidTime .

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

PHP 具有出色的时区支持,但我似乎找不到解决这个问题的方法。我能找到的最接近的是 DateTimeZone::getTransitions .它提供了原始数据,因此我可以看到可以在此基础上编写一些方法。但是它们是否已经存在于某个地方?如果没有,谁能提供一个好的实现?我希望他们能像这样工作:

$tz = new DateTimeZone('America/New_York');
echo $tz->isValidTime(new DateTime('2013-03-10 02:00:00')); # false
echo $tz->isAmbiguousTime(new DateTime('2013-11-03 01:00:00')); # true

最佳答案

我不知道有任何现有的实现,而且我还没有理由使用诸如此类的高级日期/时间功能,所以这里是一个洁净室实现。

为了启用问题中说明的语法,我们将按如下方式扩展 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);
}

为了避免分散注意力的细节使实现变得困惑,我将假设 $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,因此扩展方法应该很容易,以便删除此要求。无论如何,让界面 super 用户友好不在这个答案的范围内;展望 future ,我将专注于技术细节。

isValidTime

这里的想法是使用getTransitions查看我们感兴趣的日期/时间附近是否有任何转换。getTransitions 将返回一个包含一个或两个元素的数组; “开始”时间戳的时区情况将始终存在,如果在它之后不久发生转换,则另一个元素将存在。 MAX_DST_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 的日期函数计算无效日期/时间的时间戳这一事实,就好像挂钟时间没有偏移一样。也就是说,为 2013-03-10 02:30:002013-03-10 03:30:00 计算的时间戳在纽约时区是相同的.

不难看出如何利用这一事实:创建一个等于输入 $date 的新 DateTime 实例,然后将其向前移动 挂钟时间术语 等于以秒为单位的 DST 偏移量(必须不考虑 DST 才能进行此调整)。如果结果的时间戳(此处 DST 规则起作用)等于输入的时间戳,则输入是无效的日期/时间。

isAmbiguousTime

实现与isValidTime非常相似,只有几个细节变化:

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) {
// The clock moved forward, so obviously $date is not ambiguous
// (although it might be invalid)
return false;
}

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

最后一点取决于 PHP 日期函数的另一个实现细节:当要求为不明确的日期/时间生成时间戳时,PHP 会生成第一次(以绝对时间形式)出现的时间戳。这意味着给定 DST 更改的最新模糊时间和最早非模糊时间的时间戳将相差大于 DST 偏移量(具体而言,差异将在 [ offset + 1, 2 * offset], 其中offset 是一个绝对值)。

该实现通过再次向前“挂钟移动”并检查结果与输入 $date 之间的时间戳差异来利用这一点。

See the code in action .

关于php - 如何在 PHP 中检测不明确和无效的日期时间?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18409405/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com