[PHP][Tutorial] Usprawniamy funkcje date()

Języki, których efekty są wykonywane po stronie serwera. PHP, ASP czy CGI w połączeniu z bazami danych SQL dają niezwykłe możliwości budowy i zarządzania stron WWW. Tu zadajemy pytania oraz demonstrujemy kod, z którym mamy problem.
the_foe
Przelotem
Przelotem
Posty: 295
Rejestracja: pt sty 31, 2003 12:53 pm
Lokalizacja: Wałbrzych
Kontakt:

[PHP][Tutorial] Usprawniamy funkcje date()

Post autor: the_foe »

Różnica czasowa to zmora dla tych co mają serwer np. w stanach. Nagle okazuje się, że nic się nie zgadza. W tym tutorialu napiszemy klase ktora zastapi nam funkcje date(), z ktorej, jesli nie uzywamy fizycznego serwera a tylko wirtualnego, musimy jak najszybciej zrezygnować!
Dlaczego? Łatwiej bedzie mi to wyjasnic jesli wyjaśnijmy sobie jak dzialaja podstawowe funkcje czasu i daty.
podstawa jest tu oczywiscie time() - ktory zwraca nam tzw. znacznik (stamp) unixowy. Nie jest to zadna data, a raczej rodzaj kalendarza (dziala podobnie jak kalendarz astronomiczny, tyle ze mniej dokladnie), ktorego dana chwila jest zapisywana w postaci liczby calkowitej wyrazajacej ilosc sekund jakie minely od dnia 1 stycznia 1970 roku o godzinie 0.00 czasu GMT. I tu od razu nalezy unikac pokusy, ktorej ja sam swego czasu nie uniknalem, by dodawac roznice czasowa do tej funkcji. To najgorszy blad! Dowiedza sie o tym Ci, ktorzy zmienia serwer na serwer w innej strefie czasowej. Przy funkcji time() nie trzeba nic robic, poniewaz w kazdej strefie czasowej w jakiej moglby byc serwer zawsze zwroci te sama wartosc.
Problemem jest funkcja date(), ktora na podstawie znacznika zwraca nam date jaka byla(jest) w danej strefie. Musielibysmy uzyc funkcji gmdate(), ktora zwraca nam date zawsze ta sama, date strefy GMT. Wystraczyloby dodac tylko godzinke i byloby po robocie, ale... niestety strefa gmt nie pzrewiduje czasu letniego. Jesli jednak, co nie jest trudne, zrobimy to funkcja, to spotka nas zawod: jesli mamy serwer w stanach - czas letni tam jest wprowadzany tydzien pozniej niz u nas! To tez, choc trudniej, mozna spacyfikowac, ale dostaniemy juz funkcje potworka. Lepiej zrobic cos uniwersalnego. Dzieki czemy bedziemy mogli skakac latwo po strefach.

Zaczynamy:

tworzymy klase w osobnym pliku, ja go nazwalem jak klase udate.php
w nim ostwieramy klase i definiujemy zmienne:


[php]<?php
class udate
{
var $czl;
var $czl_styl_klient;
var $czl_styl_serwer;
var $strefa;
var $format;

}
?>[/php]

:arrow: $czl przyjmuje wartości 0 lub 1. 0 - nie uwzgledniamy czasu letniego, 1 - uwzgledniamy
:arrow: $czl_styl_klient przyjmuje wartości "US" lub "UE". "US" - czas letni ma byc brany pod uwage jak w USA, gdzie zaczyna się w pierwsza niedziele kwietnia, "UE" - tak jak w krajach Unii, gdzie zaczyna sie w ostatnia niedziele marca. Czas letni w obu stylach konczy sie w tym samym dniu.
:arrow: $czl_styl_serwer przyjmuje wartości "US" lub "UE". musimy ustawic tak aby zgadzal sie z polozeniem serwera. jezeli serwer jest w kraju gdzie czas letni rozpoczyna sie w ostatnia niedziele marca wybieramy wartosc "UE", jesli w pierwsza niedziele kwietnia to "US".
:arrow: $strefa, wartosci w zakresie -12 do 12. okresla w jakiej strefie przebywamy, lub z jakiej strefy chcemy uzyskac czas. dla Polski jest to 1 a np,. dla Nowego Yorku -5
:arrow: $format, format daty. np: "Y-m-d".

