Search code examples
coldfusiondate-formatcfml

locale specific dateformatting buggy?


We have been running into date formatting issues for over a year now. We have a EU formatting in our database (dd/mm/yyyy) and we also want to output that on our website. Problem is that we're running the dates through the date formatting functions of coldfusion, to be certain we're always outputting our dates the same way (and for other reasons).

That's where it goes wrong. The code below outputs 2 different dates, where we would expect the same date.

<cfoutput>
    #LSDateFormat('01/02/2015', 'dd/mm/yyyy', 'nl_BE')# <br />
    #LSDateTimeFormat('01/02/2015', 'dd/mm/yyyy HH:nn', 'nl_BE')#
</cfoutput>

// output
// 01/02/2015
// 02/01/2015 00:00

I have tried on trycf.com, using all different available engines. Please explain to me what I'm doing wrong here. Or tell me this is a bug that no-one ever has mentioned. But I would prefer me being wrong here..


Solution

  • I think you are misunderstanding the 'format' functions. They are designed for presentation. Their purpose is to convert a date object into a string: ie LSDateTimeFormat (date , mask). The 'mask' is used to determine what that output string looks like, not to parse the input. Notice if you pass in a date object, NOT a string, it works exactly as you expected? The result is 01/02/2015 00:00

      dateObject = createDate(2015,2,1);
      writeDump("dateObject = "& LSDateTimeFormat(dateObject, 'dd/mm/yyyy HH:nn', 'nl_BE'));
    

    Yes, CF allows you to be lazy, and pass in a string instead. However, CF must still convert that string into a date object before it can apply the mask - and you have no control over how CF does that. When you use strings, you are essentially leaving the interpretation of that string entirely up to CF. In this case, CF interprets the ambiguous string "01/02/2015" according to U.S. date rules ie month first. That produces January 2, 2015. Hence why the output of the mask dd/mm/... is 02/01/2015 00:00. So in essence, what your code is really doing is this:

         // parse string according to U.S. rules - mm/dd/yyyy
         HowCFInterpretsYourString = parseDateTime(dateString);
         LSDateTimeFormat(HowCFInterpretsYourString, 'dd/mm/yyyy HH:nn', 'nl_BE');
    

    Results:

         HowCFInterpretsYourString = {ts '2015-01-02 00:00:00'} <=== January 2nd 
         LSDateTimeFormat          = 02/01/2015 00:00           <=== Day = 2, Month = 1
    

    If you do not want CF doing the interpretation for you, pass in date objects - not strings.

    As for why LSDateFormat's behavior seems inconsistent with LSDateTimeFormat, I do not know. However, strings are ambiguous. So when you use them instead of date objects, well ... expect the unexpected.

    We should just convert to the correct date object first and then format using the normal dateFormat method.

    Just because you are only using the format functions to output the numeric date parts, does not mean that is all they do ;-) The format functions also output names, which are locale specific. For example, "MMMMM" might produce "September" or "septiembre" depending on the current locale. There are also other region specific rules, such as the placement of "month" and "day" and the exact capitalization of names. The standard Date/TimeFormat functions always use U.S. date conventions. Whereas LSDateTimeFormat uses whatever locale is supplied. In this specific case, there is not much difference because you are only outputting the numeric date parts:

    Numeric date parts (only)

    dateObject = createDate(2015,2,1);
    writeDump("LSDateTimeFormat = "& LSDateTimeFormat(dateObject, 'dd/mm/yyyy', 'nl_BE'));
    writeDump("DateTimeFormat = "& DateTimeFormat(dateObject, 'dd/mm/yyyy'));
    

    Results:

    LSDateTimeFormat = 01/02/2015 
    DateTimeFormat = 01/02/2015 
    

    However, for other formats there is a big difference. A date object may not be tied to a locale, but a string representation of a date is .. so the two functions are not interchangeable.

    Date Names:

     dateObject = createDate(2015,2,1);
     writeDump("LSDateTimeFormat = "& LSDateTimeFormat(dateObject, 'full', 'nl_BE'));
     writeDump("DateTimeFormat = "& DateTimeFormat(dateObject, 'full'));
    

    Results:

    LSDateTimeFormat = zondag 1 februari 2015 0.00 u. UTC 
    DateTimeFormat = Sunday, February 1, 2015 12:00:00 AM UTC 
    

    "EU formatting in our database (dd/mm/yyyy)"

    Not sure what you mean by that. Date/time objects do not have a format. Your IDE may display them as human friendly strings, but the date values themselves are stored as numbers. Based on what you described, it sounds like either the values are stored as strings OR are being converted to strings, which would explain the results. Instead, store the values in a date/time column, then retrieve them and pass them into the function "as is" and it should work fine.