[PHP][Tutorial] Usprawniamy funkcje date()
: czw lip 15, 2004 1:52 am
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]
$czl przyjmuje wartości 0 lub 1. 0 - nie uwzgledniamy czasu letniego, 1 - uwzgledniamy
$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.
$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".
$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
$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:
linia 2: paramtr $stamp to oczywiscie znacznik unixowy., ktory w danym momencie zostal zwrocony przez funkcje time()
linia 4: tu sprawdzamy czy na serwerze jest juz czas letni.
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.
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]
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]
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:
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]