【问题标题】:How to check if a string is a valid DATE, TIME or DATETIME如何检查字符串是否是有效的 DATE、TIME 或 DATETIME
【发布时间】:2011-02-11 00:34:13
【问题描述】:

当我尝试将值放入无效的 DATE 字段时,MySQL 似乎使用 0000-00-00 代替。有没有一种方法可以在不更新 DATE 字段的情况下进行此“检查”?并通过例如 PHP 来做到这一点?

例如,有没有一种方法可以查询 MySQL 服务器并询问“嘿,这个 DATE、TIME 或 DATETIME 对你有效吗?”

或者有没有更好的方法?

【问题讨论】:

  • 我真的很讨厌如果你通过 php 在 mysql 的日期列中插入任何无效值,你会得到一个 '0000-00-00'!它应该失败(首选)、将其保留为 null 或将其设置为字段的声明默认值。
  • 我也这样做了,但认为你可以改变这一点。只需要开启严格模式什么的:dev.mysql.com/doc/refman/5.5/en/…
  • 实际上就像@VolkerK 的回答一样,stackoverflow.com/a/2690242/39321 :)
  • 您可以随时将数据复制到测试系统并试用 ;)
  • 是的,启用严格模式,它还会发现一些其他问题。

标签: php mysql validation datetime


【解决方案1】:

我在我的应用程序中使用了这种语法,它的工作就像魅力一样!

SELECT DATE('2013-09-31') AS valid;

我的系统是 Win7x64,PHP 5.5 和 MySQL 5.6.16

【讨论】:

  • 谢谢。对我来说效果很好!
【解决方案2】:

您可以根据要使用的格式解析日期,然后调用 checkdate 来测试它是否是有效日期。请务必阅读http://php.net/manual/en/function.checkdate.php 上的 cmets。

