Jak dlouhý je měsíc leden v PHP? Vždy 31 dní

Upozornění: Tohle vlákno je hodně staré a informace nemusí být platné pro současné Nette.
maarlin
Člen | 207
+
0
-

Dnes jsem tak trochu narazil, když jsem zjistil, že časový interval 1 měsíce se v PHP chápe trochu jinak (= počet dnů navíc přetéká do dalšího měsíce), než např. v MySQL a PostgreSQL (SQLite se údajně chová stejně jako PHP).

PostgreSQL:

SELECT date '2011-01-31'+interval '1 mon'; -- 2011-02-28 00:00:00

MySQL:

SELECT DATE_ADD('2011-01-31', INTERVAL 1 MONTH) -- 2011-02-28

PHP (strtotime implementace):

date('Y-m-d G:i:s', strtotime('2011-01-31+1 month')); // 2011-03-03 0:00:00

PHP (zápis v konstruktoru DateTime):

$date = new DateTime("2011-01-31+1 month");
echo $date->format('Y-m-d G:i:s'); // 2011-03-03 0:00:00

PHP (použití nové metody přidané v PHP 5.3 – DateTime::add):

$date = new DateTime("2011-01-31");
$date->add(new DateInterval('P1M'));
echo $date->format('Y-m-d G:i:s'); // 2011-03-03 0:00:00

Další zdroje k tomuto „neočekávanému chování“, nebo bugu (přeložte si to jak chcete):

http://www.phpreferencebook.com/…ime-1-month/

http://derickrethans.nl/…-in-php.html

http://lunesu.com/index.php?…

http://www.php.net/…trtotime.php#…

Rád bych tedy spíše zde otevřel diskuzi nad tím, zda by tohle měl framework nějak řešit, pokud ano, jak?

Editoval maarlin (30. 5. 2011 23:26)

Filip Procházka
Moderator | 4668
+
0
-

A já si myslel, že to řeší DateTime. To je docela podpásovka…

22
Člen | 1478
+
0
-

mno DateTime ma ještě jednu vadu na kráse a to, že když zadáte new DateTime(‚29.02.2011‘), tak vygeneruje bez varovani 01.03.2011

hrach
Člen | 1838
+
0
-

no pokud neni rok 2011 prechodny (a neni) tak je to ok.

maarlin
Člen | 207
+
0
-

hrach napsal(a):

no pokud neni rok 2011 prechodny (a neni) tak je to ok.

Co myslíš že je OK?

Dopočet v případě většího počtu dnů v měsíci do dalšího (jak píše @22), nebo +1 month reprezentace vždy jako 31 dnů? Mě nepřijde OK ani jedno.

22
Člen | 1478
+
0
-

proč je to ok, když vygeneruje jiné datum, než má na vstupu? Čekal bych vyjímku, že takové datum nemůže vygenerovat, protože neexistuje.

PetrP
Člen | 587
+
0
-

Neni pravda.

Pridava pocet dnu v tom konretnim mesici. Ale preleza az do dalsiho mesice. V lednu to je 31 v unoru 28/29.

$d = new DateTime('2011-02-01');
$d->modify('+1 month');
// 2011-03-01

$d = new DateTime('2011-02-05');
$d->modify('+1 month');
// 2011-03-05

$d = new DateTime('2011-02-28');
$d->modify('+1 month');
// 2011-03-28

$d = new DateTime('2011-01-01');
$d->modify('+1 month');
// 2011-02-01

$d = new DateTime('2011-01-31');
$d->modify('+1 month');
// 2011-03-03
hrach
Člen | 1838
+
0
-

Ok je to proto, ze je to konzistentni chovani uz od pradavna (viz mktime)

#bavime se jenom o te „interpolaci“ data

Editoval hrach (30. 5. 2011 22:54)

PetrP
Člen | 587
+
0
-

Kdyz potrebujes na konci mesice nepretejkat na dalsi mesic stejne jako delaji sql. Tak muzes nejak takto:

$d = new DateTime('2011-01-31');
$first = new DateTime($d->format('Y-m-01'));
$first->modify('+1 month');
$d = new DateTime($first->format('Y-m-' . min($d->format('d'), $first->format('t'))));
// 2011-01-28
// t je pocet dnu v mesici
maarlin
Člen | 207
+
0
-

PetrP napsal(a):

Kdyz potrebujes na konci mesice nepretejkat na dalsi mesic stejne jako delaji sql. Tak muzes nejak takto:

Jasně, já vím že na to jsou workaroundy – ostatně na pár z nich jsem odkázal v prvním příspěvku. Moje otázka a námět k diskuzi je spíš „Mělo by tohle nějak řešit i Nette samo?“

