当前位置: 首页 > 知识库问答 >
问题:

如何在PHP中检测不明确和无效的DateTime?

赫连捷
2023-03-14

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

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

php prettyprint-override">$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

共有1个答案

甄云
2023-03-14

我不知道任何现有的实现,而且我还没有理由使用像这些这样的高级日期/时间特性,所以这里是一个干净的实现。

为了启用问题中说明的语法,我们将扩展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,因此应该很容易扩展这些方法,从而消除这个需求。无论如何,使界面超级用户友好不在本答案的范围之内;接下来,我将集中讨论技术细节。

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;
}

不难看出如何利用这一事实:创建一个新的datetime实例,该实例等于输入的$date,然后以挂钟时间为单位将其前移,以秒为单位将其前移等于DST移位(在进行此调整时必须不考虑DST)。如果结果的时间戳(这里使用DST规则)等于输入的时间戳,那么输入是无效的日期/时间。

该实现与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+12*offset],其中offset是绝对值)。

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

请参见实际操作中的代码。

 类似资料:
  • 问题内容: 我正在使用PHP中的一维数组。我想检测重复值的存在,然后计算重复值的数量并输出结果。例如,给定以下数组: 我想打印: 关于如何解决这个问题有什么建议吗? 谢谢。 麦克风 问题答案: 您可以使用array_count_values函数 将输出

  • 在一个小行星游戏中,我在旋转精灵时尝试使用XNA进行2D碰撞检测时遇到了一些问题。在子弹击中小行星之前,碰撞一直在发生(看起来是从各个方向),当飞船撞上小行星时,碰撞也是不准确的。我使用一个矩阵来旋转飞船发射的子弹,然后是一个小行星的矩阵,并且在一个Xbox独立游戏论坛上使用了每像素碰撞检测样本。 这是我的子弹矩阵…原点和位置是子弹的矢量2属性 和小行星... 我是编程新手,在过去的几天里,我一直

  • 我的应用程序中有回收器视图。UI类似于Google Play Store应用程序。它有两个视图寻呼机和项目的列表和网格以交替的方式。所有数据都是从web服务中提取的,并在两个API调用中被分叉。列表和网格的数据是从另一个API填充的。问题是,当我快速滚动recyclerview时,我会遇到这种崩溃。滚动recyclerview时,用于在列表/网格中加载数据的API调用来自。阅读了许多关于这个主题的

  • 关于测试XML标记的正确嵌套的一个问题: null 发现/lastname标记在其父标记之外的代码或方向是什么,父标记是to,/to pair? 干杯。

  • 问题内容: 我想说出JS中有效和无效日期对象之间的区别,但不知道如何: 有编写函数的想法吗? 建议使用Ash 来分析日期字符串,这为检查日期字符串是否有效提供了一种权威方法。 如果可能的话,我希望我的API接受Date实例,并能够检查/确认它是否有效。Borgar的解决方案可以做到这一点,但是我需要在浏览器中对其进行测试。我也想知道是否有更优雅的方法。 Ash使我考虑完全不让我的API接受实例,这

  • 我尝试使用request.meta['http_user_agent']来检测浏览器。但这将返回我测试时的浏览器类型列表 **我无法得到它返回值的模式。谁能告诉我如何得到浏览器名称