Práce s datumy v MySQL a výstupem z PHP

Jaký zvolit datový sloupec? A jak jednoduše získat formátovaný výstup z unixového časového razítka popřípadě z datumu ve formátu DATETIME?

Teď trochu obecně o jednotlivých datových typech, i když je to nuda – doporučuji přečíst:-D.

Ukládání času do mysql databáze lze udělat více způsoby a v různých formátech.
Může jít o datové sloupce:

  • TIMESTAMP
  • DATE
  • TIME
  • DATETIME
  • YEAR
  • INT
  • Nebo jen čistě uložený řetězec (CHAR, VARCHAR, TEXT..) v databázi, nedoporučuji.

TIMESTAMP

Timestamp je „datumočas“ ve formátu „RRRRMMDDHHMMSS“ a rozsahu od 1970–01–01 00:00:00 do 2037–01–01 00:00:00.

Datový typ TIMESTAMP je výhodný pro ukládání datumu vložení řádku (výchozí hodnota je CURRENT_TIMESTAMP), protože při úpravě se aktualizuje i čas v upraveném řádku. (Vlastnost ON UPDATE CURRENT_TIMESTAMP).

V databázi zabírá 4 bajty, při výstupu lze formát datumu a času libovolně měnit (funkce DATE_FORMAT()).

DATE

Uloží datum ve formátu „YYYY-MM-DD“ a s rozsahem „1000–01–01“ až „9999–12–31“.
Zabírá 3 bajty a taktéž lze při výstupu jednoduše formátovat.

TIME

Čas ve formátu „hh:mm:ss“, většinou prezentuje čas dne, ale může to být i uplynulý čas. Proto má celkem neobvyklý rozsah „-838:59.59“ až „838:59:59“.
Taktéž 3 bajty a jednoduché formátování.

DATETIME

Datum a čas ve formátu „YYYY-MM-DD hh:mm:ss“, rozsah „1000–01–01 00:00:00“ do „9999–12–31 23:59:59“.
Zabírá 8 bajtů.

YEAR

Rok YYYY, rozsah 1901 až 2155 a zabírá 1 bajt.

INT

INT je číselný typ a nikoliv datumový, přesto se v něm dá datum snadno uložit a i jednoduše s tím dá pracovat.

Informace o čase lze uložit i jako číslo, které prezentuje počet uplynulých sekund od začátku unixového času (1.1.1970) – takzvaný UNIX TIMESTAMP. V PHP vrací funkce time() právě počet sekund od začátku unixového času. Jako datový typ pro UNIXové časové razítko doporučuji INT(10). Zabírá 4 bajty.

Ostatní datové sloupce se pro ukládání času nehodí.

Datové typy je dobré znát, usnadní vám to spoustu práce a ušetří i nějaké to místo v databázi. Což sice není v dnešní době příliš podstatné, ale hlavně nedochází ke zbytečným ztrátám výkonu aplikací. (Jen pro představu – uložíte-li si datum v libovolném formátu do datového typu VARCHAR(255), bude zabírat místo 4–8 bajtů celých 256!)

Od teorie se odprostím, ukážu pár příkladů, které se budou hodit.

Vytvoříme si testovací tabulku ve které budeme mít datum uložený ve 3 různých datových formátech, např:

CREATE TABLE `test` (
  `id` INT NOT NULL AUTO_INCREMENT PRIMARY KEY ,
  `jmeno` VARCHAR( 64 ) NOT NULL ,
  `heslo` CHAR( 32 ) NOT NULL ,
  `cas` DATETIME NOT NULL ,
  `cas_zmeny` TIMESTAMP ON UPDATE CURRENT_TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP ,
  `cas_unix` INT( 10 ) NOT NULL
);

Vše lze „naklikat“ v phpmyadminovi nebo tento kód okopírujte a vložte v phpmyadminu do „SQL“. Takže tabulku máme, teď se půjdeme podívat, jak zobrazovat, vkládat či upravovat data z PHP skriptů.

Vložení a úprava:

Vezmu jen útržek skriptu s SQL dotazem, jak získat třeba data z formuláře je popsáno v tomto seriálu registrace.