【讨论】:

    【解决方案3】:

    由于问题是 MySQL,这里有一个 MySQL 解决方案:

    验证字段是否为日期非常困难,因为需要考虑所有不同的可能日期格式。但是,如果您知道字段日期格式是其中之一:

    'yyyy-mm-dd'
    'yyyy-mm-dd hh:mm:ss'
    'yyyy-mm-dd whatever'
    

    此代码将帮助您:

     SELECT count(*) FROM `table` 
     WHERE DATE(STR_TO_DATE(`column`, '%Y-%m-%d')) IS NOT NULL
     AND `column` NOT REGEXP '^[0-9\.]+$'
    

    基本上:

    • 第一个条件告诉您是否是日期,但不幸的是不排除数字(例如:DATE(STR_TO_DATE(**1**, '%Y-%m-%d')) = '2001-00-00'
    • 第二个确保数字被排除在外,这样您就只能得到符合上述格式的日期。

    如果count(*)>0,那么它就是一个日期,如果它是0,它就是别的东西。

    注意 此方法适用于任何日期格式的字符串,只要您事先知道它们遵循的格式(我知道并非总是如此,但仍然有用)。只需替换查询中的格式a priori

    【讨论】:

      【解决方案4】:

      您可以只使用“常量”查询,而不使用临时表和测试字段:

      mysql> select day('2010-02-31 00:00:00');
      +----------------------------+
      | day('2010-02-31 00:00:00') |
      +----------------------------+
      |                       NULL | 
      +----------------------------+
      

      【讨论】:

      • 奇怪的是,这似乎不起作用。我遇到了一个服务器,其中更新给出了错误 1292:Incorrect datetime value: '2012-09-13T17:58:23+02:00',但 select day('2012-09-13T17:58:23+02:00') 给出了 13。
      • 如果字段的值为 '0',则此方法不起作用,例如 day('0')=0。您应该添加 day(column) 不为 null 并且 day(column) 0。请参阅我的另一种方法的答案
      【解决方案5】:

      如果您为 MySQL 服务器选择不允许无效日期值的服务器模式,则包含这种格式错误的日期表示的查询将导致错误,而不是(默默地)假设 0000-00-00
      http://dev.mysql.com/doc/refman/5.0/en/server-sql-mode.html

      例如

      $pdo = new PDO('mysql:host=localhost;dbname=test', 'localonly', 'localonly'); 
      $pdo->setAttribute( PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION );
      
      $pdo->exec('CREATE TEMPORARY TABLE foo (id int auto_increment, d datetime, primary key(id))');
      
      $query = "INSERT INTO foo (d) VALUES ('2010-02-31 12:15:18')";
      foreach( array('ALLOW_INVALID_DATES', 'STRICT_ALL_TABLES') as $mode ) {
        echo $mode, ": "; flush();
        $pdo->exec("SET SESSION sql_mode='$mode'");
        $pdo->exec($query);
        echo "Ok.\n";
      }
      

      打印

      ALLOW_INVALID_DATES: Ok.
      STRICT_ALL_TABLES: 
      Fatal error: Uncaught exception 'PDOException' with message 'SQLSTATE[22007]: Invalid datetime format: 1292 Incorrect datetime value: '2010-02-31 12:15:18' for column 'd' at row 1' in [...]
      

      【讨论】:

      • 很高兴知道!将研究设置该模式。有了这个,我将不得不实际尝试然后捕获异常。有没有办法在运行插入或更新之前检查它?
      • 我不知道。但是(虚构的)函数 mysql_check_date() 返回 false 或查询的执行失败(带有特定的错误代码)是否重要?
      • 当我需要它来验证用户输入时,是的,有点。特别是如果您想在提交之前使用 AJAX 或其他方式检查“客户端”。
      【解决方案6】:

      SELECT WEEK(col) IS NOT NULL AS valid;

      或以下之一:

      SELECT DAYNAME(col) IS NOT NULL AS valid;
      SELECT TO_DAYS(col) IS NOT NULL AS valid;
      SELECT TO_SECONDS(col) IS NOT NULL AS valid;
      SELECT WEEKDAY(col) IS NOT NULL AS valid;
      SELECT WEEKOFYEAR(col) IS NOT NULL AS valid;
      SELECT YEARWEEK(col) IS NOT NULL AS valid;
      

      【讨论】:

        【解决方案7】:

        要检查一个值是否是一个日期,您可以使用 day(dateToCheck) = 0

        【讨论】:

          【解决方案8】:

          我这样做:

          if(strtotime(trim($str)) {
              // persist trim($str) in db.        
          
          }
          

          【讨论】:

            【解决方案9】:

            在php中使用这个函数来检查一个有效的日期:

            function valid_date($date) {
                return (preg_match("/^([0-9]{4})-([0-9]{2})-([0-9]{2})$/", $date));
            }
            

            或者正如@VolkerK所说,如果将无效日期插入数据库,它将被存储为 0000-00-00 所以你也可以这样做:

            SELECT * FROM table WHERE date_field > '0000-00-00'

            【讨论】:

            • 但是这个函数返回 true 与 '2010-32-56'
            • 是的,这会清除格式错误的日期,但不会处理无效日期。虽然已经在使用它进行预检查,但我使用\d 而不是[0-9]。并且不需要括号 AFAIK:/^\d{4}-\d{2}-\d{2}$/
            【解决方案10】:

            一个简单的 PHP 函数,将验证“日期”和“日期时间”的 MySQL 数据类型 在后面的代码示例中 (chk_MySQL_Datetime)。它通过使用 createFromFormat() 来验证 如果失败,则为日期/日期时间创建一个 Date 对象,然后创建日期/日期时间 是无效的。如果创建了日期对象,则使用创建的格式化字符串 date_format() 并与日期/时间进行比较。 如果字符串相等,则日期/日期时间有效。 如果不相等,则日期/日期时间无效。这个比较 是必要的,因为 createFromFormat() 将接受 2009/02/29 09:85 作为有效的日期时间 但 MySQL 不会。

            以下代码包含一个示例/测试用例,显示了对 MySQL 数据库的使用和测试。 要使用该示例,您必须将 $database->domysql_query() 替换为对数据库的调用。 echo 的输出作为 cmets 包含在 echo 语句之后。

            <?php
            /**
             * Returns false if MySQL date or datetime value would be stored as
             * 0000-00-00  or 0000-00-00 00:00:00.
             * @param string $fmt - createFromFormat format of MySQL datetime value
             * to be tested. see http://www.php.net/manual/en/datetime.createfromformat.php
             * @param string $datetime MySQL datetime value
             * to be tested.
             * @return DateTime object formatted according to $fmt or FALSE if not
             * valid MySQL datetime value (would be inserted into the database
             * 0000-00-00 00:00:00);
             */
            function chk_MySQL_Datetime($fmt, $datetime) {
                global $fmtdatetime;  // for example only
                $ckdate = DateTime::createFromFormat($fmt, $datetime);
                if ($ckdate !== FALSE) {
                    $fmtdatetime = date_format($ckdate, $fmt);
                    if ($fmtdatetime != $datetime) {
                        $ckdate = FALSE;
                    }
                }
            
                return $ckdate;
            }
            /*  Example/test of  chk_MySQL_Datetime */
            /**
             * Creates table datetimetest if it doesn't exist
             * and insert a record in primary index of type datetime
             * then select record inserted and return result as a formated string.
             * @global type $database - addressibility to database functions
             * @global type $mysqlenum - MySql errornumber of last operation
             * @global type $fmtdatetime - $datetime formatted by date_format
             * @param type $datetime - datetime vale to be set
             * @return string of what was inserted
             */
            function insert_in_table($datetime) {
                global $database, $mysqlenum, $fmtdatetime;
                $sql = "CREATE TABLE IF NOT EXISTS `datetimetest` (
              `MySQL_datetime` datetime NOT NULL COMMENT 'datetime created by MySQL',
              `in_datetime` varchar(90) NOT NULL COMMENT 'date time string',
              `fmtdatetime` varchar(90) NOT NULL COMMENT 'Output of createFromFormat',
              PRIMARY KEY (`MySQL_datetime`)
            ) ;";
                $result = $database->domysql_query($sql, $database->connection, true);
            
                $sql = "DELETE FROM `datetimetest`  WHERE MySQL_datetime='$datetime' OR `MySQL_datetime` = '0000-00-00 00:00:00'; ";
                $result = $database->domysql_query($sql, $database->connection, false);
                $sql = "INSERT INTO `datetimetest` (MySQL_datetime, in_datetime, fmtdatetime)
                   VALUES ('$datetime', '$datetime', '$fmtdatetime')";
                $result = $database->domysql_query($sql, $database->connection, false);
                $sql = "SELECT * FROM `datetimetest`  WHERE MySQL_datetime='$datetime' OR `MySQL_datetime` = '0000-00-00 00:00:00'; ";
                $result = $database->domysql_query($sql, $database->connection, false);
                $contxa = mysql_fetch_assoc($result);
            
                $ret = " Inserted datetime = " . $contxa['in_datetime'] . "," .
                        " MySQL stored " . $contxa['MySQL_datetime'] .
                        ", fmtdatetime = " . $contxa['fmtdatetime'] . "<br>";
                return $ret;
            }
            
                    global $fmtdatetime;
                    echo('<br>');
                    $format = 'Y-m-d';
                    $datetime = '2009-02-15'; // valid date
                    $date = chk_MySQL_Datetime($format, $datetime);
                    echo "Format: $format; datetime is " . ($date ? 'valid' : 'invalid' ) . " Expected $datetime is " . $fmtdatetime . "<br>";
             //echo output = Format: Y-m-d; datetime is valid Expected 2009-02-15 is 2009-02-15
            
                    $result = insert_in_table($datetime);
                    echo $result . "<br>";
             //echo output = Inserted datetime = 2009-02-15, MySQL stored 2009-02-15 00:00:00, fmtdatetime = 2009-02-15
            
                    $datetime = '2009-02-29'; //invalid date
                    $date = chk_MySQL_Datetime($format, $datetime);
                    echo "Format: $format; datetime is " . ($date ? 'valid' : 'invalid' ) . " Expected $datetime is " . $fmtdatetime . "<br>";
             //echo output = Format: Y-m-d; datetime is invalid Expected 2009-02-29 is 2009-03-01
            
                    $result = insert_in_table($datetime);
                    echo $result . "<br>";
             //echo output =  Inserted datetime = 2009-02-29, MySQL stored 0000-00-00 00:00:00, fmtdatetime = 2009-03-01
            
                    $format = 'Y-m-d H:i';
                    $datetime = '2009-02-15 14:20';
                    $date = chk_MySQL_Datetime($format, $datetime);
                    echo "Format: $format; datetime is " . ($date ? 'valid' : 'invalid' ) . " Expected $datetime is " . $fmtdatetime . "<br>";
            //echo output = Format: Y-m-d H:i; datetime is valid Expected 2009-02-15 14:20 is 2009-02-15 14:20
            
                    $result = insert_in_table($datetime);
                    echo $result . "<br>";
            //echo output = Inserted datetime = 2009-02-15 14:20, MySQL stored 2009-02-15 14:20:00, fmtdatetime = 2009-02-15 14:20
            
                    $datetime = '2009-02-15 14:63';  // invalid time
                    $date = chk_MySQL_Datetime($format, $datetime);
                    echo "Format: $format; datetime is " . ($date ? 'valid' : 'invalid' ) . " Expected $datetime is " . $fmtdatetime . "<br>";
            //echo output = Format: Y-m-d H:i; datetime is invalid Expected 2009-02-15 14:63 is 2009-02-15 15:03
            
                    $result = insert_in_table($datetime);
                    echo $result . "<br>";
             //echo output = Inserted datetime = 2009-02-15 14:63, MySQL stored 0000-00-00 00:00:00, fmtdatetime = 2009-02-15 15:03
            
                    $datetime = 'x-02-15 14:59';  // invalid time
                    $date = chk_MySQL_Datetime($format, $datetime);
                    echo "Format: $format; datetime is " . ($date ? 'valid' : 'invalid' ) . " Expected $datetime is " . $fmtdatetime . "<br>";
             //echo output = Format: Y-m-d H:i; datetime is invalid Expected x-02-15 14:59 is 2009-02-15 15:03
            
                    $result = insert_in_table($datetime);
                    echo $result . "<br>";
            //echo output =  Inserted datetime = x-02-15 14:59, MySQL stored 0000-00-00 00:00:00, fmtdatetime = 2009-02-15 15:03
            ?>
            

            【讨论】:

              【解决方案11】:

              也许您必须使用BLACKHOLE 引擎创建一个数据库,请阅读here。并检查 PDOException 抛出的异常。

              $dbh = new PDO('mysql:host=localhost;dbname=test', 'username', 'p@s5W0Rd!!!', array(PDO::ATTR_PERSISTENT => true));
              
              // CREATE DATABASE test;
              // CREATE TABLE test.foo ( bar DATETIME ) ENGINE=BLACKHOLE;
              
              try {
                  $dbh->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
              
                  $dbh->beginTransaction();
              
                  $dateTime = new DateTime();
                  $dateTime = $dateTime->format('c');
                  $dbh->exec("INSERT INTO foo (bar) VALUES ('".$dateTime."')");
                  $dbh->commit();
              } catch (PDOException $e) {
                  $errorInfo = $dbh->errorInfo();
              
                  if ($e->getCode() === '22007'
                      and $dbh->errorCode() === '22007'
                      and $errorInfo[0] === '22007'
                      and preg_match('/^incorrect datetime/', strtolower($errorInfo[2])) === 1) {
              
                      throw new Exception($errorInfo[2], (int) $e->getCode());
                  }
              }
              

              【讨论】:

                【解决方案12】:

                日期时间 - 在 SQL 中

                SELECT your_unreliable_datetime_column, CAST(your_unreliable_datetime_column as DATETIME)
                FROM some_cool_table
                WHERE
                CAST(your_unreliable_datetime_column as DATETIME) IS NOT NULL
                AND
                your_unreliable_datetime_column = CAST(your_unreliable_datetime_column as DATETIME)
                

                这应该只选择有效的DATETIMEs,也应该相应地适用于DATETIME,但没有经过测试...

                我没有找到关于转换失败时 CAST() 返回值的任何文档,但 documentation covers a special case - 可能失败 - 返回 NULL

                用于将“零”日期字符串转换为日期,CONVERT()CAST() 返回NULL 并在NO_ZERO_DATE SQL 模式为 已启用。

                此外,根据我的测试,似乎CAST()任何 转换失败时表现相同 - 返回NULL 并生成警告。

                我实际上在MariaDB 10.0.30上测试过这个

                【讨论】:

                  【解决方案13】:

                  尝试使用这个... WHERE DATE_FORMAT(DATE('$value'), '%Y-%m-%d') = '$value'

                  使用 Perl 时,$sth->如果失败,rows 将返回零

                  while $sth->如果成功,rows 将返回非零

                  【讨论】:

                    【解决方案14】:

                    它应该可以解决 MySQL 中的问题。

                    对于纯 DATE 和 TIME 场景(使用 DATETIME 值,您可以按原样进行) - 只需将初始值与任何 TIME 或 DATE 字符串值相匹配以匹配您的 DB DATETIME 格式(例如 CONCAT( date_value, " 00:00 :00") 或 CONCAT("2021-12-31 ", time_value) ) - 作为此示例中的变量应该采用 DATETIME 格式。

                    # declare variables
                    SET @invalid_date:='2021-02-30 25:64:64', @valid_date:='2021-02-28 04:01:01';
                    
                    # check valid DATE scenario: invalid_date -> NULL; valid_date -> NOT NULL - correct value
                    SELECT DATE( @invalid_date );
                    SELECT DATE( @valid_date );
                    
                    # check valid TIME scenario: invalid_date -> NULL; valid_date -> NOT NULL - correct value
                    SELECT TIME( @invalid_date );
                    SELECT TIME( @valid_date );
                    
                    # check valid DATETIME scenario: invalid_date -> NULL; valid_date -> NOT NULL - correct value
                    SELECT CAST( @invalid_date AS DATETIME );
                    SELECT CAST( @valid_date AS DATETIME );
                    
                    # purge variables
                    SET @invalid_date:= NULL, @valid_date:=NULL;
                    

                    享受吧。

                    【讨论】:

                      猜你喜欢
                      • 1970-01-01
                      • 2010-12-23
                      • 2013-02-13
                      • 2011-06-17
                      • 1970-01-01
                      • 2011-11-26
                      • 2018-03-21
                      • 1970-01-01
                      • 1970-01-01
                      相关资源
                      最近更新 更多