DateTime Casts are Language Primitives

One thing that sometimes comes as a surprise (most recently as a comment on the PowerShell blog) is that DateTime casts are always parsed in the en-US DateTime format:

Casting to a DateTime object doesn't respect the current culture of the thread. For example:

> [Threading.Thread]::CurrentThread.CurrentCulture

LCID  Name

----  ----

2057  en-GB

> [DateTime] "01/02/03"

02 January 2003

> # Note US date interpretation.

This is an explicit design goal.

To prevent subtle internationalization issues from popping into your scripts, PowerShell treats "[DateTime] '11/26/2007'" (a date constant) like a language feature – just as it does [Double] 10.5 (a numeric constant.) Not all cultures use the decimal point as the fractions separator, but programming languages standardize on it. Not all cultures use the en-US DateTime format, resulting in millions of internationalization bugs when people don’t consider the impact of having their software run in those cultures.

Imagine a random script that you find on the internet. It was probably written on a system that uses a different culture than you or I. For the script to run correctly our machines, the author needs to write in a culturally-sensitive way:

1) The approach taken by traditional software development for internationalization:

$date = New-Object System.DateTime 2007,11,27,17,56,00

or (an imagined PowerShell syntax)

$date = [DateTime->pt-PT] "terça-feira, 27 de Novembro de 2007 9:34:50"

2) An approach that assumes a specific culture, which is the approach PowerShell takes:

$date = [DateTime] "11/27/2007 5:56:00 pm"

Approach #2 is much more user-friendly, as it minimizes what you need to remember. In approach #1, you always need to remember to specify your locale when you create the DateTime. In approach #2, you always need to remember to use the en-US format (as you already do when writing floating-point constants.)

The additional benefit of assuming a specific culture is that you are protected from people that do NOT write the script in a culturally-sensitive way. While some advanced scripters might be able to understand approach #1, most scripters (and even professional software developers) do not. History has shown us that software not explicitly TESTED for internationalization fails spectacularly. I don't mean "written for international markets," I mean TESTED for them.

When an admin bashes together a script because their hair is on fire, and then wants to share it later -- testing for other regions is not high on the list of things to do.

6 Responses to “DateTime Casts are Language Primitives”

  1. Matthew K writes:

    Thanks for the explanation Lee.

    I’d bumped into this issue because I was reading (UK style) dates from a text file, and converting them to DateTime objects by casting. I fixed it by using the line below — is this the best approach?

    $date = [DateTime]::Parse($string, $(Get-culture))

    It’s great that the PowerShell Team are making efforts to remove internationisation problems from scripts, but I’m not sure I agree that using the US date format "minimizes what you need to remember" — for me, it means slightly more to remember (and it’s very easy to forget, especially as some UK style dates will also be valid US style dates).

    But I do understand the need for a standard way to represent dates. Is there any reason that the US format was chosen over the ISO 8601 format (yyyy-mm-ddThh:mm:ss) as the preferred representation?

    Having just tested it, casting from the ISO 8601 date standard works (and is guaranteed to be unambiguous), so I might use that from now on — am I likely to run in to any problems with this approach?

    Thanks for your help,

    Matthew.

  2. Russ Pitcher writes:

    "When an admin bashes together a script because their hair is on fire…"

    However, When an admin from a country that doesn’t use US date formats bashes out a script becaus their hair is on fire, they’re less likely to remember that they’ll have to change all their dates to an odd different format! Surely that’s what the regional settings were designed to do in the first place.

    Working in the UK for a US company I have enough problems sorting out which format dates are written in without having to remember to use US dates for PowerShell and UK dates for everything else! Added to that

    Would it not be better to have the option to set a built-in variable at the top of your script to force the dates to a particular format just for that script, that way we could just add something like $ScriptDateCulture = "[en-us]"

  3. Lee Holmes writes:

    Matthew:

    It’s not actually the en-US DateTime format, it’s the format used by the "Invariant Culture" in the .NET Framework. Here are some of the rules for that pretend culture:

    PS> [Globalization.CultureInfo]::InvariantCulture.DateTimeFormat

    AMDesignator : AM
    Calendar : System.Globalization.GregorianCalendar
    DateSeparator : /
    FirstDayOfWeek : Sunday
    CalendarWeekRule : FirstDay
    FullDateTimePattern : dddd, dd MMMM yyyy HH:mm:ss
    LongDatePattern : dddd, dd MMMM yyyy
    LongTimePattern : HH:mm:ss
    MonthDayPattern : MMMM dd
    PMDesignator : PM
    RFC1123Pattern : ddd, dd MMM yyyy HH’:’mm’:’ss ‘GMT’
    ShortDatePattern : MM/dd/yyyy
    ShortTimePattern : HH:mm
    SortableDateTimePattern : yyyy’-‘MM’-‘dd’T’HH’:’mm’:’ss
    TimeSeparator : :
    UniversalSortableDateTimePattern : yyyy’-‘MM’-‘dd HH’:’mm’:’ss’Z’
    YearMonthPattern : yyyy MMMM
    AbbreviatedDayNames : {Sun, Mon, Tue, Wed…}
    ShortestDayNames : {Su, Mo, Tu, We…}
    DayNames : {Sunday, Monday, Tuesday, Wednesday…}
    AbbreviatedMonthNames : {Jan, Feb, Mar, Apr…}
    MonthNames : {January, February, March, April…}
    IsReadOnly : True
    NativeCalendarName : Gregorian Calendar
    AbbreviatedMonthGenitiveNames : {Jan, Feb, Mar, Apr…}
    MonthGenitiveNames : {January, February, March, April…}

    Russ: Yes, there is definitely a trade-off involved. If you never share your scripts, or use scripts / code samples from the internet, then this feature just appears to make things more difficult. A preference variable is one way to resolve the "scripts you share impacting others" problem, but it still means nearly every example you find on the internet would have internationalization bugs.

    Lee

  4. Johannes Rössel writes:

    I would have preferred ISO 8601 over that weird date format, to be frank. After all, it’s a default format, intended to be culture-neutral. Not all Powershell scripters are from the US and most people outside the US are usually getting a headache trying to grasp that format (I can never really remember the order of the items, although thinking "middle-endian" helps getting to find them out when confronted with such a date.

  5. Michael Klement writes:

    It’s been mentioned, but just to make it explicit (many moons later): you *can and should* use ISO 8601 formats such as `[datetime] ‘2003-01-02’` instead of the equivalent, but confusing-to-non-US-users `[datetime] ’01/02/03’` format.

  6. Terry Wrennall writes:

    I use $date = get-date 01/02/03 which is a nice datetime field and is culture aware
    although technically I’ll use date-date yyyy/MM/dd when I do my own logging it’s clearer for me

Leave a Reply