Правильно визначте, чи є рядок дати дійсною датою у такому форматі


184

Я отримую рядок дати від API, і він відформатований як yyyy-mm-dd.

Зараз я використовую регулярний вираз для перевірки формату рядків, який працює нормально, але я можу побачити деякі випадки, коли це може бути правильний формат відповідно до рядка, але насправді недійсна дата. наприклад 2013-13-01, наприклад.

Чи є кращий спосіб у PHP взяти такий рядок, як 2013-13-01і сказати, чи це дійсна дата чи ні для формату yyyy-mm-dd?


Можливий дублікат перевірки дати php
Vivek Athalye

2
Відповідь тут набагато краща.
Себастьян

Відповіді:


454

Для цього ви можете використовувати 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 . Спочатку написав Главич . ]


Тестові приклади:

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

2
@pedromanoel: для стандартного введення дати ви можете використовувати strtotime, але для нестандартних форматів, які strtotimeне розпізнає, вам знадобиться інше рішення. А для php версії 5.2 підтримка припинилася на січень 2011 року, для 5.3 підтримка припинилася в серпні 2014 року.
Главич

2
вважайте цю датуvar_dump( validateDate('2012-2-9'));
поважно

4
Функція працює правильно. Він повернувся помилковим, оскільки вказаний вами формат був неправильним. Якщо ви хочете використовувати день і місяць без 'Y-n-j'нульових нулів, то формат повинен бути @reignsly.
Амаль Муралі

1
@ AntonyD'Andrea: ні, без цієї частини не вийде. Тому що $dне буде помилковим у даті дати, яка перекинула частини, як 13-й місяць (2013-13-01). Але це дійсно залежить, чого ти хочеш. Якщо вам потрібно, наприклад, validateDate('2015-55-66')бути дійсним, то так, вам потрібно лише перевірити, чи $dє об’єкт чи ні.
Главич

81

Визначте, чи є якийсь рядок датою

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

2
Це підтверджує цілий спектр дійсних форматів дати не просто yyyy-mm-dd.
EWit

10
@MichelAyres це тому, що php вважається 2015-02-30дійсною датою, тому що коли вказаний день перевищує кількість днів у вказаному місяці (або негативному) php переходить на наступний місяць. Оскільки дата гарантовано має такий формат, yyyy-mm-ddце можна виправити, змінивши повернення на return (bool)strtotime($myDateString) && date("Y-m-d", strtotime($myDateString)) == $myDateString;.
elitechief21

2
Чому це (bool)strtotime('s')виходить справжнім?
Піон

це також повертає 1 checkIsAValidDate ("F");
Вайбхав Бханушалі

ми можемо використовувати $myDateString = str_replace("/", '-', $myDateString);до повернення, якщо рядок дати містить косої риси (/), наприклад: - dd / mm / yyyy
Yashrajsinh Jadeja

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
Гарне прямолінійне рішення, працював перший раз, дякую :)
Девід Белл

Знову ж таки, коли ви користуєтесь тестом, усередині, ifщоб повернутися просто, trueабо falseповерніть сам тест.
Віктор Шредер

4
Це передбачає, що в масиві $ tempDate є щонайменше 3 елементи.
особа27

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

@vineet - це не вдається. Якщо рік 2, 20, 202, 2020або навіть якщо рік 20201- це повертає істину кожен раз.
ролінгер

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щоб повернутися просто, trueабо falseповерніть сам тест.
Віктор Шредер

Але 2018-3-24 повертається помилковим, метод отримує 2018-3-24, коли застосовується формат return return 2018-03-24; як я можу повернути правду двома способами?
Aquiles Perez

12

Цей варіант не тільки простий, але також приймає практично будь-який формат, хоча з нестандартними форматами він може бути баггі.

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

Це повинна була бути прийнята відповідь! Набагато, набагато простіше.
Вебмайстер G

2
Важливо зауважити, що це не буде працювати з британським (d / m / Y) форматом, оскільки буде припускати, що він перетворює американський (m / d / Y). Здається, це спрацює лише в тому випадку, якщо день менше 12!
Сильвестр Сарацева

@galki - я перевірив це, і це не відповідає певним значенням. Якщо $ date = '202-03-31', вона повертає істину. Але це не дійсна дата. Я виявив, що якщо змінити рік на недійсний рік, він завжди повернеться істинним.
ролінгер

