使用YYYY-MM-DD格式验证日期的PHP正则表达式

7

我正在尝试创建一个日期正则表达式验证器。我的问题是,我正在使用一个"日期"类型的输入字段,在Chrome中运作得非常好;它在Chrome中打开类似于日历的界面,但在其他浏览器中却没有反应,因此我决定手动输入日期。

这是我抛出错误的信息(我正在寻找YYYY-MM-DD格式):

$date_regex ='#^(19|20)\d\d[\- /.](0[1-9]|1[012])[\- /.](0[1-9]|[12][0-9]|3[01])$#';
$hiredate = $_POST['hiredate'];
if (!preg_match($date_regex, $hiredate)){
    $errors[] = 'Your hire date entry does not match the YYYY-MM-DD required format.';
}

我知道这方面已经有很多例子了,但是我已经尝试了大概20个例子,却仍然无法解决问题。也许我漏掉了什么。

如果有必要的话,这是输入字段:

<input type="date" name="hiredate" />

1
为什么不使用“-”分隔符并改用checkdate()函数呢? - Wayne Whitty
有标准的流程吗?我在另一篇帖子中找到了它,但对我没有用。顺便说一下,我想让他们插入像YYYY/MM/DD或YYYY.MM.DD或YYYY-MM-DD这样的日期格式,以免束缚他们太多。 - Cataneo
3
在后端将适当的maxlength和只接受数字的验证连接成正确格式之前,提供3个独立的<input>框是否更容易?您可以为每个输入框添加侦听器,以在用户“按下”2个数字时进入下一个输入框。对于用户而言,他们并不关心您需要哪种格式,如果不是他们个人的本地格式,最好不要引起他们的沮丧。 - MonkeyZeus
我其实也考虑过这个问题,但是考虑到有些月份有28-29天,而有些月份有31天,这样会不会让事情变得更加复杂呢?我猜正则表达式应该没问题,因为我在很多帖子中都看到了它。也许我的错误处理方式完全错误了...但你可能说得对。 - Cataneo
我不明白。你具体是什么意思?实际上,不支持的浏览器是我的问题。有时候我会使用-webkit-或-moz-来编写代码以兼容多个浏览器,但现在我想只有Chrome可以帮助我。 - Cataneo
显示剩余6条评论
3个回答

15

不要使用正则表达式来实现这个功能,你可以使用DateTime::createFromFormat函数得到相同的结果。

// specify your date's original format, in this example m/d/Y (e.g. 08/31/2013)
$format = "m/d/Y";
$hireDate = DateTime::createFromFormat($format, $_POST['hiredate']);
if(!$hireDate) {
 // createFromFormat returns false if the format is invalid;
} else {
   //change it to any format you want with format() (e.g. 2013-08-31)
   echo $hireDate->format("Y-m-d");
}

您可以在此处阅读更多信息:

http://php.net/manual/zh/datetime.createfromformat.php

但问题似乎与PHP完全无关。

PHP运行在后端,而您似乎有一个前端问题。

我还怀疑问题不在于您使用的输入类型。如果一个浏览器不支持您指定的输入类型,则会默认为文本。请参见此处:

http://jsfiddle.net/FKGCA/

我的浏览器不知道<input type="whatever" />是什么,因此它将输入类型默认为“text”。如果我将这4个输入包装在<form action="myForm.php" method="POST"></form>标签中,浏览器会将输入发送到服务器,因为服务器不关心 / 知道输入是隐藏的、单选按钮、选择、文本还是密码。服务器只接收原始数据。

很可能,您的问题出在Javascript上,而不是PHP。尝试看看未显示小部件的浏览器是否告诉您页面有某种错误。

Safari和Firefox提供了开发/调试工具,IE则不太确定。


那正是我所陈述的。这个“小工具”在Chrome中可以工作,并且在其他浏览器中转换为文本输入类型。这正是我尝试验证的内容。我已经更新了日期格式并将字段拆分为三个部分,然后会继续进行下一步操作。 - Cataneo
1
对于这种情况,jQuery就派上用场了。否则,您需要测试支持性:http://diveintohtml5.info/detect.html#input-types,如果不支持,则必须使用基于JavaScript的替代方案(如jQuery)。 - ILikeTacos

10

你的正则表达式无法正常工作,因为你使用了未转义/分隔符。

验证格式为YYYY-MM-DD的日期的正则表达式如下:

^(19|20)\d\d[\-\/.](0[1-9]|1[012])[\-\/.](0[1-9]|[12][0-9]|3[01])$
它将验证年份以1920开头,月份不大于12且不等于0,日期不大于31且不等于0

在线示例

使用您的初始示例,您可以像这样进行测试:

$date_regex = '/^(19|20)\d\d[\-\/.](0[1-9]|1[012])[\-\/.](0[1-9]|[12][0-9]|3[01])$/';
$hiredate = '2013-14-04';

if (!preg_match($date_regex, $hiredate)) {
    echo '<br>Your hire date entry does not match the YYYY-MM-DD required format.<br>';
} else {
    echo '<br>Your date is set correctly<br>';      
}

示例在线


1
没错,这个可以工作。我使用了很多正则表达式的变体,可能已经把它改坏了。你真是有一双敏锐的眼睛,Ilia Rostovtsev。 - Cataneo

0

在一行语句中检查和验证YYYY-MM-DD日期

function isValidDate($date) {
    return preg_match("/^(\d{4})-(\d{1,2})-(\d{1,2})$/", $date, $m)
        ? checkdate(intval($m[2]), intval($m[3]), intval($m[1]))
        : false;
}

请查看我的回答这里的详细信息。

不要盲目使用DateTime :: createFromFormat来验证日期。让我们以不存在的日期2018-02-30为例:

$d = DateTime::createFromFormat("Y-m-d", "2018-02-30");
var_dump((bool) $d); // bool(true)

是的,它返回true,而不是你可能期望的false。更有趣的是:

$d = DateTime::createFromFormat("Y-m-d", "2018-99-99");
var_dump((bool) $d); // bool(true)

同样是true... 因此,它只验证数字的位数。 再试一次:

$d = DateTime::createFromFormat("Y-m-d", "ABCD-99-99");
var_dump($d); // bool(false)

最后是 false
我们可以从这个片段看出发生了什么。
$d = DateTime::createFromFormat("Y-m-d", "2018-02-30");
var_dump($d);

// var_dump OUTPUT
object(DateTime)#1 (3) {
  ["date"]=>
  string(26) "2018-03-02 16:41:34.000000"
  ["timezone_type"]=>
  int(3)
  ["timezone"]=>
  string(3) "UTC"
}

正如您所看到的,当我们传递不存在的2018-02-30时,DateTime对象包含2018-03-02。我认为这是因为2018年2月只有28天,即最大日期是2018-02-28,当我们传递30这一天时,createFromFormat只是将30天添加到2018-02-01并创建新日期,而没有进行任何先前日期验证。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接