Příklad 1:

<?php
/****** Přidání
* Připojení na databázi, získání proměnných, jejich ošetření atd...
*/
$Vlozeni=mysql_query("
          INSERT INTO `test`
              (`jmeno`,`heslo`,`cas`, `cas_unix`)
          VALUES
             ('".$Jmeno."', '".$Heslo."', NOW(), ".time().")
          ") or die ("Chyba v SQL dotazu: " . mysql_error());

// Naschvál jsem vynechal sloupec `cas_zmeny`, vloží se automaticky
// (Výchozí DEFAULT CURRENT_TIMESTAMP)

if($Vlozeni){
  echo "Záznam byl uložen";
}
?>

<?php
/****** Úprava
* To samé i při úpravě (spojení na databázi, získání proměnných a jejich ošetření)
*/

$Uprava=mysql_query("
          UPDATE `test`
              SET
              `jmeno`='".$Jmeno."',
              `heslo`='".$Heslo."'
            WHERE
              `id`=".(int)$_POST['id_opravovaneho_zaznamu']."
          ") or die ("Chyba v SQL dotazu: " . mysql_error());

// Naschvál jsem vynechal sloupec `cas_zmeny`, upraví se automaticky
// (Vlastnost ON UPDATE CURRENT_TIMESTAMP)

if($Uprava){
  echo "Záznam byl upraven";
}
?>

Výpis do tabulky je pak jen otázkou chviličky.

Příklad 2:

<?php
/*
* Připojení na databázi...
*/
$Dotaz=mysql_query("
      SELECT
         `id`, `jmeno`, `cas` , `cas_zmeny`, `cas_unix`
      FROM `test`
      ") or die ("Chyba v SQL dotazu: " . mysql_error());

echo "<table>";
while($zaznam=mysql_fetch_array($Dotaz)){
  echo "
    <tr>
      <td>".$zaznam['id']."</td>
      <td>".$zaznam['jmeno']."</td>
      <td>".$zaznam['cas']."</td>
      <td>".$zaznam['cas_zmeny']."</td>
      <td>".$zaznam['cas_unix']."</td>
    </tr>
  ";
}
echo "</table>";
?>

A jak jsem již říkal, formát času můžeš měnit pomocí funkce DATE_FORMAT, její popis najdeme v manuálu – zde. Praktický ukázka přímo v dotazu pro přehlednost.

Příklad 3:

<?php
/*
* Připojení na databázi...
*/
$Dotaz=mysql_query("
      SELECT
         `id`, `jmeno`, DATE_FORMAT(`cas`, '%Y %m %d') AS `cas`
      FROM `test`
      ") or die ("Chyba v SQL dotazu: " . mysql_error());
// atd...
?>

Z DATETIME můžete získat unixové časové razítko a unixového časového razítka můžeme zístat DATETIME pomocí funkcí UNIX_TIMESTAMP a FROM_UNIXTIME. V databázi tedy nemusíme mít 2× stejný údaj, můžeme si vybrat jednu variantu a tu používat.

Krátký příklad, kde použiju obě tyto funkce, navíc funkce FROM_UNIXTIME umí i rovnou formátovat výstup stejně jako funkce DATE_FORMAT().

Příklad 4:

<?php
/*
* Připojení na databázi...
*/
$Dotaz=mysql_query("
      SELECT
       `id`, `jmeno`,
       UNIX_TIMESTAMP(`cas`) AS `UnixRazitkoZDateTime`,
       FROM_UNIXTIME(`cas_unix`) AS `DateTimeZCasovehoRazitka`,
       FROM_UNIXTIME(`cas_unix`,'%d.%m.%Y') AS `DateTimeZCasovehoRazitkaFormatovany`
      FROM `test`
      ") or die ("Chyba v SQL dotazu: " . mysql_error());

// Sloupcům přiřadím alias, tak dlouhý je kvůli přehlednosti - pojmenování je na vás.

echo "
  <table>
    <tr>
      <th>ID</th>
      <th>Jméno</th>
      <th>Unix z datumu</th>
      <th>Datum z Unixu</th>
      <th>Datum z Unixu formátovaný</th>
    </tr>
  ";
while($zaznam=mysql_fetch_array($Dotaz)){
  echo "
    <tr>
      <td>".$zaznam['id']."</td>
      <td>".$zaznam['jmeno']."</td>
      <td>".$zaznam['UnixRazitkoZDateTime']."</td>
      <td>".$zaznam['DateTimeZCasovehoRazitka']."</td>
      <td>".$zaznam['DateTimeZCasovehoRazitkaFormatovany']."</td>
    </tr>
  ";
}
echo "</table>";
?>

MySQL má spoustu možností, umí datumy sčítat, počítat rozdíly mezi nimi a spoustu dalších věcí, pokud máte zájem – podívejte se do MySQL manuálu – vaše nová bible ;-)


<Starší | tento článek | Novější>

Napsáno: 20. 12. 2007, 10:15 | Přečteno: 11018x | Kategorie: PHP School | Napsal: peCan.cz |
Komentáře: 31

Po dlouhý odmlce to zase rozjíždíš? :)) nebo tě jen na vánoce chytla taková můza? Pěkný článek ;-) ;-)

