正确确定日期字符串是否为该格式的有效日期


183

我从API收到日期字符串,其格式为yyyy-mm-dd

我目前正在使用正则表达式来验证字符串格式,这种方法可以正常工作,但是我可以看到某些情况,根据字符串,它可能是正确的格式,但实际上是无效的日期。即2013-13-01,例如。

PHP中是否有更好的方法来接收诸如这样的字符串,2013-13-01并确定它是否为格式的有效日期yyyy-mm-dd



2
无论如何,这里的答案要好得多。
塞巴斯蒂安

Answers:


452

您可以DateTime为此目的使用类:

function validateDate($date, $format = 'Y-m-d')
{
    $d = DateTime::createFromFormat($format, $date);
    // The Y ( 4 digits year ) returns TRUE for any integer with any number of digits so changing the comparison from == to === fixes the issue.
    return $d && $d->format($format) === $date;
}

[ 功能取自这个答案。也在php.net上。最初由Glavić撰写。]


测试用例:

var_dump(validateDate('2013-13-01'));  // false
var_dump(validateDate('20132-13-01')); // false
var_dump(validateDate('2013-11-32'));  // false
var_dump(validateDate('2012-2-25'));   // false
var_dump(validateDate('2013-12-01'));  // true
var_dump(validateDate('1970-12-01'));  // true
var_dump(validateDate('2012-02-29'));  // true
var_dump(validateDate('2012', 'Y'));   // true
var_dump(validateDate('12012', 'Y'));  // false

演示!


14
如果您使用的是PHP 5.2.x,则应strtotime先获取Unix时间戳,然后date('Y-m-d', $t)获取字符串日期。然后,您可以像此答案一样比较它们。
pedromanoel 2014年

2
@pedromanoel:对于标准日期时间输入,您可以使用strtotime,但是对于strtotime无法识别的非标准格式,您将需要其他解决方案。对于php版本5.2的支持在2011年1月停止,对于5.3版本的支持在2014
Glavić14年

2
考虑这个日期var_dump( validateDate('2012-2-9'));
6

4
该功能正常工作。由于您指定的格式不正确,因此返回false。如果要使用不带前导零的日和月,则格式应为'Y-n-j'@reignsly。
Amal Murali 2015年

1
@ AntonyD'Andrea:不,没有那部分就无法工作。因为$d您输入的日期不会是错误的,该日期已溢出部分,例如第13个月(2013-13-01)。但这确实取决于您想要什么。例如validateDate('2015-55-66'),如果您需要有效,则可以,只需检查是否$d为对象。
Glavić

80

确定是否有任何字符串是日期

function checkIsAValidDate($myDateString){
    return (bool)strtotime($myDateString);
}

2
这不仅可以验证整个有效日期格式,还可以验证整个范围yyyy-mm-dd
EWit 2014年

10
@MichelAyres这是因为php被2015-02-30视为有效日期,因为当给定的日期大于给定月份(或负数)的天数时,php会滚动到下个月。由于日期被保证为格式,yyyy-mm-dd因此可以通过将return更改为来固定日期return (bool)strtotime($myDateString) && date("Y-m-d", strtotime($myDateString)) == $myDateString;
elitechief21 2015年

2
为何(bool)strtotime('s')如此真实?
Peon

这还将返回1 checkIsAValidDate(“ F”);
Vaibhav Bhanushali '16

$myDateString = str_replace("/", '-', $myDateString);如果日期字符串包含斜杠(/),例如:
-dd

37

与php预建函数一起简单使用:

function checkmydate($date) {
  $tempDate = explode('-', $date);
  // checkdate(month, day, year)
  return checkdate($tempDate[1], $tempDate[2], $tempDate[0]);
}

测试

   checkmydate('2015-12-01'); //true
   checkmydate('2015-14-04'); //false

1
不错的直接解决方案,是第一次使用,谢谢:)
David Bell

同样,在使用测试时,在内if可以简单地返回truefalse,然后返回测试本身。
VictorSchröder'17

4
假定$ tempDate数组中至少有3个元素。
person27年

