Jak dlouhý je měsíc leden v PHP? Vždy 31 dní
- maarlin
- Člen | 207
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://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)
- PetrP
- Člen | 587
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
- PetrP
- Člen | 587
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
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
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
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
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
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
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é.
- maarlin
- Člen | 207
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ě.