Filip Procházka
Moderator | 4668
+
0
-

A co takhle? ;)

echo Nette\DateTime::from('2011-01-31')
	->modify('first day of next month')
	->format('d.m.Y'); // 1.2.2011

echo Nette\DateTime::from('2011-01-31')
	->modify('last day of next month')
	->format('d.m.Y'); // 28.2.2011

Editoval HosipLan (30. 5. 2011 23:04)

PetrP
Člen | 587
+
0
-

Ale 2 prispevky vyse rikam ze nemas pravdu, ze to neni vzdu 31.

Spravnejsi nadpis prispevky by mel byt:
Jak dlouhý je leden v PHP? Vždy 31 dní

Tedy chova se to spravne, jen to dokaze „pretekat“.

maarlin
Člen | 207
+
0
-

PetrP napsal(a):

Ale 2 prispevky vyse rikam ze nemas pravdu, ze to neni vzdu 31.

Spravnejsi nadpis prispevky by mel byt:
Jak dlouhý je leden v PHP? Vždy 31 dní

Tedy chova se to spravne, jen to dokaze „pretekat“.

Že tohle řešení je „správné“ je tvůj subjektivní názor, nebo pro to máš nějaké argumenty? :-)

A ano, za unáhlený nadpis se omlouvám, jen už ho nemám oprávnění teď změnit.

HosipLan napsal(a):

A co takhle? ;)

echo Nette\DateTime::from('2011-01-31')
	->modify('first day of next month')
	->format('d.m.Y'); // 1.2.2011

echo Nette\DateTime::from('2011-01-31')
	->modify('last day of next month')
	->format('d.m.Y'); // 28.2.2011

V tomto případě se zrovna hodí, že počítáš od posledního dne, ale co třeba 27.1.2011? Tam už asi relevantní „last day of next month“ úplně nebude.

Editoval maarlin (30. 5. 2011 23:27)

PetrP
Člen | 587
+
0
-

maarlin napsal(a):

Že tohle řešení je „správné“ je tvůj subjektivní názor, nebo pro to máš nějaké argumenty? :-)

Pretekani nepovazuju za spravne. Ohrazuju se ze pises ze php ma vzdy 31 dni mesic.

A ano, za unáhlený nadpis se omlouvám, jen už ho nemám oprávnění teď změnit.

Ok menim ho na: Jak dlouhý je měsíc leden v PHP? Vždy 31 dní.


Jo a mimochodem (vsiml jsem si ze to je v Feature Request), nemyslim si ze by neco takoveho melo resit nette ;]

David Grudl
Nette Core | 8218
+
0
-

Přetékání není úplně šťastné, bylo by lepší, kdyby konstruktor DateTime vyhodil výjimku. Ale udělat obecný workaround by asi bylo dost složité.

paranoiq
Člen | 392
+
0
-

na tohle ‚blbé‘ chování si stěžujte spíš autorům kalendáře. obecně jsou dva způsoby jak přičíst měsíc a PHP si prostě vybralo jeden z nich, ten méně intuitivní. třetí možnost je přičíst průměrný měsíc, tedy 30,436875 dne :P

paranoiq
Člen | 392
+
0
-

a otázka za dva bludišťáky: jak by se mělo chovat odečtení měsíce od data 28.2.2011? bude výsledkem 28.1.2011 nebo 31.1.2011?

Editoval paranoiq (31. 5. 2011 7:51)

maarlin
Člen | 207
+
0
-

paranoiq napsal(a):

a otázka za dva bludišťáky: jak by se mělo chovat odečtení měsíce od data 28.2.2011? bude výsledkem 28.1.2011 nebo 31.1.2011?

PostgreSQL vrací 2011-01-28 00:00:00

MySQL vrací 2011-01-28

PHP vrací 2011-01-28 0:00:00

Tedy pokud k přetečení nedojde, nic se neděje a prostě se jen hýbe s číslem měsíce.

Chování zmíněných dvou databází chápu tak, že se nejdříve odečte/přičte patřičný počet měsíců a zkusí se nastavit daný počet dnů v novém měsíci. Pokud je to moc, „ořízne“ se přetékající počet dnů v daném měsíci.

Samozřejmě, jsou tam takové případy, kdy to nemusí přijít úplně vhod, např.:

SELECT date '2011-03-31'-interval '1 mon'+interval '1 mon'; -- 2011-03-28 00:00:00

Jak píše @paranoiq, přičítání měsíců se dá chápat různě. Snaha ale je, aby se to alespoň mezi databází a aplikací chovalo pokud možno stejně.