Reputation: 16792
I have a date (eg. yyyy-mm-dd hh:mm:ss) and I want to be able to return true if we have a string matching that pattern, completely, or that partially matches that pattern, from beginning to end. eg. 44
would match the pattern because 44
would match the yy but -12
would not match because -
isn't a valid "y" character.
A few inelegant solutions occur to me. I could do preg_match
on something like this:
#^\d(\d(\d(\d(-)?)?)?)?$#
That just performs a partial match based on the year. It'd need to be expanded out to account for the month, day, hour, minute, etc, but that should show idea # 1.
I could also do something like...
$patterns = [
'',
'\d',
'\d\d',
'\d\d\d',
'\d\d\d\d',
'\d\d\d\d-',
...
];
isset($patterns[strlen($str)] && preg_match('#^' . $patterns[strlen($str)] . '$#', $str)
But that kinda seems convoluted as well.
I could also do this:
switch (strlen($str)) {
case 1: return preg_match('#^\d$#', $str);
case 2: return preg_match('#^\d\d$#', $str);
case 3: return preg_match('#^\d\d\d$#', $str);
case 4: return preg_match('#^\d\d\d\d$#', $str);
case 5: return preg_match('#^\d\d\d\d-$#', $str);
...
}
But that seems bloated as well.
In other words, I'm looking for a function for which 2005-
will return true, as will 2
and 2005-1
. But -2005
will return false, as will 205-
or neubert
.
What I want to be able to do is to pass valid values into an SQL query. eg. WHERE date_column LIKE '$str%'
. If date_column
is a DATETIME then searching for -12-
is a waste of time because it's not possible for date_column
to have that as a value.
Any ideas?
Upvotes: 4
Views: 81
Reputation: 6252
I know this question has already been answered and closed, but I thought it was an interesting challenge and I was determined to come up with a non-regex solution. I also wanted it to be more reusable, rather than hard-coded for one specific case.
Here is what I ended up with; I'm sure it could be further improved! :)
// case-insensitive string format comparison
// if $strict is true, the string lengths must also match
// if $strict is false, the strings are compared left to right
function strfcasecmp($str, $format, $strict = false) {
$len1 = strlen($str);
$len2 = strlen($format);
// make sure we have a valid length
if ($len1 < 1 || $len1 > $len2) {
return false;
}
// if strict, make sure length matches as well
if ($strict) {
if ($len1 !== $len2) {
return false;
}
}
// compare alpha, numeric, space & printable characters
for ($i = 0; $i < $len1; $i++) {
switch (true) {
case ctype_alpha($format[$i]):
if (!ctype_alpha($str[$i])) {
return false;
}
break;
case ctype_digit($format[$i]):
if (!ctype_digit($str[$i])) {
return false;
}
break;
case ctype_space($format[$i]):
if (!ctype_space($str[$i])) {
return false;
}
break;
case ctype_punct($format[$i]):
if ($str[$i] !== $format[$i]) {
return false;
}
break;
default:
// character must match at least one type specified above
return false;
}
}
return true;
}
Example usage based on your original question:
$format = '0000-00-00 00:00:00';
var_dump(strfcasecmp('44', $format)); // true
var_dump(strfcasecmp('-12', $format)); // false
var_dump(strfcasecmp('2005-', $format)); // true
var_dump(strfcasecmp('2', $format)); // true
var_dump(strfcasecmp('2005-1', $format)); // true
var_dump(strfcasecmp('-2005', $format)); // false
var_dump(strfcasecmp('205-', $format)); // false
var_dump(strfcasecmp('neubert', $format)); // false
Upvotes: 1
Reputation: 20899
Something like this should work:
function doesMatch($str) {
// The date pattern split so that each array entry matches exactly one character
$pattern_chunks = array(
'\d', '\d', '\d', '\d', '-',
'\d', '\d', '-',
'\d', '\d',
'\s',
'\d', '\d', ':',
'\d', '\d', ':',
'\d', '\d'
);
$chunk_count = count($pattern_chunks);
$str_len = strlen($str);
// If the string is empty, it's clearly not a date
if ( $str_len < 1 ) { return false; }
// If the string is longer than our pattern chunks, there's no way it matches
if ( $str_len > $chunk_count ) { return false; }
// Make a pattern using the first N chunks of our pattern parts
$pattern = '^' . implode('', array_slice($pattern_chunks, 0, $str_len)) . '$';
// Return if the string matches
return (preg_match($pattern, $str) > 0);
}
For example your input string is 6 characters long, it only uses the first 6 chunks of the date pattern (^\d\d\d\d-\d$
).
Upvotes: 2
Reputation: 785246
Based on comments below your question and if I understood the question right, you can use this single regex to match your inputs:
^(?:\d{1,3}|\d{4}(?:-(?:\d{1,2}(?:-\d{0,2})?)?)?)$
Upvotes: 2
Reputation: 9123
You could also match using strpos()
:
$a = "2016-05-17 was a good day.";
if (strpos(substr($a, 0, 10), '2016-05-17') !== false) {
echo "Indeed it was";
}
Upvotes: 0