Interesting Times

Alright, it’s been a while. But here’s a post that I wanted to do a few weeks back already, but didn’t get around to do it. But in the meantime, There were a few more ideas being tossed about, so now it’s about time I write this.

So, here’s what it’s about: As a software developer, I have almost anything computer-related set to english. Unfortunately, the english dialects that are acceptable (being en_US and en_GB) both have funny date strings that I do not like. At all. American “month-day-year” is just as stupid as day/month/year, especially because americans also use month/day/year, so it’s never that clear.

What I want is to have a date string in a format that is used around here, which is day.month.year. Or I might contemplate to display it in a proper ISO format — year-month-day. So What I did is I read up a bit on locales, how they’re generated, and how I could create my own locale setting for date/time display. Here’s a nice page about locales.

Turns out that there are a few caveats to be aware of, but other than that, it’s actually quite easy. Here’s how it goes (on Arch Linux, at least. Others should be similar): The various formats are described in files located in /usr/share/i18n/locales, and are then converted into a system-usable format using the locale-gen command. To use the new locale, you can set any of the LC_* variables as you like. But step after step now.

Messing with locale formats

First, copy the locale file that is closest to what you like to a custom file. I’ve used the following:

~ % cd /usr/share/i18n/locales
/usr/share/i18n/locales % sudo cp en_GB en_XX

Now we can edit this file. Don’t worry too much about the format, for now, I’ll explain it in a second. Note that all the localized strings are strictly noted using a very verbose unicode notation.

Now what we want to change is the date format. This is described in the LC_TIME locale setting, so we look up that section (Just search for the line starting with LC_TIME). What we find there is a listing that describes various representations of time and date, like abday (Weekday names, abbreviated), day (Weekday names), and so on.

What we are interested in, are the d_t_fmt, d_fmt, and date_fmt settings. They describe the datetime and date formats, respectively.

If you decode the unicode string (man ascii is helpful), you find that the ones from the en_GB locale translate to something like this: date_fmt becomes “*%a %b %e %H:%M:%S %Z %Y“, d_t_fmt becomes “%a %d %b %Y %T %Z“, and d_fmt becomes “%d/%m/%y*“. These look a lot like strftime() strings, so we look them up in the man page.

Instead of reordering and re-encoding the strings, we’re playing lazy: Why not copy the formats over from a locale where they already look the way we want? Let’s do this (I assume you know how to open the de_CH file (or whatever you like) and copy over the relevant lines).

Compiling the locale

Having done this, we need to activate that locale, so the locale-gen tool knows that it has to compile it, too.

~ % sudo su -
~ # echo en_XX.UTF-8 UTF-8 >> /etc/locale.gen

Note that you only need to do this once.

Now, to compile the locales, call locale-gen. You should get some output like this:

~ # locale-gen
Generating locales...
  de_CH.UTF-8... done
  en_GB.UTF-8... done
  en_US.UTF-8... done
  en_XX.UTF-8... done
Generation complete.

As you see, our new locale is generated. Remember: Each time you mess with the original locale file, you have to re-run locale-gen again.

Quality control

Of course, you’re now eager to see if our messing resulted in something usable. Let’s test it with the date command, first using the old locale, then with the new one:

~ % echo $LANG
en_GB.UTF-8
~ % date +%x
09/11/10

So, this is our old date format. Now let’s switch to the new format:

~ % export LC_TIME=en_XX.UTF-8
~ % date +%x
09.11.2010

Nice, huh? Now, if course we would like to have this setting permanent, right? Let’s do it.

Making it permanent

Since locale settings are controlled using environment variables, it should be fairly easy to add them to the profile init script, usually /etc/profile. Arch Linux supports a directory where you can put your settings in your own file, so it won’t get changed on a system update.

So let’s create this file and put it in /etc/profile.d:

~ % sudo su -
~ # cd /etc/profile.d
/etc/profile.d # echo "export LC_TIME=en_XX.UTF-8" > zz_locale.sh
/etc/profile.d # chmod +x zz_locale.sh

This should be all you need… but wait! Since those files are only evaluated on a new login, you need to ssh into your machine to test it — or log out and back in.

~ % ssh localhost
Last login: Thu Nov 11 20:42:52 2010 from 192.168.1.80
~ % date +%x
09.11.2010

YAY! It works!

GIEF ME TEH FUNNEH!

A day or two ago, the idea came up to extend this idea to mess around a bit with weekday and month names. As a great fan of funny cats, it’s almost a sacred duty to change “Saturday” to “Caturday” — and so I did. I’m not gonna explain how to edit the locale file, just what I did:

  1. Edit the /usr/share/i18n/locales/en_XX file again
  2. Find the abday section
  3. In the last entry, change <U0053> (which is unicode for S) to <U0043> (which is C)
  4. Find the day section
  5. Again, in the last entry, do the same change.
  6. Run locale-gen
  7. Set the date to a saturday caturday, and enjoy:

    % sudo date –set=‘2010-11-20’ Cat Nov 20 00:00:00 CET 2010

In my setup, I also changed “Sunday” to “Sleepday”. This needs a bit more work, but here’s the unicode string for Sleepday:

<U0053><U006C><U0065><U0065><U0070><U0064><U0061><U0079>

Moar funneh

The thing that kicked off the need to write this post a few days ago, was actually this post here: howto create your own timezone. I don’t want to go as far as creating my own time zone. But the sleep talking man knows that it’s cake o’clock all day long, and we need to be reminded of this as often as possible. And here we go — reading up on the TZ documentation first:

~ % export TZ="CAKEOCLOCK-1CAKEOCLOCK-2,M4.1.0/2,M10.5.0/2"
~ % sudo date --set="2010-07-20 12:30 UTC"
Tue Jul 20 14:30:00 CAKEOCLOCK 2010
~ % sudo date --set="2010-12-20 12:30 UTC"
Mon Dec 20 13:30:00 CAKEOCLOCK 2010

The above format defines normal and daylight saving time, naming both CAKEOCLOCK. I had a difficult time figuring out the format, as I expected that I had to put spaces where the TZ documentation description put them, but the example shows that there are no spaces at all. Old school parser, huh?

Now we already have a global file that messes up time data, so why not add it there, too?

~ % sudo su -
~ # echo 'export TZ="CAKEOCLOCK-1CAKEOCLOCK-2,M4.1.0/2,M10.5.0/2"' >> /etc/profile.d/zz_locale.sh

Okay, that’s about enough for today. Good night!