Validating an Australian date with PHP
An often annoying thing with being an Australian PHP developer are the dates formats which follows the American standard, i.e. mm/dd/yyyy.
This means we can't easily accept an outside date in Australian format without doing some string manipulation on it to get it into a nice format for native functions like strtotime().
When accepting a date in PHP such as dd/mm/yyyy, we often want to validate it for...
- Syntax - does it match dd/mm/yyyy ?
- Correctness - is it a valid date, i.e. do the digits correspond to real dates ?
- Future - has the date passed already ?
Of course, the last rule does not need to be always be checked, but I often find myself needing to ensure that it has passed.
I've put together a little function that can validate an Australian date, based on the criteria above.
Round 1
function validateDate($str) {
preg_match_all('/^(\d{1,2})\/(\d{1,2})\/(\d{4})$/', (string) $str, $matches);
// Validate syntax
if ( ! isset($matches[0][0])) {
return FALSE;
}
$year = (int) $matches[3][0];
$month = (int) $matches[2][0];
$day = (int) $matches[1][0];
// Make sure date is correct
if ( ! checkdate($month, $day, $year)) {
return FALSE;
}
// Make sure date hasn't passed
if (mktime(0, 0, 0, $month, $day, $year) <= time()) {
return FALSE;
}
return TRUE;
}But wait? Regex? checkdate() and mktime()? Doesn't PHP now provide an easy OO way to play with dates? Well, yes, it does sir!
Round 2
function validateDate($str) {
$date = DateTime::createFromFormat('j/m/Y', $str);
$errors = $date->getLastErrors();
if (isset($errors['warning_count']) AND $errors['warning_count'] > 0 OR isset($errors['errors_count']) AND $errors['errors_count'] > 0 ) {
return FALSE;
}
$diff = $date->diff(new DateTime());
if ($diff->invert === 0) {
return FALSE;
}
return TRUE;
}Ahh... much better!
Often, knowing if a date failed validation isn't enough, we want to know why. This is where a validation library comes in handy - you can pass this function as a callback to it, and then call the library's add error method upon finding an error, to accurately inform the end user as to why their entered date did not pass validation.
Being a Kohana 3 man myself, I find its validation library incredibly helpful. I'm sure you can use your imagination to adapt it for a callback for the validation library.
Comments
Ricardo
Posted on Wednesday, 20th October 2010 @ 2:45am.Test done with a small change on my code.
$val --> in this case, receives a date field in the format dd/mm/yyyy
[code]
if( preg_match('@^(\d{2}\/\d{2}\/\d{4}|\d{4}-\d{2}\-d{2})$@', $val) ) {
$data = preg_split("/[\s\/] /", $val);
if(!checkdate($data[1],$data[0],$data[2])){
self::stackError( $errors, $fieldName, $array['message']);
}
}
[/code]
Tested with Selenium. Used 71/02/1998 (typo) and 10/22/1998 (10/22 could be interpreted as 22/10). Plus, 10-02/1998, 10/**/1998 and 10/AA/1998 also fails.
Cheers!
Ricardo
Posted on Wednesday, 20th October 2010 @ 2:10am.I will test your suggested OO check date. So far, I can validate a date with checkdate. The problem relies where PHP consider correct both 31/10/2010 and 10/31/2010, because it understand both formats. Nevertheless, thank you for your help!
Alexander Dickson
Posted on Sunday, 17th October 2010 @ 7:53pm.@Russel Dias
From what I just tested, the DateTime object does not throw an exception if there is an error or warning present.
If it did, a try/catch block would indeed be more succinct.
However, let me know if I am wrong! Cheers.
Russell Dias
Posted on Sunday, 17th October 2010 @ 4:47pm.Not too sure about the DateTime library. So correct me if I'm wrong.
But, couldn't that verbose error checking be reduced by a simple try / catch construct?
try {
$date = DateTime::createFromFormat('j/m/Y', $str)
} catch (Exception $e) {
//$e->getMessage();
}
Can't test this out at the moment; I will mess around with it tonight.
Leave a Comment
Note: Your comment may require approval before it is posted to the site.