Validate different date formats

Asked

Viewed 1,361 times

3

I am having difficulty validating different date formats. I need to convert the dates to the format Y-m-d to save in the bank.

The dates come like this:

11/12/2014 // MÊS/DIA/ANO
30/10/2014 // DIA/MÊS/ANO

I’ve tried it simple:

echo date('Y-m-d', strtotime('11/12/2014')); // 2014-11-12 = OK
echo date('Y-m-d', strtotime('31/10/2014')); // 1970-01-01 = FAIL
  • Other ways to convert: http://answall.com/q/21774/91

  • 5

    Unfortunately it is impossible to determine exactly what kind of date you are receiving when they are ambiguous... You will have to choose which type should be considered the standard... (Checking which one occurs most often)

  • 1

    The problem when storing in the database is due to what has already been discussed in the answers. However, you should not add problems to your current question because the answers are no longer valid, you should open a new question to deal with the database problem.

  • @Zuul done, thank you

3 answers

9

PHP 5 >= 5.3.0

You can make use of the functions of Datetime, particularly Datetime::createFromFormat:

$date = DateTime::createFromFormat('d/m/Y', '31/10/2014');

echo $date->format('Y-m-d');    // Output: 2014-10-31

Example in Ideone.


In detail

To clarify, any solution to your current problem is to convert the date but indicating the format in which it is. It is impossible to detect whether the input format is MM/DD or DD/MM.

The exception would be in cases where the value is greater than 12 where logically it is a given day only 12 months. But even so, nothing guarantees that there was no mistake on the part of the user when entering the value for the month.

The above solution gives you a way to convert the date where you indicate the input format along with the date, and can then generate the output as desired.

To illustrate your case:

// entrada com MÊS / DIA / ANO
$date1 = DateTime::createFromFormat('m/d/Y', '11/12/2014');

echo $date1->format('Y-m-d');    // Output: 2014-11-12

// entrada com DIA / MÊS / ANO
$date2 = DateTime::createFromFormat('d/m/Y', '31/10/2014');
echo $date2->format('Y-m-d');    // Output: 2014-10-31

Example in Ideone.

  • thank you for the force

6


There are several ways to solve this depending on your need. One of them is this:

echo date('Y-m-d', strtotime(str_replace('/', '-', '31/10/2014')));

Behold working in the ideone. And in the repl it.. Also put on the Github for future reference.

Under normal conditions the function strtotime() accepted date in format Y-m-d. Making this conversion to a European format (with the dash instead of the bar) it accepts in d-m-Y.

This is not the best solution (there may be ambiguities) but it is one that approaches the way you are using.

It is good to read the other answers and the comment in the question. You may have problems with the incoming format this way unless you can guarantee before that everything is correct, which seems unlikely to me that this is happening. You are solving your problem of the moment but may cause others in the future if you do not guarantee the format.

  • thank you!..........

3

It seems you have a more serious problem than just date formatting: because they are standardized or standardized in a single type/input format.

As this does not fit the scope of the topic, if I have assumed correctly, the solution presented by Zuul, although correct from the programmatic point of view, is not enough because it takes into account a single date format.

Therefore, I offer the function below, created by a forum friend introduced here in the community on another topic:

/**
 * Altera uma data para outro formato
 *
 * @param string $date String contendo a data a ser formatada
 * @param string $outputFormat Formato de saida
 * @throws Exception Quando não puder converter a data
 * @return string Data formatada
 * @author Hugo Ferreira da Silva
 */
function parseDate($date, $outputFormat = 'd/m/Y'){
    $formats = array(
        'd/m/Y',
        'd/m/Y H',
        'd/m/Y H:i',
        'd/m/Y H:i:s',
        'Y-m-d',
        'Y-m-d H',
        'Y-m-d H:i',
        'Y-m-d H:i:s',
    );

    foreach($formats as $format){
        $dateObj = DateTime::createFromFormat($format, $date);
        if($dateObj !== false){
            break;
        }
    }

    if($dateObj === false){
        throw new Exception('Invalid date:' . $date);
    }

    return $dateObj->format($outputFormat);
}

For testing:

$testDates = array(
    '2012-10-30 00:00:00',
    '06/01/1986 14',
    '06/12/1983 14:30:10',
    '1984-01-06 14:30:10',
);