@rolinger дивно ... можливо, це бачить 202AD? Яку часову позначку дає "strtotime"?
галки

@galki - не визначено, але 202повертає негативне число - яке все-таки проходить тест.
ролінгер

10

Ви також можете проаналізувати дату місяця, дати та року, а потім ви можете використовувати функцію PHP, про checkdate()яку ви можете прочитати тут: 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';
    }

у випадку лютого (як коментує elitechief21) функцією єValidDate ($ дата) {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 день, включаючи лютий. Будь-який рік може відбутися 29 лютого. Для перевірки дат за допомогою RegExp потрібно щось набагато складніше, із зворотними посиланнями та негативними поглядами.
Віктор Шредер

9

Найпростіший спосіб перевірити, чи вказана дата дійсна, ймовірно, перетворивши її в одночасний strtotimeформат, відформатувавши її у формат даної дати та порівнявши її:

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

Звичайно, ви можете використовувати регулярне вираження, щоб перевірити правильність, але це буде обмежено заданим форматом, кожного разу, коли вам доведеться редагувати його, щоб задовольнити інші формати, а також це буде більше, ніж потрібно. Вбудовані функції - це найкращий спосіб (у більшості випадків) для досягнення робочих місць.


1
Я вважаю, що ця відповідь досить низької якості, особливо враховуючи, що вже є відповіді строточасу.
GrumpyCrouton

4
Це найкоротша відповідь, і вона працює. Трохи суворо сказати, що це низька якість.
Тім Роджерс

@ user4600953 - це найпростіший, який працює. Багато інших говорять про використання, checkdate()але я вважаю, що термін перевірки невдалий, якщо рік має ЯКЩЕ значення: 2, 20, 202, 2020, 20201- всі повертаються правдою. Я йду з вашим рішенням!
ролінгер

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 ні, ви цього не зробите, контрольний лист буде бомбити, якщо вони відсутні, ви можете написати isset () для кожного, але я просто придушу попередження
Містер Хеліс,

7

У мене є така річ, що навіть з PHP мені подобається знаходити функціональні рішення. Так, наприклад, відповідь, яку дає @migli, справді хороша, надзвичайно гнучка та елегантна.

Але є проблема: що робити, якщо вам потрібно перевірити багато рядків DateTime того самого формату? Вам доведеться повторювати формат повсюдно, що суперечить принципу DRY . Ми могли б поставити формат у постійний, але все-таки нам доведеться передати константу як аргумент для кожного виклику функції.

Але не бійтеся більше! Ми можемо використовувати каррі для нашого порятунку! PHP не робить це завдання приємним, але все-таки можна реалізувати currying з PHP:

<?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
Найкраща відповідь зсувного ІМХО (вирішує конкретну проблему ОП з більшою гнучкістю та майже такою ж кількістю коду, що й решта відповідей)
StubbornShowaGuy

IMHO, це дійсно некрасиве програмування, просто покладіть свої значення в масив і проведіть їх, щоб перевірити, але, будь ласка, не робіть цього!
Тім

Мені цікаво @Tim, чи можете ви надати приклад перевірки масиву / циклу?
Віктор Шредер

5

Я боюся, що більшість проголосованих рішень ( https://stackoverflow.com/a/19271434/3283279 ) не працює належним чином. Четвертий тестовий випадок (var_dump (validateDate ('2012-2-25')); // false) неправильний. Дата правильна, оскільки відповідає формату - m дозволяє місяць з нульовим нульовим числом або без нього (див. Http://php.net/manual/en/datetime.createfromformat.php ). Тому дата 2012-2-25 знаходиться у форматі Ymd, і тестовий випадок повинен бути правдивим, а не хибним.

Я вважаю, що кращим рішенням є перевірка можливої ​​помилки наступним чином:

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

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

3

Як щодо цього?

Ми просто використовуємо блок "пробування".

$dateTime = 'an invalid datetime';

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

Цей підхід не обмежується лише одним форматом дати / часу, і вам не потрібно визначати жодну функцію.


це не працюватиме для значення часу 0000-00-00 00:00:00
Naseeruddin VN

@NaseeruddinVN '0000-00-00 00:00:00'- дійсне значення дати. Це лише перше значення. Однак властивість дати об’єкта datetime буде '-0001-11-30 00:00:00'.
Джуліан

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, інакше вона поверне дату.



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
Код без будь-яких пояснень не дуже корисний.
Герт Арнольд

-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";
}
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.