Odpověď | 2007-12-20 20:22:23 | Příspěvek upraven: 2007-12-20 20:22:39
.:. The Zero | www

(Jen pro představu – uložíte-li si datum v libovolném formátu do datového typu VARCHAR(255), bude zabírat místo 4–8 bajtů celých 256!)

Není pravda. To by bylo v případě CHAR, VARCHAR už podle jména zabírá variabilní prostor. Ale i tak není dobrý nápad ukládat v tom datum.

Odpověď | 2007-12-20 21:50:06

OK, napsal jsem to blbě. Zabírá X bajtů + 1 bajt pro zaznamenání délky. Čili kdybych chtěl uložit datum „9999–12–31 23:59:59“, zabíralo by 20 bajtů (19+1).

Dík za upozornění. V článku uvedený údaj by platil pro CHAR, kde se zbytek místa v db zaplní mezerami.

Odpověď | 2007-12-20 22:06:47

cau paradni clanek nechtel bys napsat clanek na nahodny obrazek z galerky? zrovna si s tim nevim rady. dokazal jsem si do promenne dat vsechna id fotek co mam ve tvaru 1,2,3,4,5,6,7­,8,9,10 a dat to jako pole $arrayid=arra­y($idfotek);

pak tam mam vyber jednoho toho prvku z pole

$random=array_ran­d($arrayid);

$id=$arrayid[$ran­dom];

echo $id; – a tady mi to vypise vsechny id jako bych dal vypsat idfotek.. nevedel jsem presne jak se zbavit u toho seznamu idecek posledni carky tady mam cely kod. jsem zatim jen zacatecnik samouk.

require „admin/db.php“;

$vsechnaid=mys­ql_query(„SELECT id FROM fotky ORDER BY id DESC“);

while ($vypisid = mysql_fetch_a­rray($vsechna­id)) {

$seznamid=$sez­namid.$vypisid[‚id‘]­.„,“;

}

$seznamid=$seznamid-„,“;

$posledniquery=mys­ql_query(„SELECT id FROM fotky ORDER BY id LIMIT 1“);

$poslednivypis = mysql_fetch_a­rray($posledni­query);

$seznamid=$sez­namid.„,“.$pos­lednivypis[‚id‘];

$arrayid=arra­y($seznamid);

$random=array_ran­d($arrayid);

$id=$arrayid[$ran­dom];

echo $id;

exit;

Odpověď | 2007-12-22 14:10:23 | Příspěvek upraven: 2007-12-22 14:11:50

hmmm pokud chces nahodny vyber tak zbytecne to vse srat do PHP jen nahodny vyber zrob SQLkem

SELECT * FROM tabulky ORDER BY RAND() LIMIT 10

napriklad:-)

Odpověď | 2007-12-22 14:13:53

tj… diky moc vyzkousim. zacatecnik se nezapre. :D

Odpověď | 2007-12-22 14:35:58 | Příspěvek upraven: 2007-12-22 14:36:32