2
@ person27:return sizeof($tmpDate) == 3 && checkdate($tmpDate[1]...
neurino

@vineet-这失败。如果年份2202022020或者即使今年是20201-它的每一次返回true。
罗林格

16

确定字符串是否为日期,即使字符串为非标准格式

(strtotime不接受任何自定义格式)

<?php
function validateDateTime($dateStr, $format)
{
    date_default_timezone_set('UTC');
    $date = DateTime::createFromFormat($format, $dateStr);
    return $date && ($date->format($format) === $dateStr);
}

// These return true
validateDateTime('2001-03-10 17:16:18', 'Y-m-d H:i:s');
validateDateTime('2001-03-10', 'Y-m-d');
validateDateTime('2001', 'Y');
validateDateTime('Mon', 'D');
validateDateTime('March 10, 2001, 5:16 pm', 'F j, Y, g:i a');
validateDateTime('March 10, 2001, 5:16 pm', 'F j, Y, g:i a');
validateDateTime('03.10.01', 'm.d.y');
validateDateTime('10, 3, 2001', 'j, n, Y');
validateDateTime('20010310', 'Ymd');
validateDateTime('05-16-18, 10-03-01', 'h-i-s, j-m-y');
validateDateTime('Monday 8th of August 2005 03:12:46 PM', 'l jS \of F Y h:i:s A');
validateDateTime('Wed, 25 Sep 2013 15:28:57', 'D, d M Y H:i:s');
validateDateTime('17:03:18 is the time', 'H:m:s \i\s \t\h\e \t\i\m\e');
validateDateTime('17:16:18', 'H:i:s');

// These return false
validateDateTime('2001-03-10 17:16:18', 'Y-m-D H:i:s');
validateDateTime('2001', 'm');
validateDateTime('Mon', 'D-m-y');
validateDateTime('Mon', 'D-m-y');
validateDateTime('2001-13-04', 'Y-m-d');

使用测试时,在内if简单地返回truefalse,然后返回测试本身。
维克多·施罗德

但是2018-3-24返回false,方法接收2018-3-24,当格式适用时返回2018-03-24; 如何以两种方式返回true?
Aquiles Perez

12

此选项不仅简单,而且几乎可以接受任何格式,尽管使用非标准格式可能会出现问题。

$timestamp = strtotime($date);
return $timestamp ? $date : null;

这应该是公认的答案!简单得多。
网站站长G

2
需要注意的重要一点是,这将不适用于英国(d / m / Y)格式,因为它将假定它正在转换美国(m / d / Y)。只有当一天少于12天时,它才似乎起作用!
西尔维斯特·萨拉斯瓦斯

@galki-我对此进行了测试,但它无法通过某些值。如果$ date ='202-03-31',则返回true。但这不是一个有效的日期。我发现,如果将年份更改为无效年份,则始终返回true。
罗林格

@rolinger很奇怪...也许正在看到202AD?“ strtotime”会给哪一年的时间戳?
galki

@galki-不确定,但202返回负数-仍通过测试。
罗林格

9

您还可以解析月份日期和年份的日期,然后可以使用checkdate()可以在此处阅读的PHP函数:http : //php.net/manual/en/function.checkdate.php

您也可以尝试以下一种方法:

$date="2013-13-01";

if (preg_match("/^[0-9]{4}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/",$date))
    {
        echo 'Date is valid';
    }else{
        echo 'Date is invalid';
    }

如果是2月(如elitechief21所述),函数isValidDate($ date){return preg_match(“ / ^ [0-9] {4}-(0 [1-9] | 1 [0-2])-(0 [1-9] | [1-2] [0-9] | 3 [0-1])$ ​​/“,$ date)&& date(” Ymd“,strtotime($ date))== $ date; }
abdulwadood

4
该解决方案非常差,因为它在任何意义上都不检查日期有效性。任何月份都可以有31天,包括2月。任何一年都可以是2月29日。要使用RegExp验证日期,将需要更为复杂的内容,包括反向引用和否定的前瞻性。
VictorSchröder'17

9

检查给定日期是否有效的最简单方法可能是使用将其转换为unixtime strtotime,将其格式化为给定日期的格式,然后进行比较:

function isValidDate($date) { return date('Y-m-d', strtotime($date)) === $date; }

当然,您可以使用正则表达式来检查有效性,但是它将限于给定的格式,每次您必须对其进行编辑以满足其他格式时,它也会超出要求。内置功能是完成工作的最佳方法(在大多数情况下)。


1
我觉得这个答案的质量很低,特别是考虑到已经有 strtotime答案。
GrumpyCrouton

4
这是最短的答案,并且有效。很难说这是低质量的。
Tim Rogers

@ user4600953-这是最简单的方法。许多其他人都在说要使用checkdate()-但我发现如果年份为ANY值,则checkdate失败:2, 20, 202, 2020, 20201-全部返回true。我要解决您的问题!
罗林格

7

根据cl-sah的回答,但这听起来更好,更短...

function checkmydate($date) {
  $tempDate = explode('-', $date);
  return checkdate($tempDate[1], $tempDate[2], $tempDate[0]);
}

测试

checkmydate('2015-12-01');//true
checkmydate('2015-14-04');//false

你需要测试,如果count($tempDate) === 3虽然
VDarricau

@VDarricau不,您不会,如果缺少它们,checkdate会炸弹,您可以为每个单独的代码写isset(),但我只是抑制了警告
Heelis

7

我有一个东西,即使使用PHP,我也喜欢找到功能解决方案。因此,例如,@ migli给出的答案确实是一个很好的答案,高度灵活且优雅。

但这有一个问题:如果您需要验证许多相同格式的DateTime字符串怎么办?您将不得不在各处重复格式,这与DRY原理背道而驰。我们可以将格式放入常量中,但是仍然必须将常量作为参数传递给每个函数调用。

但是别再害怕了!我们可以用咖喱角来营救!PHP并没有使这项任务令人愉快,但是仍然可以使用PHP实现currying:

<?php
function validateDateTime($format)
{
    return function($dateStr) use ($format) {
        $date = DateTime::createFromFormat($format, $dateStr);
        return $date && $date->format($format) === $dateStr;
    };
}

那么,我们刚刚做了什么?基本上,我们将函数主体包装为匿名形式,然后返回此类函数。我们可以这样调用验证函数:

validateDateTime('Y-m-d H:i:s')('2017-02-06 17:07:11'); // true

是的,没什么大不同……但是真正的力量来自于部分应用的函数,该函数可以通过以下方式实现:

// Get a partially applied function
$validate = validateDateTime('Y-m-d H:i:s');

// Now you can use it everywhere, without repeating the format!
$validate('2017-02-06 17:09:31'); // true
$validate('1999-03-31 07:07:07'); // true
$validate('13-2-4 3:2:45'); // false

功能编程FTW!


2
压倒性的恕我直言的最佳答案(以更大的灵活性和几乎与其余答案相同的代码量解决了OP的特定问题)
StubbornShowaGuy

恕我直言,这确实是丑陋的编程,只需将您的值放入数组中并遍历它们进行验证,但是请不要这样做!
蒂姆(Tim)

我很好奇@Tim,能否给我们举个数组/循环验证的例子?
维克多·施罗德

5

恐怕多数投票解决方案(https://stackoverflow.com/a/19271434/3283279)无法正常工作。第四个测试用例(var_dump(validateDate('2012-2-25')); //否)错误。日期是正确的,因为它与格式相对应-m允许一个月,带或不带前导零(请参阅:http : //php.net/manual/en/datetime.createfromformat.php)。因此,日期2012-2-25的格式为Ymd,并且测试用例必须为true而不是false。

我相信更好的解决方案是按如下方式测试可能的错误:

function validateDate($date, $format = 'Y-m-d') {
    DateTime::createFromFormat($format, $date);
    $errors = DateTime::getLastErrors();

    return $errors['warning_count'] === 0 && $errors['error_count'] === 0;
}

3

这个怎么样?

我们只使用try-catch块。

$dateTime = 'an invalid datetime';

try {
    $dateTimeObject = new DateTime($dateTime);
} catch (Exception $exc) {
    echo 'Do something with an invalid DateTime';
}

这种方法不仅限于一种日期/时间格式,您不需要定义任何函数。


这不适用于日期时间值的注册时间00:00:00
Naseeruddin VN

@NaseeruddinVN '0000-00-00 00:00:00'是有效的日期时间值。这只是第一个价值。但是,datetime对象的date属性将为'-0001-11-30 00:00:00'
朱利安(Julian)

1

经过测试的Regex解决方案:

    function isValidDate($date)
    {
            if (preg_match("/^(((((1[26]|2[048])00)|[12]\d([2468][048]|[13579][26]|0[48]))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|[12]\d))))|((([12]\d([02468][1235679]|[13579][01345789]))|((1[1345789]|2[1235679])00))-((((0[13578]|1[02])-(0[1-9]|[12]\d|3[01]))|((0[469]|11)-(0[1-9]|[12]\d|30)))|(02-(0[1-9]|1\d|2[0-8])))))$/", $date)) {
                    return $date;
            }
            return null;
    }

如果日期无效或不是yyyy-mm-dd格式,则返回null,否则将返回日期。



0
/*********************************************************************************
Returns TRUE if the input parameter is a valid date string in "YYYY-MM-DD" format (aka "MySQL date format")
The date separator can be only the '-' character.
*********************************************************************************/
function isMysqlDate($yyyymmdd)
{
    return checkdate(substr($yyyymmdd, 5, 2), substr($yyyymmdd, 8), substr($yyyymmdd, 0, 4)) 
        && (substr($yyyymmdd, 4, 1) === '-') 
        && (substr($yyyymmdd, 7, 1) === '-');
}

-1
    /**** date check is a recursive function. it's need 3 argument 
    MONTH,DAY,YEAR. ******/

    $always_valid_date = $this->date_check($month,$day,$year);

    private function date_check($month,$day,$year){

        /** checkdate() is a php function that check a date is valid 
        or not. if valid date it's return true else false.   **/

        $status = checkdate($month,$day,$year);

        if($status == true){

            $always_valid_date = $year . '-' . $month . '-' . $day;

            return $always_valid_date;

        }else{
            $day = ($day - 1);

            /**recursive call**/

            return $this->date_check($month,$day,$year);
        }

    }

1
没有任何解释的代码不是很有用。
Gert Arnold

-2

试试看:

$date = "2017-10-01";


function date_checker($input,$devider){
  $output = false;

  $input = explode($devider, $input);
  $year = $input[0];
  $month = $input[1];
  $day = $input[2];

  if (is_numeric($year) && is_numeric($month) && is_numeric($day)) {
    if (strlen($year) == 4 && strlen($month) == 2 && strlen($day) == 2) {
      $output = true;
    }
  }
  return $output;
}

if (date_checker($date, '-')) {
  echo "The function is working";
}else {
  echo "The function isNOT working";
}
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.