Teraz ustawimy wartosci standardowe. To znaczy dla tych, dla ktorychi nie okreslimy wartosci przy wywolaniu klasy. Robimy tzw. konstruktora czyli funkcje o takiej samej nazwie jak klasa. Dzieki temu bedzie ona sie wywolywac automatycznie:

[php]<?php
function udate()
{
$this->czl=1;
$this->czl_styl_klient="UE";
$this->czl_styl_serwer="UE";
$this->strefa=1;
$this->format="Y-m-d H:i";
}

?>[/php]

tu chyba wszytko jasne. Teraz musimy stworzyc funkcje, ktorymi bedziemy zmieniac nasze wartosci:

[php]<?php
function ustaw_czl($set)
{
if ($set==1 || $set==0)
{
$this->czl=$set;
}
else
{
$this->czl=$this->czl;
}
}

function ustaw_czl_styl_klient($set)
{
if ($set=="US" || $set=="UE")
{
$this->czl_styl_klient=$set;
}
else
{
$this->czl_styl_klient=$this->czl_styl_klient;
}
}

function ustaw_czl_styl_serwer($set)
{
if ($set=="US" || $set=="UE")
{
$this->czl_styl_serwer=$set;
}
else
{
$this->czl_styl_serwer=$this->czl_styl_serwer;
}
}

function ustaw_strefa($set)
{
if ($set<12 || $set>-12)
{
$this->strefa=$set;
}
else
{
$this->strefa=$this->strefa;
}
}

function ustaw_format($set)
{
$this->format=$set;
}

?>[/php]

Oprocz zmiennej $format, kazda inna ma ma zabezpiedczenie - jesli podamy mu wartosc spoza dopuszczalnego zakresu zwroci nam wartosci zdefinowane na poczatku.

Teraz przechodzimy do sedna. Glowna funkcja:

[php]<?php
function pokaz_date($stamp)
{
$chk_dls=date("I",$stamp);
$year=gmdate("Y",$stamp);
///

if ($this->czl_styl_klient=="UE")
{
$day=25;
while ($dow!="Sun")
{
$a=gmmktime (1,0,0,3,$day,$year);
$dow=gmdate("D",$a);
$day++;
}
$border=$a;
}
elseif($this->czl_styl_klient=="US")
{
$day=1;
while ($dow!="Sun")
{
$a=gmmktime (1,0,0,4,$day,$year);
$dow=gmdate("D",$a);
$day++;
}
$border=$a;
}

///
if ($this->czl==1)
{
if (($this->czl_styl_klient=="US" && $this->czl_styl_serwer=="US")||($this->czl_styl_klient=="UE" && $this->czl_styl_serwer=="UE"))
{
if ($chk_dls==1){$dat=gmdate($this->format,$stamp+(($this->strefa*3600)+3600));
}
else
{
$dat=gmdate($this->format,$stamp+($this->strefa*3600));
}
}
elseif($this->czl_styl_klient=="US" && $this->czl_styl_serwer=="UE")
{
if ($chk_dls==1 && $stamp>=$border)
{
$dat=gmdate($this->format,$stamp+(($this->strefa*3600)+3600));
}
else
{
$dat=gmdate($this->format,$stamp+($this->strefa*3600));
}
}
elseif($this->czl_styl_klient=="UE" && $this->czl_styl_serwer=="US")
{
if ($chk_dls==1 || ($stamp>=$border && $stamp<$border+(7*24*3600)))
{
$dat=gmdate($this->format,$stamp+(($this->strefa*3600)+3600));
}
else
{
$dat=gmdate($this->format,$stamp+($this->strefa*3600));
}
}
}
elseif($this->czl==0)
{
$dat=gmdate($this->format,$stamp+($this->strefa*3600));
}

print $dat;
}

?>[/php]