dal me napadl navod na narozeniny.. odpocet proste za 50 dni mas narozeniny (18 let) nebo nejblizsi narozeniny ze vsech registrovanych slavi uzivatel adolfek dne 20.4.1990 (18 let). vim ze by to tam muselo byt odecteni data narozeni roku 1990 od data narozeni roku aktualniho ale prestupne roky a dalsi veci vubec netusim. chtel jsem to udelat na web ale vzdavam to nikde nemuzu nic poradneho najit a samotnemu mi to nejde. jinak web mam rozdelany na www.adolfek.borec.cz/kabr08/

Odpověď | 2007-12-22 16:49:58

Narozeniny jsou celkem psycho. Hlavně díky konci roku – cosi jsem teď napsal, ale asi by to šlo i jednodušeji.

Zjistí, kdo má do 14 dnů narozeniny (sloupec narozeniny, typ DATE)

/**
Spočítá číslo dne následujících narozenin a porovná,
jestli je mezi číslem dne aktuálního datumu
a číselem dne v určitém zvoleném intervalu.

MySQL 5 only, (c) peCan.cz, 2007
*/

SELECT
*
FROM `uzivatele`
WHERE
  IF(
    (
      TO_DAYS(MAKEDATE( EXTRACT(YEAR FROM CURDATE()),1))
      +
      (
        TIMESTAMPDIFF(
          DAY,
          MAKEDATE( EXTRACT(YEAR FROM CURDATE()),1),
          MAKEDATE( EXTRACT(YEAR FROM CURDATE()),DAYOFYEAR(narozeniny))
        )
      )
    ) < TO_DAYS(CURDATE()),

    /* Pokud jiz letos mel narozeniny, pricteme 365 dni*/
    (
      TO_DAYS(MAKEDATE( EXTRACT(YEAR FROM CURDATE()),1))
      +
      (
        TIMESTAMPDIFF(
          DAY,
          MAKEDATE( EXTRACT(YEAR FROM CURDATE()),1),
          MAKEDATE( EXTRACT(YEAR FROM CURDATE()),DAYOFYEAR(narozeniny))
        )
      )
    )+365,

    /* Jeste narozeniny nemel */
    (
      TO_DAYS(MAKEDATE( EXTRACT(YEAR FROM CURDATE()),1))
      +
      (
        TIMESTAMPDIFF(
          DAY,
          MAKEDATE( EXTRACT(YEAR FROM CURDATE()),1),
          MAKEDATE( EXTRACT(YEAR FROM CURDATE()),DAYOFYEAR(narozeniny))
        )
      )
    )
  )
BETWEEN
  TO_DAYS(CURDATE())
AND
  TO_DAYS(DATE_ADD(CURDATE(), INTERVAL 14 DAY))

Pozor, funguje jen na MySQL 5!

Odpověď | 2007-12-22 23:36:09 | Příspěvek upraven: 2007-12-22 23:51:11

diky moc uz to pouzivam!! teda ted na tom pracuju. kdybys nekdy vymyslel kod primo na pocet dni do narozenin bylo by to fajn. nebo ten pocet roku alespon (18let) apod

Odpověď | 2007-12-23 10:37:50 | Příspěvek upraven: 2007-12-23 10:43:53

Počet dní je primitivní – to je patrné i z předchozího příkladu. A věk taky není nic složitého (pokud nechceš zjišťovat, zda již narozeniny letos měl)…

/**
Počet dní od narození, kolitáté LETOS bude mít narozeniny
*/
SELECT
(TO_DAYS(CURDATE()) - TO_DAYS(`narozeniny`) ) AS `PocetDnu`,
(YEAR(CURDATE())-YEAR(`narozeniny`)) AS `Vek`
FROM `test`
2007-12-23 10:55:56

diky edit: jo takhle.. ten pocet dni jsem myslel DO narozenin.. a ten vek je takhle bez overeni jestli uz narozky mel docela nepouzitelny. to budou mit vsichni kazdy leden o rok vic. nejlepsi by bylo kdybych si mohl vypsat ze do 30 dnu oslavi narozeniny tito uzivatele a tam bych mel i pocet dni do narozenin a v zavorce kolik oslavi roku. ale to uz je docela slozite. jestli budes mit nekdy cas tak bych za to byl vdecny.

