PHP Date – Polskie nazwy dni i miesięcy. Funkcje date strftime

Programując w PHP czasami natykamy się na problemy operując na datach w języku polskim. Chcąc uzyskać nazwy miesięcy i/lub dni tygodnia nie zawsze możemy polegać na wbudowanych w PHP funkcjach. Tym bardziej jeśli w grę wchodzi ich odmiana. Bardzo łatwo możemy sobie z tym poradzić. Wystarczy dopisać swoją funkcję.

Konfiguracja wbudowanych funkcji

Do dyspozycji mamy funkcje date() oraz strftime(). Pierwsza z nich niestety nie zwraca daty z lokalnymi nazwami. Za to strftime() jest możliwe do ustawienia.

Co należy ustawić, aby domyślnie PHP korzystał z lokalizacji polskiej?

$arrLocales = array('pl_PL', 'pl','Polish_Poland.28592');
setlocale( LC_ALL, $arrLocales );

Funkcja setlocale() ustawia opcje regionalne. Za pierwszy parametr może przyjmować:

  • LC_ALL dla wszystkich poniżej
  • LC_COLLATE ciąg dla porównania, patrz strcoll ()
  • LC_CTYPE na charakter i klasyfikację konwersji, na przykład strtoupper ()
  • LC_MONETARY dla localeconv ()
  • LC_NUMERIC dla separator (Patrz także localeconv ())
  • LC_TIME do formatowania daty i czasu z strftime ()
  • LC_MESSAGES dla systemu odpowiedzi (dostępna jeśli PHP zostało skompilowane z libintl)