Wyjasnie po krotce co sie tu dzieje:
:arrow: linia 2: paramtr $stamp to oczywiscie znacznik unixowy., ktory w danym momencie zostal zwrocony przez funkcje time()
:arrow: linia 4: tu sprawdzamy czy na serwerze jest juz czas letni.
:arrow: linia 8-29: musimy wiedziec kiedy dokladnie przechodzimy w danym roku na czas letni w US lub UE. Chodzi o to, zebysmy odpowiednio zkorektowali to co dostalismy w lini 4.
:arrow: od lini 32: tworzymy date za pomoca funkcji gmdate(), dodajac/odejmujac do/od znacznika odpowiednia liczbe sekund. wynika to ze zmiennej "strefa". W przypadku kiedy musimy wziac pod uwage czas letni, mamy trzy warunki: serwer i klient znajduja sie w takiej strefie gdzie czas letni zaczyna sie w tym samym dniu, serwer znajduje sie w strefie gdzie rozpoczecie czasu letniego jest w roznych dniach (2 przypadki).

Teraz kilka przykladow. Bierzemy na tapete stamp z chwili przejscia na czas letni: 985482000 to 2001-03-25 02:00 wg czasu CET (np. Polska) a 2001-03-25 03:00 uwzgledniajac przejscie na czas letni. Byla to chwila kiedy z gdziny 2.00 przesunelismy zegarki na godzine 3.00

jesli mamy serwer w Polsce:
[php]<?php
include "udate.php";
$udate=new udate();
$udate->pokaz_date("985482000");
?>[/php]

zwroci nam: "2001-03-25 03:00"

jesli w stanach:

[php]<?php
include "udate.php";
$udate=new udate();
$udate->ustaw_czl(1);
$udate->ustaw_czl_styl_klient("UE");
$udate->ustaw_czl_styl_serwer("US");
$udate->ustaw_strefa(1);
$udate->pokaz_date("985482000");

?>[/php]

zwroci nam znowu "2001-03-25 03:00", uzylem wszytkich dostepnych medod w celach pokazowych, niektorych nie trzeba bylo, bo i tak zawieraja wartosci juz zadeklarowane w konstruktorze

a jaka jest godzina w NY?:

[php]<?php
include "udate.php";
$udate=new udate();
$udate->ustaw_czl(1);
$udate->ustaw_czl_styl_klient("US");
$udate->ustaw_czl_styl_serwer("UE");
$udate->ustaw_strefa(-5);
$udate->pokaz_date("985482000");

?>[/php]

otrzymamy date: "2001-03-24 20:00"

=====

oto jednolita klasa:

[php]<?php
class udate{
var $czl;
var $czl_styl_klient;
var $czl_styl_serwer;
var $strefa;
var $format;

function udate()
{
$this->czl=1;
$this->czl_styl_klient="UE";
$this->czl_styl_serwer="UE";
$this->strefa=1;
$this->format="Y-m-d H:i";
}

function ustaw_czl($set)
{
if ($set==1 || $set==0)
{
$this->czl=$set;
}
else
{
$this->czl=$this->czl;
}
}

function ustaw_czl_styl_klient($set)
{
if ($set=="US" || $set=="UE")
{
$this->czl_styl_klient=$set;
}
else
{
$this->czl_styl_klient=$this->czl_styl_klient;
}
}

function ustaw_czl_styl_serwer($set)
{
if ($set=="US" || $set=="UE")
{
$this->czl_styl_serwer=$set;
}
else
{
$this->czl_styl_serwer=$this->czl_styl_serwer;
}
}

function ustaw_strefa($set)
{
if ($set<12 && $set>-12)
{
$this->strefa=$set;
}
else
{
$this->strefa=$this->strefa;
}
}

function ustaw_format($set)
{
$this->format=$set;
}

function pokaz_date($stamp)
{
$chk_dls=date("I",$stamp);
$year=gmdate("Y",$stamp);
///

if ($this->czl_styl_klient=="UE")
{
$day=25;
while ($dow!="Sun")
{
$a=gmmktime (1,0,0,3,$day,$year);
$dow=gmdate("D",$a);
$day++;
}
$border=$a;
}
elseif($this->czl_styl_klient=="US")
{
$day=1;
while ($dow!="Sun")
{
$a=gmmktime (1,0,0,4,$day,$year);
$dow=gmdate("D",$a);
$day++;
}
$border=$a;
}

///
if ($this->czl==1)
{
if (($this->czl_styl_klient=="US" && $this->czl_styl_serwer=="US")||($this->czl_styl_klient=="UE" && $this->czl_styl_serwer=="UE"))
{
if ($chk_dls==1){$dat=gmdate($this->format,$stamp+(($this->strefa*3600)+3600));
}
else
{
$dat=gmdate($this->format,$stamp+($this->strefa*3600));
}
}
elseif($this->czl_styl_klient=="US" && $this->czl_styl_serwer=="UE")
{
if ($chk_dls==1 && $stamp>=$border)
{
$dat=gmdate($this->format,$stamp+(($this->strefa*3600)+3600));
}
else
{
$dat=gmdate($this->format,$stamp+($this->strefa*3600));
}
}
elseif($this->czl_styl_klient=="UE" && $this->czl_styl_serwer=="US")
{
if ($chk_dls==1 || ($stamp>=$border && $stamp<$border+(7*24*3600)))
{
$dat=gmdate($this->format,$stamp+(($this->strefa*3600)+3600));
}
else
{
$dat=gmdate($this->format,$stamp+($this->strefa*3600));
}
}
}
elseif($this->czl==0)
{
$dat=gmdate($this->format,$stamp+($this->strefa*3600));
}

print $dat;
}

}