Odpověď | 2007-12-23 11:18:32 | Příspěvek upraven: 2007-12-23 11:29:20

Vypíše počet dní do narozenin a kolik mu bude let.

(
SELECT
  *,
  (TO_DAYS(MAKEDATE( EXTRACT(YEAR FROM CURDATE()),DAYOFYEAR(narozeniny))) - TO_DAYS(CURDATE()) ) AS `DoNarozenin`,
  (YEAR(CURDATE())-YEAR(`narozeniny`)) AS `Vek`
  FROM test
  WHERE
    (
      TO_DAYS(MAKEDATE( EXTRACT(YEAR FROM CURDATE()),1))
      +
      (
      TIMESTAMPDIFF(
        DAY,
        MAKEDATE( EXTRACT(YEAR FROM CURDATE()),1),
        MAKEDATE( EXTRACT(YEAR FROM CURDATE()),DAYOFYEAR(narozeniny))
      )
      )
    )
    BETWEEN
      TO_DAYS(CURDATE())
    AND
      TO_DAYS(DATE_ADD(CURDATE(), INTERVAL 14 DAY))
)UNION(
SELECT
  *,
  (TO_DAYS(MAKEDATE( EXTRACT(YEAR FROM CURDATE()) + 1 ,DAYOFYEAR(narozeniny))) - TO_DAYS(CURDATE()) ) AS `DoNarozenin`,
  (YEAR(CURDATE())-YEAR(`narozeniny`)) + 1 AS `Vek`
  FROM test
  WHERE
    (
      TO_DAYS(MAKEDATE( EXTRACT(YEAR FROM CURDATE()),1))
      +
      (
      TIMESTAMPDIFF(
        DAY,
        MAKEDATE( EXTRACT(YEAR FROM CURDATE()),1),
        MAKEDATE( EXTRACT(YEAR FROM CURDATE()),DAYOFYEAR(narozeniny))
      )
      )
    )+ 365
    BETWEEN
      TO_DAYS(CURDATE())
    AND
      TO_DAYS(DATE_ADD(CURDATE(), INTERVAL 14 DAY))
)
Odpověď | 2007-12-23 14:16:19

diky moc tohle presne jsem mel na mysli..

Odpověď | 2007-12-23 15:32:55

prosimte.. zjistil jsem jdnu takovou chybicku a nevim co s ni. kdyz maji dva lide ve stejny den narozeniny tak to vypise nejdriv moje jmeno (20.04.) a potom kamosku (20.04.), a problem je v tom ze u me to napise za 99 dni a u ni za 100 dni.. nevim proc.

Odpověď | 2008-01-12 00:09:20

halo? help pls.. fakt si nevim rady. vypada to ze to chybuje jen u tech co maj stejny datum narozeni. ale nevim to jiste.

2008-01-15 20:16:44

peCan.cz: co takhle další článek na toto téma o SQL?

Odpověď | 2007-12-23 14:38:44
.:. pajah | www | mail

Pěkné ;-)

Odpověď | 2007-12-24 09:47:42

prosimte sry ze porad otravuju, ale kdyz mam tohle:

(„SELECT *, DATE_FORMAT(narozeni, ‚%d.%m.%Y‘) AS narozeni, DATE_FORMAT(poslprihlaseni, ‚%d.%m.%Y, v %H:%i‘) AS poslprihlaseni FROM uzivatele ORDER BY id DESC“)

a potrebuju ty uzivatele seradit abecedne.. jak to udelat? to bude nejak ORDER BY.....? nemuzu to nikde najit.

Odpověď | 2007-12-24 15:17:55 | Příspěvek upraven: 2007-12-24 15:21:11
ORDER BY {sloupec_s_nazvy_uzivatelu}

nebo

ORDER BY {sloupec_s_nazvy_uzivatelu} DESC

