如何在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允许我,我会立即奖励赏金。这正是我想要的。赏金。再次感谢@马特森:谢谢,很高兴能帮忙。