40

I am working on a script that will import some data from a CSV file. As I am doing this I want to be able to check a variable to see if it is a valid date string.

I have seen several ways to check if a sting is a date, but most of them require you to now the format. I will not know the format that the date will in.

right now I am using strtotime(), but this fails to easily

$field ="May";
if(strtotime($field)){
    echo "This is a date";
}

In this case, "May" was the persons first name, and not a date at all.

Can any one recommend more reliable function?

Edit based on questions from some of you.

For a variable to pass as a "date" in my case, it would need to be specific to a day/month/year, so just "May" would be to vague to count.

Based on that and Pauls good point below we can also test to see if the string contains a number, such as

$field ="May";
if(strtotime($field) && 1 === preg_match('~[0-9]~', $field)){
    echo "This is a date";
}else{
    echo "Nope not a date";
}

This seems to cover my immediate needs, but can any one spot any issues or suggest improvements?

Alex
  • 1,535
  • 2
  • 16
  • 26
  • How would your script tell whether `May` was a name or a date? – lonesomeday May 21 '12 at 20:15
  • 2
    You need to give *some* specifications what kind of values you accept. Otherwise "May" is just as good as any other value. – JJJ May 21 '12 at 20:17
  • Check if the person has a last-name – Joelmob May 21 '12 at 20:17
  • Are you testing *every single* field you're getting to check if it is a date? Doesn't your Excel file have some sort of general format that it follows, like firstName, lastName, date, where you'd only need to check every third field or something? – animuson May 21 '12 at 20:22
  • Aminuson, sadly I will be getting data from different sources so can not count on any consistancy. – Alex May 21 '12 at 20:24
  • 1
    Is there any pattern at all? Do the months appear numerically as well as in a string? Are the dates separated in cells, i.e. Month in A1, Day in B2, or are they all in one cell? – Paul Dessert May 21 '12 at 20:24
  • Jahana, good point. For a field to be a "date" it would have to be specific to an exact year and day, so "May" is just to vague – Alex May 21 '12 at 20:25
  • Paul Good point! Yes the date string I would need would have to have a number in it as well as just the word "May" to be valid.. So if(strtotime($field) && 1 === preg_match('~[0-9]~', $field)){} would be one step better!! – Alex May 21 '12 at 20:26
  • @Alex - Yeah, That's a tough one, but at least you could filter it a little more. I'm sure you could get this a little more accurate, but that's beyond my skills. :) – Paul Dessert May 21 '12 at 20:30
  • You are trying to interpret data in an unknown pattern that may or may not be a date... thats going to be tough. My concern is that if the input data is going to be that vague, you could mis-interpret the data and do more harm than good than if you just accepted what it gave you. – FluffyKitten May 21 '12 at 20:31
  • 1
    @Alex: Just matching a number in input is far from satisfactory as it can take `'0May'` as valid date. – anubhava May 21 '12 at 20:38
  • FluffyKitten, Agree. My detection at this point will be used to provide a "Best Guess" to users in a second import step, where they will have the ability to review a sample of X records, and confirm the data format and correct mapping to the database fields. – Alex May 21 '12 at 20:39

3 Answers3

57

Use date_parse and check the values of the returned array

$date = date_parse("May")

// ["year"] == FALSE
// ["month"] == 5
// ["day"] == FALSE

You can also pass those into checkdate.

$date = date_parse($someString);
if ($date["error_count"] == 0 && checkdate($date["month"], $date["day"], $date["year"]))
    echo "Valid date";
else
    echo "Invalid date";
saluce
  • 13,035
  • 3
  • 50
  • 67
  • I think you got it... Can any one see any flaws with this? – Alex May 21 '12 at 20:54
  • A tiny flaw: `date_parse` uses the `strtotime` parsing rules, which are [well-defined but finicky](http://www.php.net/manual/en/datetime.formats.php). It's possible to submit data that returns a legal date but is not what was intended. – Charles May 21 '12 at 21:30
  • 2
    @Charles without requiring a well-defined format for input, any algorithm to parse a date automatically will have issues – saluce May 21 '12 at 23:20
  • 1
    I know this is old, but if you used `date_parse("2014-01-01 12:00:00'; select * from users;")` it will say the date is valid. However, the $date['error_count'] will be greater than 0, which might be a better option to go through. see http://ideone.com/Xl1HTb – Joseph Mar 20 '14 at 21:34
  • @joseph4tw Good point. I've updated the answer to include checking the error count on the `date_parse`. – saluce Mar 21 '14 at 17:08
  • 1
    @saluce `$date["errors"]` is an array, thus it will always be false. I think what you need is `$date["error_count"]` – Janis Peisenieks Apr 08 '14 at 10:32
5

I don't think there is a all-in-one answer to this problem. You may have different strategy depending on your use case.

Your strtotime() is a perfect solution, but as you say, you may end up with false positive. Why? Because may may be a word or a name. However, what is the result of strtotime('May')?

echo date(DateTime::ISO8601, strtotime('May'));
2012-05-21T00:00:00+0200

So giving only the month will return a date of the current year and the current day starting at midnight with the given month. A possible solution would be to check if your string has the current day and/or the current year included, this way, you may check against to make sure your date is a fully qualified date and valid.

echo date(DateTime::ISO8601, strtotime('May Day')); // (strtotime() returns false)
1970-01-01T01:00:00+0100

echo date(DateTime::ISO8601, strtotime('May 21'));
2012-05-21T00:00:00+0200

A simple strpos() or even a regex should do the trick.

However it is a bit odd and should be used only if you have no other way to do.

I believe that a better solution would be to define a set of valid format and interpolate the result to make sure that the date is valid.

$validDateFormatPatterns = array(
 '[0-9]{1,2}-[0-9]{1,2}-[0-9]{4}', // 21-05-2012, obviously this pattern is simple and would accept 05-21-2012,
 'the [0-9]{1,2}(th|st|nd|rd) (January|February|...|May|...|December) [0,9]{4}', // The 21st May 2012
);

You should try to cover most of the case and I'm sure you will be able to find regex that checks for most current date format.

In any case, you may need to adapt your function from time to time because there is no easy way to make it bulletproof.

Boris Guéry
  • 47,316
  • 8
  • 52
  • 87
1

I know this was asked a long time ago, but looking around for this and trying to avoid regex, I came up with this:

function checkInputIsDate($date) {
    return (bool)strpbrk($date,1234567890) && strtotime($date);
}

This works because it takes away the issues posted above where only a month is passed into strtotime by making sure there are numbers in the string with strpbrk as well as verifying strtotime outputs a date.

And learned about a function I didn't know existed.

Hope this helps someone.

Bryan
  • 3,449
  • 1
  • 19
  • 23