jedno bude od A a druhé od Z, musíš vyzkoušet, přesně nevím už na mě leze senilita :-)

Odpověď | 2007-12-24 16:45:05

diky! vesele vanoce!

Odpověď | 2007-12-24 17:03:06
.:. DJ Miky | www

Ach, ta čeština…

Práce s daty a ne datumy :)

Odpověď | 2007-12-26 23:26:09

njn, já jsem ten češtin, přesto se vsadím že moje chybná češtin verze bude vyhledávanější;-)

Odpověď | 2007-12-27 08:26:23

Promiň, ale hrozně mě bije do očí ten nadpis – DATY a ne DATUMY!! Horší ale je, jak to formulovat, aby to vyjařovalo datum a ne data |-/ Ještě jednou promiň, na kvalitě článku to neubírá…

Odpověď | 2008-02-05 20:23:04

Aha, ty neumíš gramatiku, já zase číst :-D

Odpověď | 2008-02-05 20:24:08
.:. Martin

Perfektní článe. Moc děkuji, M.

Odpověď | 2008-02-22 23:01:37
.:. piem | www | mail

Dobrý den pane Petře Graubnere

Jmenuji se Pavel Macalla a chtěl jsem Vás požádat o pár rad.Mam web stránku z názvem http://www.piem.cz/ a momentálně mám, promně nevyřešitelný problěm s dizkuzí najde te ji http://www.piem.cz/diskuze.php. Jak sám vidíte je tam spusty upozornění.má domená php podporuje a i mysql a atd.Urči tě se teď ptáte jestli mám vytvořenou databázi na zapisování dat,mám , ale nejsem si jist zda-li dobře a o co mi jde.

Nyní Vás prosím zda byste se nemohl podívat jaký je problem a případně mi poradit co mám špatně byl bych Vám zavázán.

P.S.Odepsat mi můžete na email Pavel.Macalla@seznam.cz Předem děkuji Přeji přýjemnž den naschledanou.

Odpověď | 2008-07-03 12:31:57

Ahoj, podle chyb nejsi připojený k databázi.

Odpověď | 2008-07-04 11:46:13
.:. piem | www | mail

Bobrý den děkuji za odpověd

Chtěl jsem se zeptat jestli by ste mi stím mohl pomoct šlo by to ? byl bych Vám zavázán

děkuji S pozdravem Pavel Macalla (piem)

Odpověď | 2008-07-05 15:32:25

ozvi se na ICQ.

Odpověď | 2008-07-05 20:10:49

nechce sa mi to cele citat mam ulozene time v databaze (sekundy od 1970) a chcem ze komentar pridany pr. 15.11.2008 o 15:16 pls mail dik

Odpověď | 2008-11-17 16:33:01
.:. Petrdddd

adsgdsg sdag dsh dsh dsh ads8-o

Odpověď | 2009-12-21 00:15:36
Přidej komentář
Jméno
Web
Mail
Kontrola Zadejte číslo pět
Text

:-)
:-D
:-(
|-/
:-[]
;-)
8-|
8-o
Zde formátuje Texy! (syntaxe)
  • Příklad formátování příspěvku:

    **Tlustě**
    *Kurzíva*
    "Odkaz":http://neco.cz nebo http://neco.cz (i www.neco.cz)
    /---code php
    php kód
    \---

  • Pro odeslání zprávy můžete použít klávesovou zkratku Alt+S. (Podporují jen některé prohlížeče)
  • HTML znaky budou převedeny na entity.
  • Vyjadřujte se tu jako doma, ať víme jak to u Vás vypadá.
  • Odkazy začínající http:// budou automaticky převedeny na odkazy , nepoužívejte však v jednom příspěvku více jak 3 - to dělají jen spam roboti:-)
Víte jaký je rozdíl mezi holkou modrookou a čípkem? ??? Holka modrooká sedává u potoka a čípek se dává do prdele.
Petr Graubner | ICQ- 264912843, Jabber - pecan27@njs.netlab.cz | RSS | Šlape na miniRS | Styl CoolStyle | Zpracováno za: 0.375s | Počet SQL dotazů ve fóru: 1 | Admin