Dzięki temu zabiegowi ustawiliśmy język polski jako domyślny. Jednak problemy zaczynają się w przypadku, gdy zechcemy użyć kodowania innego niż ISO-8859-2 (które jest ustawiane poprzez Polish_Poland.28592'). Musimy niestety przekonwertować zwracane dane z ISO-8859-2 na UTF-8. Zrobimy to za pomocą prostej funkcji:

function strftimeV($format,$timestamp){
	return iconv("ISO-8859-2","UTF-8",ucfirst(strftime($format,$timestamp)));
}

Użycie:

<?php
echo strftimeV('%A %d %B %Y',strtotime('2009-09-02'));
?>

Daje w wyniku Środa 02 wrzesień 2009, co jest troszeczkę nie po polsku.

Utworzenie własnej funkcji obsługi formatu daty

Pozostaje nam stworzenie własnej funkcji, która odpowiednio zamieni format.
Na początku skryptu ustawiamy domyślną strefę czasową używaną przez funkcje date/time:

date_default_timezone_set('Europe/Warsaw');

Następnie tworzymy naszą funkcję, która odpowiednio przekonwertuje standardowy format daty dla funkcji date(). Najlepiej aby była zgodna, tak aby w przypadku zamiany jej na zwykłą date() nie trzeba było przepisywać formatów. Niestety w języku polskim mamy odmianę, co skutecznie uniemożliwia nam pełną zgodność. Problem pojawia się właściwie tylko w przypadku miesięcy, dni tygodnia zwykle używa się w formie podstawowej, natomiast miesiące już różnie. Raz jest styczeń, innym razem stycznia. Aby móc skorzystać z kilku form będziemy musieli rozszerzyć format daty o własne znaczniki.
Interesujące nas znaczniki formatu daty to l (małe L) oraz F. Dodamy własne oznaczenie f, Które nie występuje na liście parametrów formatu date. Utwórzmy zatem własną funkcję dateV() – czyli funkcję dateVokiel() 😉

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
function dateV($format,$timestamp=null){
	$to_convert = array(
		'l'=>array('dat'=>'N','str'=>array('Poniedziałek','Wtorek','Środa','Czwartek','Piątek','Sobota','Niedziela')),
		'F'=>array('dat'=>'n','str'=>array('styczeń','luty','marzec','kwiecień','maj','czerwiec','lipiec','sierpień','wrzesień','październik','listopad','grudzień')),
		'f'=>array('dat'=>'n','str'=>array('stycznia','lutego','marca','kwietnia','maja','czerwca','lipca','sierpnia','września','października','listopada','grudnia'))
	);
	if ($pieces = preg_split('#[:/.\-, ]#', $format)){	
		if ($timestamp === null) { $timestamp = time(); }
		foreach ($pieces as $datepart){
			if (array_key_exists($datepart,$to_convert)){
				$replace[] = $to_convert[$datepart]['str'][(date($to_convert[$datepart]['dat'],$timestamp)-1)];
			}else{
				$replace[] = date($datepart,$timestamp);
			}
		}
		$result = strtr($format,array_combine($pieces,$replace));
		return $result;
	}
}

Edit: Listing został zaktualizowany ze względu na wykryte błędy, szczegóły w komentarzach
Krótkie omówienie funkcji dla wywołania:

<?php
echo dateV('l j f Y',strtotime('2009-09-02'));
// Środa 2 września 2009
?>

W linii 2 definiujemy tablicę, która zawiera polskie odpowiedniki dni tygodnia oraz nazw miesięcy w odpowiedniej formie. Tablica zawiera indeksy odpowiadające standardowym znacznikom funkcji date, które zamienimy na nasze. Wewnątrz tablicy pola dat mają za zadanie wskazanie znacznika dla funkcji date, który zostanie użyty jako index dla tablicy str. Taki zabieg jest konieczny, gdyż musimy utworzyć znaczniki liczbowe danego dnia tygodnia bądź miesiąca, aby móc wyciągnąć ich polskie nazwy.
Linia 7 rozbija nam podany format na pojedyncze znaczniki.
W linii 8 tworzymy aktualny znacznik czasu, jeśli nie został podany.

Linie 10-16 są kluczowe dla zrozumienia sposobu zamiany podanego formatu aby zwracał poprawne dane.
W linii 10 sprawdzamy, czy aktualnie wybrany znacznik formatu występuje w naszej tablicy kwalifikującej go do zmiany.
Jeśli tak, to w linii 11 do tablicy $replace dodajemy kolejny wiersz zawierający już utworzony element daty. Sposób jego utworzenia może wydawać się skomplikowany, jednak nie jest 😉
Rozbijmy wpis na części składowe. W pierwszej iteracji $datepart=='l'

1
2
3
4
5
6
7
$replace[] = // dopisanie wiersza tablicy
	$to_convert[$datepart]['str'][( // odwołanie $to_convert['l']['str'][] czyli tablica dni tygodnia o indeksie:
		date( // data w formacie:
			$to_convert[$datepart]['dat'], //tablica dni tygodnia - format N (czyli numery dni od 1 (dla Poniedziałku) aż do 7 (dla Niedzieli))
			$timestamp // znacznik czasu
		)-1) // pomniejszony o 1 (indexy w tablicy zaczynają się od 0 )
	];

Natomiast jeśli $datepart nie zawiera się w tablicy $to_convert wykona się linia 13.
W linii 13 do tablicy $replace dodawane jest standardowe wywołanie funkcji date()

Na koniec po przejściu przez wszystkie elementy formatu, w linii 16, dokonuje się zamiana każdego z elementów formatu na odpowiadający mu, utworzony w pętli wcześniej, wynik działania funkcji date.

No i to wszystko 😉 Wystarczy teraz dodać własne znaczniki i odpowiadające im dane, aby móc cieszyć się poprawnymi, polskimi nazwami 😉

 

Przeczytaj także

Komentarze: 15

Dodaj komentarz »

 
 
 

Bardzo mi się przydała 2 opcja kiedy, właśnie piszę jedno-plikowy panelik admina z bazą w pliku tekstowym, dla kolegi 🙂

 

Odpowiedz

 

Hmm, fajne ale. No właśnie ale. Mam coś takiego:

$d = dateV("l, d f Y (d-m-Y) H:i", strtotime($q->data));
echo $d;

Wynik:
Mon01ay, 01 lutego 2010 (01-02-2010) 19:30

Co się dzieje?

 

Odpowiedz

 

Na liście znaków oddzielających poszczególne elementy daty nie było uwzględnionego przecinka. Wystarczy linię 7 zamienić z:

if ($pieces = split('[:/. -]', $format)){

Na poniższy kod i powinno być ok:

if ($pieces = split('[:/. -,]', $format)){

Albo zrezygnować z przestarzałej funkcji split i użyć preg_split (jak słusznie zauważył jedre)

Linia 7 po poprawkach (już zaktualizowana w kodzie w treści posta)

if ($pieces = preg_split('#[:/.-, ]#', $format)){
 

Odpowiedz

 

dzięki za wstawkę, już się przydała! 🙂

 

Odpowiedz

 

Bardzo dziekuje, swietna funkcja, dziala az milo.

 

Odpowiedz

 

Jak zauważył użytkownik czarodziej na forum cba.pl w tym wątku , moja funkcja w pewnych przypadkach nie zachowywała się poprawnie.

Wynikało to z zastosowania funkcji str_replace, która potrafi ponownie zmienić już zmieniony ciąg. W związku z powyższym należy zamienić linię 16 z głównego listingu.

// z tego, błędnego rozwiązania:
$result = str_replace($pieces,$replace,$format);
// na poprawione:
$result = strtr($format,array_combine($pieces,$replace));

Zastosowanie poprawionej funkcji:

echo dateV('j f Y G:i');
// zwraca:
// 20 października 2010 22:01

W treść wpisu została (ponownie) poprawiona.

 

Odpowiedz

 

Dzięki za świetny artykuł i świetną funkcję!!!

 

Odpowiedz

 

W moim przypadku, żeby skrypt działał musiałem poprzedzić backslashem spacje i inne znaki. Tak wygląda mój kawałek kodu, wyświetlający ostatnią datę modyfikacji dokumentu:

echo dateV("l,\ d f Y,\ \g\o\d\z.\ H:i:s", filemtime($_SERVER['SCRIPT_FILENAME']));
// czwartek, 23 lutego 2012, godz. 18:01:18

 

Odpowiedz

 

Hej! Świetna robota, ale ja zmieniłem funkcję rozbijającą na
str_split
więc rozdzielanie nie jest potrzebne (choć przecież celowe 😉 )
Dodałem jeszcze sobie rzymskie miesiące do converta:

 'X'=>array(
    'dat'=>'n',
    'str'=>array('I','II','III','IV','V','VI','VII','VIII','IX','X','XI','XII')
),

Jako timestamp przyjmuję datę i ją tłumaczę strtotime-em, kiedy nie jest integerem, a jeszcze zaraz przywalę „+1d”, „-2m” – jako timestamp względem dziś :)))
Szaleć to szaleć!! 🙂

 

Odpowiedz

Wacuś Zagłada
 

Proponuję w linii 7:
if( $pieces = preg_split( '#[:/.\-,\s+]#', $format, NULL, PREG_SPLIT_NO_EMPTY ) )
dzięki czemu będzie np. działać notacja:
dateEx( 'l, j f Y, G:i' )
czyli spacje po przecinkach. Wygląda to o wiele czytelniej, niż string pokropiony przecinkami.

A ogólnie bardzo przydatny snippet, dziękuję serdecznie.

 

Odpowiedz

Pacer Media Sp. z o. o. | Polskie strony powinny być po polsku
 

[…] od­mie­niona. Mamy 21 li­piec 2014 r. A prze­cież użycie pro­stego kodu, na przy­kład z tej strony roz­wią­zuje pro­blem. Na tej stronie też użyłem sza­blonu an­giel­sko­ję­zycz­nego i […]

 

Odpowiedz

 

Bardzo mi się przydało!
Dzięki wielkie!

 

Odpowiedz

 

Mnie zastanawia następująca rzecz. Lokalna maszyna wyświetla prawidłowe nazwy miesięcy przy ustawieniu:

date_default_timezone_set('Europe/Warsaw');
setlocale(LC_ALL, 'pl_PL');

{$c.creation_date|date_format:"%d %B %Y, %H:%M"}

Jednak ten sam kod na serwerze produkcyjnym działa inaczej. Czy istnieje jakiś sposób do zmuszenia PHP i funkcji strftime() to właściwego działania?

 

Odpowiedz

 

[…] nie odmie­niona. Mamy 21 lipiec 2014 r. A prze­cież uży­cie pro­stego kodu, na przy­kład z tej strony roz­wią­zuje pro­blem. Na tej stro­nie też uży­łem sza­blonu angiel­sko­ję­zycz­nego […]

 

Odpowiedz

 

Super robota dzięki !!!

 

Odpowiedz

 

Dodaj komentarz

 
(nie będzie publikowany)
 
 
Komentarz
 
 

Dozwolone tagi XHTML: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>

 
© 2009 - 2016 Vokiel.com
WordPress Theme by Arcsin modified by Vokiel