Short, Localized Dates and Four-Digit Years


Details matter.

When working on the date formatting portion of a global web app recently, I really wanted to get two details right.

  1. Localize the dates – People in different parts of the world are used to seeing dates displayed differently. Give the people what they expect.
  2. Four-digit years – Two-digit years are confusing. It's difficult enough to parse "10/12/2011" when you don't know whether the month or the day comes first. Parsing "10/12/11" just adds to the cognitive load.

I know the user's locale. PHP has IntlDateFormatter to handle localizing the dates. This should be easy.

But for space and legacy reasons, I needed a format like "10/11/2012". Well, IntlDateFormatter::SHORT gives "10/11/12". That's almost perfect. Almost. You know, except for those pesky, confusing two-digit years.

I ran a test for IntlDateFormatter::SHORT for a handful of different locales anyway. Maybe I'd get lucky.

en_US: 10/11/12
en_GB: 11/10/2012
de_DE: 11.10.12
fr_FR: 11/10/2012
ru_RU: 11.10.12
en_ZW: 11/10/2012
ps_AF: م. ۲۰۱۲/۱۰/۱۱

Kinda lucky. I guess. I mean, some of those locales nail it. But I need every locale to have a four-digit year. And I certainly can't define every localized format myself: I'm completely unfamiliar with how things are done in Afghanistan, Zimbabwe, or the 200 or so states in between.

I took a walk.

On the walk, I came up with a plan.

The plan: IntlDateFormatter can get and set the date formatting pattern. I can get the formatting pattern for a specific locale and coax it into using a four-digit year. Good. But I needed to tread lightly; I didn't want to mess with the date formatting pattern too much because I didn't want to, for example, turn a Russian date into something Russians don't understand.

Treading lightly meant changing any occurrence of yy to yyyy but being extra careful to only match exactly two 'y's, not four. Everybody Stand Back. I know regular expressions.

After much fiddling – because who among us can really write a regex correctly on the first try? – I ended up with preg_replace("/(?<!y)yy(?!y)/", "yyyy", $pattern). It seems to work.

And I ran the test again.

en_US: 10/11/2012
en_GB: 11/10/2012
de_DE: 11.10.2012
fr_FR: 11/10/2012
ru_RU: 11.10.2012
en_ZW: 11/10/2012
ps_AF: م. ۲۰۱۲/۱۰/۱۱

Hooray! Localized dates with four-digit years. Exactly the details I need.