?>[/php]
Ostatnio zmieniony pt lut 04, 2005 2:18 am przez the_foe, łącznie zmieniany 1 raz.
the_foe
Przelotem
Przelotem
Posty: 295
Rejestracja: pt sty 31, 2003 12:53 pm
Lokalizacja: Wałbrzych
Kontakt:

Re: [PHP][Tutorial] Usprawniamy funkcje date()

Post autor: the_foe »

jesli chcemy zwracac date do zmiennej to musimy zamienic w lini 140

:arrow: print $dat; na return $dat;

wtedy:
[php]<?php
include "udate.php";
$udate=new udate();
$a=$udate->pokaz_date(time());
print "<span style='font: 20px Courier'>".$a."</span>";
?>[/php]
otrzymamy datę bieżącą.

aby powrocic do standardowych ustawien (przyjetych w kontruktorze), jeśli je zmienialismy, wystaczy wpisac:
[php]<?php
$udate->udate();
?>[/php]
przyklad:
[php]<?php
include "udate.php";
$udate=new udate();
$udate->ustaw_czl_styl_klient("US");
$udate->ustaw_format("H:i");
$a=$udate->pokaz_date("985482000");
$udate->udate();
$b=$udate->pokaz_date("985482000");
echo $a."<br>".$b;
?>[/php]
zwroci nam:
02:00
2001-03-25 03:00
Grzegorz Winiarski
Nowy
Nowy
Posty: 147
Rejestracja: pt lut 27, 2004 10:36 pm
Lokalizacja: Kraków

Re: [PHP][Tutorial] Usprawniamy funkcje date()

Post autor: Grzegorz Winiarski »

Może zamiast przyklejać ten temat, lepiej by było umieścić go w tutorialach do PHP?
the_foe
Przelotem
Przelotem
Posty: 295
Rejestracja: pt sty 31, 2003 12:53 pm
Lokalizacja: Wałbrzych
Kontakt:

Re: [PHP][Tutorial] Usprawniamy funkcje date()

Post autor: the_foe »

Grzegorz Winiarski pisze:Może zamiast przyklejać ten temat, lepiej by było umieścić go w tutorialach do PHP?
potrzebowalby troche przeredagowania - w tej chwili jest mocno "liczący na inteligencje czytelnika". No i sama stylistyke, nizebyt sie staralem.
tmz
Nowy
Nowy
Posty: 63
Rejestracja: ndz sty 16, 2005 10:16 am
Kontakt:

Re: [PHP][Tutorial] Usprawniamy funkcje date()

Post autor: tmz »

Generalnie przydatna funkcja. Troche mnie mierzi w funkcjach ustaw_* czesci ELSE - sa zupelnie niepotrzebne, nie robia nic.
Ponadto jest jeszcze maly feler w ustaw_strefe - zamiast || powinno byc &&
Pozdrawiam
xrb
Posty: 13
Rejestracja: wt sie 30, 2005 3:24 pm
Lokalizacja: Olsztynek
Kontakt:

Re: [PHP][Tutorial] Usprawniamy funkcje date()

Post autor: xrb »

