Validate different date formats

3

I'm having trouble validating different date formats. I need to convert the dates to the Y-m-d format to save to the bank.

The dates come like this:

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

I have tried the simple way:

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
    
asked by anonymous 12.11.2014 / 23:02

3 answers

6

There are several ways to resolve this depending on your need. One of these is this:

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

See running on ideone . And no Coding Ground . Also put it on GitHub for future reference .

Under normal conditions the strtotime function accepts date in Y-m-d format. By doing this conversion to a European format (with the dash in place of the slash) it accepts in d-m-Y .

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

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

    
12.11.2014 / 23:16
9

PHP 5> = 5.3.0

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

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

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

Example on Ideone .

In detail

To clarify, any solution to your current problem involves doing the date conversion 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 terms 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 means of converting the date where you indicate the input format along with the date, and can then output 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 on Ideone .

    
12.11.2014 / 23:18
3

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

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

So, I offer the function below, created by a forum friend introduced here in the other topic community:

/**
 * 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);
}

To test:

$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 );
}

Output:

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 Zuul's solution is not enough, try experimenting with this same array of random dates to create a DateTime object without changing 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 the d / m / Y format whereas the function presented here takes into account the eight major date / time formats. >

As explained in the comments in this answer the function presented here causes 11/12/2014 to return 2014-12-11 which although validly programmatically does not compute with the required logic.

This is because the function primarily takes dates into the Brazilian DAY / MONTH / YEAR format and as foreach is interrupted when a format is successfully created by a DateTime object it 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',

);

You have 11/12/2014 returned 2014-12-11 and 10/30/2014 return 2014 -10-30 .

One more consideration:

In the same way that you can and should reorder the array of formats as best you can, you can also include new formats specific to your needs.

As the first of your dates is in a format valid for a date but not considered in the available formats (m / d / Y), simply 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 keep in mind that both this solution and Zuul's proposal 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 if a given component of a date is a day, a month, or a year.

Placing the new m / d / Y format at the beginning of $ formats will return what you expect for the first date. But for the second it would return 2016-06-10 , which I imagine to be a DateTime issue itself because there is no month 30.

    
12.11.2014 / 23:40