Rozmohl se nám tady takový nešvar. Vývojáři velice často opravují chyby, na které jsme si už zvykli a považujeme je za standard.
Jednou z takových chyb je výpis kalendářního data v češtině. Požádáme-li standardní knihovní funkcistrftime(3)
modifikátorem %B
o plné jméno měsíce v aktuálním národním prostředí, bývalo zvykem, že funkce vypíše název měsíce v češtině, bez velkého počátečního písmena (na rozdíl od angličtiny se názvy měsíců a dnů v týdnu česky píší bez velkých písmen) a v prvním pádu.
Použití prvního pádu není pro zápis kalendářního data příliš vhodné, jména měsíců běžně zapisujeme a čteme v druhém pádu. Na druhou stranu, má-li aplikace vypsat jméno měsíce jako takové, je použití prvního pádu zcela na místě. Jednoduché nahrazení všech jmen měsíců druhým pádem tedy není bez rizika.
K právě takové změně relativně nedávno v projektu glibc došlo. Knihovna jazyka C, kterou používá v podstatě každá aplikace, změnila názvy měsíců z prvního na druhý pád. Původní názvy měsíců jsou nyní uloženy v nestandardní proměnné alt_mon
. Došlo tak k nastolení souladu s globální databází CLDR, která definuje měsíce v druhém pádu pro formátované datum a zároveň v prvním pádu pro samostatně stojící název měsíce. Změna je v glibc verze 2.28, která vyšla 1. srpna 2018. Některé distribuce ji ale zahrnují i do udržovacích vydání předchozí verze, konkrétně v Gentoo jde o verzi 2.27-r6.
Chování je nepředvídatelné
Jakkoli je oprava chvályhodná, mění chování, které bylo po řadu let standardem a pro aplikace je obtížné poznat, zda aktuální systém používá verzi knihovny před opravou nebo po ní. Spustíte-li tedy například utilitucal
na systému, kde byla nedávno aktualizována knihovna glibc, výstup nebude úplně ideální:
$ cal -3 prosince 2018 ledna 2019 února 2019 Po Út St Čt Pá So Ne Po Út St Čt Pá So Ne Po Út St Čt Pá So Ne 1 2 1 2 3 4 5 6 1 2 3 3 4 5 6 7 8 9 7 8 9 10 11 12 13 4 5 6 7 8 9 10 10 11 12 13 14 15 16 14 15 16 17 18 19 20 11 12 13 14 15 16 17 17 18 19 20 21 22 23 21 22 23 24 25 26 27 18 19 20 21 22 23 24 24 25 26 27 28 29 30 28 29 30 31 25 26 27 28 31
Přitom právě nástroj cal
dělá všechno pro to, aby použil alternativní názvy měsíců – takové, které se hodí pro samostatně stojící název měsíce. Během kompilace testuje, zda knihovna glibc alternativní názvy podporuje. Pokud ano, použije je, jinak použije běžné. Protože se ale detekce provádí pouze během kompilace, je nutné po aktualizaci knihovny glibc překompilovat i balíček util-linux
, jehož součástí cal
je. Pak budou měsíce opět zobrazovány v prvním pádu.
Jak vlastně funguje nastavení lokalizace
Každý admin ví, že pro nastavení lokalizace slouží několik proměnných prostředí. První jménem LANG
má nejnižší prioritu a představuje výchozí preferenci uživatele. Následuje několik proměnných začínajících LC_
, které specifikují nastavení konkrétní oblasti – například LC_TIME
ovlivňuje právě lokalizaci související s datem a časem. Nejvyšší prioritu má pak proměnná LC_ALL
, která přebíjí všechny ostatní, a proto je dobré ji až na výjimečné případy nenastavovat.
Co už ale není úplně zřejmé, je fakt, že bez ohledu na nastavení proměnných každá aplikace startuje ve výchozím nastavení lokalizace. Aby se začala chovat podle nastavení v proměnných prostředí, musí k tomu aplikace zavolat funkcisetlocale(3)
s prázdným řetězcem na místě názvu lokalizace. Jsou-li k dispozici i alternativní názvy měsíců, je možné je získat formátovacím řetězcem %OB
funkce strftime(3)
– toto chování bylo převzato z FreeBSD. Na staré verzi glibc ale tento formátovací retězec nefunguje.
Vytvořil jsem demonstrační příklad. Spuštěný na staré verzi glibc vypíše:
Locale: C Format string: %B Result: January, February, March, April, May, June, July, August, September, October, November, December Format string: %OB Result: %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB Locale: cs_CZ.UTF-8 Format string: %B Result: leden, únor, březen, duben, květen, červen, červenec, srpen, září, říjen, listopad, prosinec Format string: %OB Result: %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB, %OB
Po aktualizaci glibc ten samý program vypíše měsíce v druhém i prvním pádu:
Locale: C Format string: %B Result: January, February, March, April, May, June, July, August, September, October, November, December Format string: %OB Result: January, February, March, April, May, June, July, August, September, October, November, December Locale: cs_CZ.UTF-8 Format string: %B Result: ledna, února, března, dubna, května, června, července, srpna, září, října, listopadu, prosince Format string: %OB Result: leden, únor, březen, duben, květen, červen, červenec, srpen, září, říjen, listopad, prosinec
Přechodné období nebude snadné
Nová verze knihovny glibc zcela jistě směřuje správným směrem. Díky přítomnosti obou variant názvů měsíců je možné nově používat knihovní funkce i v aplikacích, kde záleží na kvalitě české jazykové verze; použití knihovní funkce namísto vlastního řešení v aplikační logice nepochybně usnadní výrobu dalších jazykových verzí, kde v ideálním případě bude stačit přepnutí proměnné LANG
.
V následujících několika letech nás ale bohužel čeká období nestability, kdy se nebude možné spolehnout na tvar názvu měsíce. Aplikacím, které závisí na precizním výstupu, tak nezbývá než knihovní funkce nepoužívat, dokud se situace nestabilizuje.