witam. wlasnie tego szukalem tylko ze jestem troszke nie kumaty ;] a wiec to wszystko ma byc w jednym pliku?a jesli tak to w jakim miejscu mam includowac ten plik jesli mam skrypt, np do newsow i wyswietla mi zla godzine bo serwer na ktorym znajduje sie moje konot jest w USA? prosze o wyrozumialosc i odp ; z gory dzieki pozdrawiam narka
the_foe
Przelotem
Przelotem
Posty: 295
Rejestracja: pt sty 31, 2003 12:53 pm
Lokalizacja: Wałbrzych
Kontakt:

final solution?

Post autor: the_foe »

Podana klasa, jest dobra przy kompleksowych systemach, np. w sytuacji kiedy chcemy plynnie przeskakiwac miedzy strefami. Sporzadzilem jednak funkcje, ktora dziala identycznie jak [manual]date[/manual], z tym ze zawsze, niezaleznie gdzie jest serwer, zwraca czas polski.
Oto funkcja:
[php]<?php
function p_date($style="Y-m-d H:i",$stamp=0){

/*
funkcja pokazuje zawsze polski czas lokalny,
nie ma znaczenia jaki jest czas lokalny
na serwerze. Zalozeniem jest to, ze w Polsce zmiana
czasu na letni i zimowy nastepuje zgodnie z europejskimi
zasadami - +1 h w ostatnia niedziele marca 01:00 GMT
- 1h w ostatnia miedziele pazdziernika 01:00 GMT
te zasady na pewno beda w Polsce do 2008 roku, zgodnie
z rozporzadzeniem rzadu z 2004 roku. Prawie na pewno nie ulegnie
to zadnym zmianom po 2008 roku.
*/

/* szykujemy stampa - jesli stamp=0
to bierzemy aktualnego */
if ($stamp==0||empty($stamp))$stamp=time();

#rok GMT
$rok=gmdate("Y",$stamp);

#znajduje graniczne stampy dla zmian czasy

#w marcu
$day=25;//bo najwczesniej zmiana moze byc 25
$dow="1";
while ($dow!="0")
{
$a=gmmktime (1,0,0,3,$day,$rok);
$dow=gmdate("w",$a);
$day++;
}
//$a=stamp graniczny dla marca

#w pazdzierniku
$day=25;
$dow="1";
while ($dow!="0")
{
$b=gmmktime (1,0,0,10,$day,$rok);
$dow=gmdate("w",$b);
$day++;
}
//$b=stamp graniczny dla pazdzienika

#ustawiamy offset
if($stamp>=$a && $stamp<$b){
#czas letni, roznica 2 godzin
$offset=7200;
}
else
{
#czas zimowy, roznica 1. godziny
$offset=3600;
}

return gmdate($style,$stamp+$offset);
}
?>[/php]

ta funkcja jest ok 10 razy wolniejsza od zwyklego [manual]date[/manual]. No, ale nie ma rozy bez ognia ;)
Ostatnio zmieniony ndz maja 06, 2007 5:36 pm przez the_foe, łącznie zmieniany 1 raz.
egzemplarz
Nowy
Nowy
Posty: 143
Rejestracja: pt gru 08, 2006 3:46 pm

Re: [PHP][Tutorial] Usprawniamy funkcje date()

Post autor: egzemplarz »

Pytanie do pan the_foe. Czy można użyć prezentowanego przez pana skryptu na swojej stronie, za darmo, nie łamiąc przy tym prawa? Przydałby mi się ;).
the_foe
Przelotem
Przelotem
Posty: 295
Rejestracja: pt sty 31, 2003 12:53 pm
Lokalizacja: Wałbrzych
Kontakt:

Re: [PHP][Tutorial] Usprawniamy funkcje date()

Post autor: the_foe »

egzemplarz pisze:Pytanie do pan the_foe. Czy można użyć prezentowanego przez pana skryptu na swojej stronie, za darmo, nie łamiąc przy tym prawa? Przydałby mi się ;).
prosze uzywać, zmieniac, itd...
zaliczam to do free software ;)
www.skrypty.pro
Nowy
Nowy
Posty: 128
Rejestracja: czw lis 04, 2010 9:41 am
Lokalizacja: www.skrypty.pro
Kontakt:

Re: [PHP][Tutorial] Usprawniamy funkcje date()

Post autor: www.skrypty.pro »

To dobrze,że wolno używać. Jest to bardzo dobrze opracowane i będzie przydatne dla początkujących i nie tylko.
Sam nawet z tego skorzystam.
Dzięki wielkie the_foe
ODPOWIEDZ