I'm receiving a date string from an API, and it is formatted as yyyy-mm-dd
.
I am currently using a regex to validate the string format, which works ok, but I can see some cases where it could be a correct format according to the string but actually an invalid date. i.e. 2013-13-01
, for example.
Is there a better way in PHP to take a string such as 2013-13-01
and tell if it is a valid date or not for the format yyyy-mm-dd
?
Validate with checkdate function:
$date = '2019-02-30';
$date_parts = explode( '-', $date );
if(checkdate( $date_parts[1], $date_parts[2], $date_parts[0] )){
//date is valid
}else{
//date is invalid
}
Give this a try:
$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";
}
/**** 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);
}
}
I'm afraid that most voted solution (https://stackoverflow.com/a/19271434/3283279) is not working properly. The fourth test case (var_dump(validateDate('2012-2-25')); // false) is wrong. The date is correct, because it corresponds to the format - the m allows a month with or without leading zero (see: http://php.net/manual/en/datetime.createfromformat.php). Therefore a date 2012-2-25 is in format Y-m-d and the test case must be true not false.
I believe that better solution is to test possible error as follows:
function validateDate($date, $format = 'Y-m-d') {
DateTime::createFromFormat($format, $date);
$errors = DateTime::getLastErrors();
return $errors['warning_count'] === 0 && $errors['error_count'] === 0;
}
You can also Parse the date for month date and year and then you can use the PHP function checkdate()
which you can read about here: http://php.net/manual/en/function.checkdate.php
You can also try this one:
$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';
}
Accordling with cl-sah's answer, but this sound better, shorter...
function checkmydate($date) {
$tempDate = explode('-', $date);
return checkdate($tempDate[1], $tempDate[2], $tempDate[0]);
}
Test
checkmydate('2015-12-01');//true
checkmydate('2015-14-04');//false
I have this thing that, even with PHP, I like to find functional solutions. So, for example, the answer given by @migli is really a good one, highly flexible and elegant.
But it has a problem: what if you need to validate a lot of DateTime strings with the same format? You would have to repeat the format all over the place, what goes against the DRY principle. We could put the format in a constant, but still, we would have to pass the constant as an argument to every function call.
But fear no more! We can use currying to our rescue! PHP doesn't make this task pleasant, but it's still possible to implement currying with PHP:
<?php
function validateDateTime($format)
{
return function($dateStr) use ($format) {
$date = DateTime::createFromFormat($format, $dateStr);
return $date && $date->format($format) === $dateStr;
};
}
So, what we just did? Basically we wrapped the function body in an anonymous and returned such function instead. We can call the validation function like this:
validateDateTime('Y-m-d H:i:s')('2017-02-06 17:07:11'); // true
Yeah, not a big difference... but the real power comes from the partially applied function, made possible by currying:
// 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
Functional programming FTW!
How about this one?
We simply use a try-catch block.
$dateTime = 'an invalid datetime';
try {
$dateTimeObject = new DateTime($dateTime);
} catch (Exception $exc) {
echo 'Do something with an invalid DateTime';
}
This approach is not limited to only one date/time format, and you don't need to define any function.
Determine if string is a date, even if string is a non-standard format
(strtotime doesn't accept any custom format)
<?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');
Use in simple way with php prebuilt function:
function checkmydate($date) {
$tempDate = explode('-', $date);
// checkdate(month, day, year)
return checkdate($tempDate[1], $tempDate[2], $tempDate[0]);
}
Test
checkmydate('2015-12-01'); //true
checkmydate('2015-14-04'); //false
/*********************************************************************************
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) === '-');
}
The easiest way to check if given date is valid probably converting it to unixtime using strtotime
, formatting it to the given date's format, then comparing it:
function isValidDate($date) {
return date('Y-m-d', strtotime($date)) === $date;
}
Of course you can use regular expression to check for validness, but it will be limited to given format, every time you will have to edit it to satisfy another formats, and also it will be more than required. Built-in functions is the best way (in most cases) to achieve jobs.
This option is not only simple but also accepts almost any format, although with non-standard formats it can be buggy.
$timestamp = strtotime($date);
return $timestamp ? $date : null;
Determine if any string is a date
function checkIsAValidDate($myDateString){
return (bool)strtotime($myDateString);
}
Tested Regex solution:
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;
}
This will return null if the date is invalid or is not yyyy-mm-dd format, otherwise it will return the date.
Source: Stackoverflow.com