I wish this were generally possible, but POSIX locale categories have only so many formats, most of which correspond to more historic formats/scenarios, but if you get d_fmt and t_fmt right, things seem to improve generally. Some time ago, I’ve created one that comes as suitably close to a full ISO 8601 locale as possible on a Debian GNU/Linux (glibc) system.
The way to do this correctly is not entirely trivial. You’ve got to create a full locale of your own, even if you only use it for LC_CTIME. You’ve got to edit some values manually, but only for date/time; you can, thankfully, just include the defaults for the others.
I’ve done this for de_DE (or rather de_DE.UTF-8) as base locale. The resulting locale definition file looks as follows (but read on below):
comment_char % escape_char / % $MirOS: contrib/hosted/tg/deb/de_DE@iso8601,v 1.1 2021/04/05 21:13:47 tg Exp $ %- % This is an alternative locale definition file, GNU libc compatible, % which provides an LC_TIME category that is mostly conformant to DIN % 5008 and ISO 8601. It uses %-d as an extension (use the alternative % date_fmt if not supported); the timezone offset is rendered without % colon due to https://bugs.debian.org/799476 which is not proper but % the best we can currently do; date +'%Y-%m-%dT%H:%M:%S%:z' works. % % Install with: % $ sudo localedef -i de_DE@iso8601 -f UTF-8 -c de_DE.UTF-8@iso8601 % glibc LC_IDENTIFICATION title "German locale for Germany with DIN ISO 8601 date/time format" source "BOSng" contact "mirabilos" language "German" territory "Germany" revision "1.0" date "2021-04-04" category "i18n:2012";LC_IDENTIFICATION category "i18n:2012";LC_CTYPE category "i18n:2012";LC_COLLATE category "i18n:2012";LC_TIME category "i18n:2012";LC_NUMERIC category "i18n:2012";LC_MONETARY category "i18n:2012";LC_MESSAGES category "i18n:2012";LC_PAPER category "i18n:2012";LC_NAME category "i18n:2012";LC_ADDRESS category "i18n:2012";LC_TELEPHONE category "i18n:2012";LC_MEASUREMENT END LC_IDENTIFICATION % POSIX LC_CTYPE copy "de_DE" END LC_CTYPE % POSIX LC_COLLATE copy "de_DE" END LC_COLLATE % POSIX LC_TIME abday "So";"Mo";"Di";"Mi";"Do";"Fr";"Sa" day "Sonntag";/ "Montag";/ "Dienstag";/ "Mittwoch";/ "Donnerstag";/ "Freitag";/ "Samstag" abmon "Jan.";/ "Feb.";/ "M<U00E4>rz";/ "Apr.";/ "Mai";/ "Juni";/ "Juli";/ "Aug.";/ "Sep.";/ "Okt.";/ "Nov.";/ "Dez." mon "Januar";/ "Februar";/ "M<U00E4>rz";/ "April";/ "Mai";/ "Juni";/ "Juli";/ "August";/ "September";/ "Oktober";/ "November";/ "Dezember" % this *should* be the first line; GNU cannot even do its own extensions right %d_t_fmt "%Y-%m-%dT%H:%M:%S%:z (%Z), %G-W%V-%u (%a)" d_t_fmt "%Y-%m-%dT%H:%M:%S%z (%Z), %G-W%V-%u (%a)" d_fmt "%Y-%m-%d" t_fmt "%H:%M:%S" am_pm "";"" t_fmt_ampm "" week 7;19971130;4 first_weekday 2 first_workday 2 cal_direction 1 % use the second line if %-d does not work for you date_fmt "%A, %-d. %B %Y, %H:%M:%S %Z" %date_fmt "%a %Y-%m-%d, %H:%M:%S %Z" END LC_TIME % POSIX LC_NUMERIC copy "de_DE" END LC_NUMERIC % POSIX LC_MONETARY copy "de_DE" END LC_MONETARY % POSIX LC_MESSAGES copy "de_DE" END LC_MESSAGES % glibc LC_PAPER copy "de_DE" END LC_PAPER % glibc LC_NAME copy "de_DE" END LC_NAME % glibc LC_ADDRESS copy "de_DE" END LC_ADDRESS % glibc LC_TELEPHONE copy "de_DE" END LC_TELEPHONE % glibc LC_MEASUREMENT copy "de_DE" END LC_MEASUREMENT
If you wish to do this for some other locale, such as es_ES, you’ll need to make the following modifications to it (in my example, I’m unashamedly copying from glibc localedata/locales/es_ES):
- change
title, language and territory in the head (and the others, source, contact, revision and date as well while there) to suitable values (e.g. language Spanish, territory Spain) - if not using glibc, you might need to remove a few entries commented with
% glibc (such as LC_NAME), but don’t if your system does support them (the -c flag to localedef should also make it accept that, but YMMV) - change all the
copy "de_DE" lines (there’s a lot of them!) to copy "es_ES" - change
abday to a list of weekday name abbreviations (starting with Sunday), e.g. "dom";"lun";"mar";"mi<U00E9>";"jue";"vie";"s<U00E1>b" (here you have also the chance to use 2‑letter or 3‑letter abbreviations to suit your taste) - change
day to a list of full weekday names, e.g. "domingo";"lunes";"martes";"mi<U00E9>rcoles";"jueves";"viernes";"s<U00E1>bado" - change
abmon and mon to abbreviated and full month names ("ene";"feb";… and "enero";"febrero";…) - save as
es_ES@iso8601 - change the comment near the beginning appropriately, the compile command is now
sudo localedef -i es_ES@iso8601 -f UTF-8 -c es_ES.UTF-8@iso8601
Then, you can export LC_TIME=es_ES.UTF-8@iso8601 to use this. Alternatively, if you do not have superuser priviliegues, you can use LOCPATH (or MUSL_LOCPATH) to install the locale data locally; for glibc, this looks as follows:
mkdir -p ~/.local/locpath localedef -i es_ES@iso8601 -f UTF-8 -c ~/.local/locpath/es_ES.UTF-8@iso8601
Thereafter, export LOCPATH=$HOME/.local/locpath and LC_TIME=es_ES.UTF-8@iso8601 to use.
For other languages, countries, encodings, etc. proceed correspondingly.
In theory, you can also set LC_ALL to it, since it copies the remaining locale category definitions from the corresponding master file for the chosen locale.