foreach($testDates as $date){
    var_dump( parseDate($date, 'Y-m-d') );
    //var_dump( DateTime::createFromFormat('d/m/Y', $date) instanceof DateTime );
}

Exit:

string '2012-10-30' (length=10)
string '1986-01-06' (length=10)
string '1983-12-06' (length=10)
string '1984-01-06' (length=10)

Just to illustrate why the solution of friend Zuul is not enough to experiment with this same array of random dates to create a Datetime object without change the format:

foreach($testDates as $date){
    var_dump( DateTime::createFromFormat('d/m/Y', $date) instanceof DateTime );
}

All four iterations result in FALSE because none of them are in format d/m/Y whereas the function presented here takes into account the eight main date/time formats.

As explained in the comments of this answer the function presented here makes return 11/12/2014 2014 2014-12-11 that although valid programmatically does not compute with the required logic.

This is because primarily the function takes into account dates in the Brazilian DAY/MONTH/YEAR format and as the foreachis stopped when a format creates a Datetime object successfully results in a false positive.

The solution is quite simple, just reorder the array $formats in the way that suits you best.

For example:

$formats = array(
    'Y-m-d',
    'Y-m-d H',
    'Y-m-d H:i',
    'Y-m-d H:i:s',
    'd/m/Y',
    'd/m/Y H',
    'd/m/Y H:i',
    'd/m/Y H:i:s',

);

Already makes it 11/12/2014 return 2014-12-11 and 30/10/2014 return 2014-10-30.

One more consideration:

Just as you can and should reorder the format array as you think best you can also include new formats specific to your need.

Since the first of your dates is in a format valid for a date but not considered in the available formats (m/d/Y), just add it to the array:

$formats = array(
    'm/d/Y',        // <---
    'Y-m-d',
    'Y-m-d H',
    'Y-m-d H:i',
    'Y-m-d H:i:s',
    'd/m/Y',
    'd/m/Y H',
    'd/m/Y H:i',
    'd/m/Y H:i:s',
);

But bear in mind that both this solution and the one proposed by Zuul will fail according to the logic you expect because your dates are not normalized.

As far as I know there is no way to automatically detect whether a given component of a date is a day, a month or a year.

Putting the new format m/d/Y at the beginning of $formats will return what is expected for the first date. But for the second return 2016-06-10, which I imagine to be a problem of Datetime itself because there is no month 30.

  • Good morning @Brunoaugusto, this function is very good, however! <br/> as example above date it returns 11/12/2014 // 2014-12-11 despite being a valid date, the more the correct of this in question would be 2014-11-12 <br/> and if I add in the 1st row of the date formats m/d/Y so that it returns the correct one to another date is wrong 31/10/2014 // 2016-07-10

  • 1

    Observing: Although it is a good answer, since it is the same as this other answer, effectively the procedure you should adopt would be to close this question as duplicate and not duplicate your answer!

  • 1

    Regarding your answer, it does not meet the requirements of the question, see example in Ideone to check. The question deals with the problem where a date comes as MÊS / DIA / ANO and the other date comes as ANO / MÊS / DIA. The function you suggest in its current form fails and converts the data incorrectly. This role of yours may be good in the answer from where you extracted it, but for this scenario does not meet the problem in question. Note: I did not deny the answer, but if you do not adjust it, since it is incorrect in its current form, I will be forced to do so :)

  • There is no need to adjust, only to complement the answer. This is because the function takes into account primarily dates in the Brazilian format d/m/Y. But if you invert the variable formats $formats putting the Y-m-d before the result changes and, 11/12/2014 now returns 2014-12-11

  • @Brunoaugusto Correct, I suggest an update to your sample code because in its current form, within the foreach() does not express well your suggestion to use this function. Perhaps two examples, one for each date in the question, each with proper output format ;)

  • 1

    I was writing, but you two write faster than me >.<

  • Yes @Brunoaugusto, have you read my 1st comment? if I invert the date format the other date becomes incorrect.

  • I supplemented the answer again

  • @Brunoaugusto, thank you for your strength

Show 4 more comments

Browser other questions tagged

You are not signed in. Login or sign up in order to post.