使用PHP检查变量是否为有效日期

40
我正在编写一个脚本,将从CSV文件中导入一些数据。在此过程中,我想检查一个变量是否为有效的日期字符串。
我已经看到了几种检查字符串是否为日期的方法,但大多数方法都需要你知道日期的格式。而我不知道日期的格式。
目前我正在使用strtotime(),但它容易失败。
$field ="May";
if(strtotime($field)){
    echo "This is a date";
}
在这个案例中,“May”是某人的名字,而不是一个日期。
有没有人能推荐更可靠的函数?
根据你们一些问题的编辑。
在我的情况下,要将一个变量作为“日期”传递,它需要特定于一天/月/年,因此只有“May”太模糊了,不能算作日期。基于这一点和保罗在下面提出的好点子,我们还可以测试字符串是否包含数字,例如:
$field ="May";
if(strtotime($field) && 1 === preg_match('~[0-9]~', $field)){
    echo "This is a date";
}else{
    echo "Nope not a date";
}

这似乎满足了我的直接需求,但有人能否发现任何问题或者提出改进建议?


2
你需要给出一些规定,说明你接受什么样的值。否则,“May”与任何其他值一样好。 - JJJ
1
有任何模式吗?月份是否以数字形式和字符串形式出现?日期是否分开在单元格中,例如A1中的月份,B2中的日期,还是它们都在一个单元格中? - Paul Dessert
@Alex - 是的,这是一个棘手的问题,但至少你可以更细致地过滤一下。我相信你可以让它更准确一些,但那已经超出了我的能力范围。 :) - Paul Dessert
你正在尝试解释一些未知模式的数据,这可能是日期,也可能不是... 这将会很困难。我担心的是,如果输入数据如此模糊,你可能会误解数据,并比接受它所给出的信息更加有害无益。 - FluffyKitten
1
@Alex:仅仅匹配输入中的数字远远不够满意,因为它可以将“'0May'”作为有效日期。 - anubhava
显示剩余7条评论
3个回答

57

使用date_parse函数并检查返回数组中的值。

$date = date_parse("May")

// ["year"] == FALSE
// ["month"] == 5
// ["day"] == FALSE
你也可以将这些参数传递给checkdate函数。
$date = date_parse($someString);
if ($date["error_count"] == 0 && checkdate($date["month"], $date["day"], $date["year"]))
    echo "Valid date";
else
    echo "Invalid date";

我认为你懂了... 有人能看出这里有什么缺陷吗? - Alex
一个微小的缺陷:date_parse使用了strtotime解析规则,这些规则定义良好但很挑剔。有可能提交的数据返回一个合法的日期,但并不是预期的日期。 - Charles
2
@Charles,没有明确定义输入格式的情况下,任何自动解析日期的算法都会存在问题。 - saluce
1
我知道这是比较旧的,但如果你使用了 date_parse("2014-01-01 12:00:00'; select * from users;") 这个函数,它会说这个日期是有效的。然而,$date['error_count'] 将会大于0,这可能是更好的选择。请参见 http://ideone.com/Xl1HTb。 - Joseph
@joseph4tw 说得好。 我已经更新了答案,包括对date_parse错误计数的检查。 - saluce
1
@saluce $date["errors"] 是一个数组,因此它总是为 false。我认为你需要的是 $date["error_count"] - Janis Peisenieks

5

我认为这个问题没有一个全能的答案。根据您的使用情况,您可能会有不同的策略。

您的strtotime()是一个完美的解决方案,但正如您所说,您可能会遇到误报。为什么?因为“may”可能是一个单词或一个名字。但是,strtotime('May')的结果是什么?

echo date(DateTime::ISO8601, strtotime('May'));
2012-05-21T00:00:00+0200

如果只提供月份,则返回当前年份和从午夜开始的当前日期,给定月份。一个可能的解决方案是检查您的字符串是否包含当前日期和/或当前年份,这样,您可以检查以确保您的日期是完全合格的日期并且有效。

echo date(DateTime::ISO8601, strtotime('May Day')); // (strtotime() returns false)
1970-01-01T01:00:00+0100

echo date(DateTime::ISO8601, strtotime('May 21'));
2012-05-21T00:00:00+0200

一个简单的strpos()或者正则表达式应该就可以解决问题。

然而这有点奇怪,只有在没有其他方法时才应该使用。

我认为更好的解决方案是定义一组有效的格式,并插值结果以确保日期有效。

$validDateFormatPatterns = array(
 '[0-9]{1,2}-[0-9]{1,2}-[0-9]{4}', // 21-05-2012, obviously this pattern is simple and would accept 05-21-2012,
 'the [0-9]{1,2}(th|st|nd|rd) (January|February|...|May|...|December) [0,9]{4}', // The 21st May 2012
);

您应该尝试涵盖大部分情况,我相信您将能够找到检查大多数当前日期格式的正则表达式。

无论如何,您可能需要不时调整您的函数,因为没有简单的方法使其防弹。


1

我知道这个问题很久以前就被问过了,但是在寻找答案时,我想避免使用正则表达式,于是得出了以下方法:

function checkInputIsDate($date) {
    return (bool)strpbrk($date,1234567890) && strtotime($date);
}

这样做的原因是通过使用strpbrk确保字符串中有数字,并验证strtotime输出日期,消除了上述仅传递一个月份到strtotime的问题。
并且学习了一个我不知道存在的函数。
希望这能帮助某些人。

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