Thursday 22 August 2013

Not every single string that can possibly be parsed as a date should be treated like a date!

G'day:
Here's some date-parsing stupidity from CFML that James Buckingham put onto my radar.



In a change from usual, here's the output (and the code will follow):

English (UK)

Rawparsedlocale-aware parsed
30/2/13{ts '1930-02-13 00:00:00'}-
30 February 13{ts '1930-02-13 00:00:00'}-

Rawparsedlocale-aware parsed
28/2/13{ts '2028-02-13 00:00:00'}{ts '2013-02-28 00:00:00'}
28 February 13{ts '2013-02-28 00:00:00'}{ts '2013-02-28 00:00:00'}

English (US)

Rawparsedlocale-aware parsed
30/2/13{ts '1930-02-13 00:00:00'}{ts '1930-02-13 00:00:00'}
30 February 13{ts '1930-02-13 00:00:00'}{ts '1930-02-13 00:00:00'}

Rawparsedlocale-aware parsed
28/2/13{ts '2028-02-13 00:00:00'}{ts '2028-02-13 00:00:00'}
28 February 13{ts '2013-02-28 00:00:00'}{ts '2013-02-28 00:00:00'}


This is just bloody stupid (and I'm sure I've touched on it before), but there is not situation in which a string formatted as "xx/xx/xx" should be interpreted as a date "yy/mm/dd". No-one writes dates as "yy/mm/dd". They might write it "yy-mm-dd", but the only time slashes are used is when the date is in "dd/mm/yy" (if you're a normal person) or "mm/dd/yy" (if your a USAn person). But that formatting never means "year first". So CF should not accept "30/2/13" as a date, because there is no 30th of Feburary, nor is there a  month "Vigintioctober". So "30/2/13" is not a date, should fail validation checks, and error if one attempts to parse it as a date.

Ditto... CF can't go "well '30 February 13' means '1930 February 13th", but '28 February 13' means '28 February 2013'". No. Pick a format. it's either "dd mmmm yy" or "yy mmmm dd". Don't chop and change. You stupid language. And I severely doubt anyone on the planet would see a string of format "xx mmmm yy" as "yy mmmm dd" anyhow. I'm less sure of this than I am that "yy/mm/dd" doesn't exist, but moderately sure.

What was the person who wrote this validation / parsing code thinking: "I know, I'll accept any old shit and if there's even the slightest chance I can interpret it as a date, I will"? Because that's how it seems.

And that is stupid.

Here's the code:

for (locale in ["english (uk)", "english (us)"]){
    setLocale(locale);
    writeOutput("<h3>#getLocale()#</h3>");

    datesTable(["30/2/13","30 February 13"]);
    writeOutput("<br>");
    datesTable(["28/2/13","28 February 13"]);
    writeOutput("<hr>");
}

public void function datesTable(dates){
    writeOutput('<table border="1"><thead><tr><th>Raw</th><th>parsed</th><th>locale-aware parsed</th></tr></thead><tbody>');
    for (date in dates) {
        writeOutput("<tr><td>#date#</td>");
        if (isValid("date", date)){
            writeOutput("<td>#parseDateTime(date)#</td>");
        }else{
            writeOutput("<td>-</td>");
        }
        if (isValidDateForCurrentLocale(date)){
            writeOutput("<td>#parseDateTimeForCurrentLocale(date)#</td>");
        }else{
            writeOutput("<td>-</td>");
        }
        writeOutput("</tr>");
    }
    writeOutput('</tbody></table>');
}


public boolean function isValidDateForCurrentLocale(required string date){
    var locale = getLocale();
    var validator = "eurodate";
    if (locale == "english (us)"){
        validator = "date";        
    }
    return isValid(validator, date);
}

public date function parseDateTimeForCurrentLocale(required string date){
    var locale = getLocale();
    if (locale == "english (us)"){
        return parseDateTime(date);        
    }else{
        return lsParseDateTime(date);        
    }
}

I had to write those two little locale-aware functions at the bottom as I can't see a way of getting CFML to go "parse the string according to my current locale". I have to manually use either US-locale-specific functions, or non-US-locale-specific functions.

BTW, Adobe: it's not a eurodate, it's just "a date". It's the States that have got the weirdo formatting, not Europe (and, indeed, everywhere else in the English-speaking world). Canada is not in Europe. Australia is not in Europe. New Zealand... not in Europe. you get my drift. Gotta love hard-coded jingoism.

--
Adam