PHP ponúka dostatok funkcií na prácu s časom a dátumom. Ukážeme si, ako pomocou nich vytvoriť svoj vlastný kalendár v PHP. Článok je určený skôr začiatočníkom, ale taktiež môže pomôcť pri tvorbe vašej vlastnej knižnice na kalendáre.

Oboznámenie sa s funkciami

Začiatočníkom najskôr odporúčam prečítať si článok Jiřího Lahvičky, v ktorom sa stručne zoznámia s funkciou date a mktime. K funkcii mktime si však povieme viac, čo využijeme pri tvorbe kalendára. Syntax je nasledovná:

int mktime ( int hour, int minute, int second, int month, int day, int year [, int is_dst])

Funkcia vracia počet sekúnd od dňa (pred dňom) 1. 1. 1970 po nami zadaný dátum. Nepovinným parametrom is_dst určujeme, či sa jedná o letný čas (is_dst=1), alebo nie (is_dst=0), default hodnota je -1, vtedy PHP nastavuje hodnotu letného času samo. Funkcia umožňuje vkladať jednotlivé parametre aj v „nekorektnom formáte“. Príklad z manuálu k PHP:

echo date („M-d-Y“, mktime (0,0,0,12,32,1997));
echo date („M-d-Y“, mktime (0,0,0,13,1,1997));
echo date („M-d-Y“, mktime (0,0,0,1,1,1998));
echo date („M-d-Y“, mktime (0,0,0,1,1,98));

Každý riadok skriptu vráti rovnaký dátum „Jan-01-1998“. Rok môže byť zadaný aj ako čísla 0-69 reprezentujúce roky 2000-2069 a čísla 70-99 reprezentujúce roky 1970-1999. Funkcia nám jednoducho umožňuje zistiť posledný deň predchádzajúceho mesiaca tak, že za parameter day dosadíme 0. Záporné čísla parametra day budú znamenať predchádzajúce dni mesiaca pred mesiacom month. Opäť si uvedieme príklad z manuálu k PHP:

$lastday = mktime (0,0,0,3,0,2000);
echo strftime („Posledným dňom vo Februári (únor) 2000 je: %d“, $lastday);
$lastday = mktime (0,0,0,4,-31,2000);
echo strftime („Posledným dňom vo Februári (únor) 2000 je: %d“, $lastday);

Výsledkom v oboch prípadoch bude reťazec „Posledným dňom vo Februári (únor) 2000 je: 29“. To, s čím sme sa doteraz oboznámili, nám postačí k vytvoreniu kalendára. Možno vás však bude ešte zaujímať od a do ktorého dátumu si budeme môcť prezerať kalendár. Funkcia mktime nám vracia počet sekúnd od 1. 1. 1970, typu integer, čo znamená (ak rátame s 32 bitovým číslom so znamienkom), že hranica je medzi rokmi 1902 a 2037. To však iba na systémoch, v ktorých PHP vráti aj zápornú hodnotu, teda počet sekúnd pred 1. 1. 1970. PHP v4.3 pre Windows na dni pred 1. 1. 1970 vypisovalo „Windows does not support negative values for this function“. Čo som odskúšal na unixových systémoch, mktime vrátilo aj záporné hodnoty a kalendár bolo možné zobrazovať aj pred rokom 1970.

Algoritmus pre kalendár

V úvode si vytvoríme dva objekty, $color a $date:

class Ccolor {
  var $wday_txt = „#bbbbdd“;
  var $wend_txt = „#ddbbbb“;
  var $wday = „#ccccee“;
  var $wend = „#eecccc“;
  var $wday_noact = „#9090a6“;
  var $wend_noact = „#a69090“;
  var $today = „#ffffc0“;
  }
// objekt
$color = new Ccolor ();
class Cdate {
  var $day;
  var $month;
  var $year;
  function Cdate ($time) {
    $this->day = date („j“, $time);
    $this->month = date („n“, $time);
    $this->year = date („Y“, $time);
    }
  }
// objekt
$date = isset ( $time ) ? new Cdate ( $time ) : new Cdate ( time () );

Do $color si uložíme farby kalendára. Do $date hodnoty dátumu (mesiac, deň a rok), buď podľa premennej $time, ktorá bude obsahovať počet sekúnd od 1. 1. 1970 a podľa nej budeme zobrazovať kalendár pre mesiac, ktorý vráti funkcia date („n“, $time), prípadne ak nebude $time nastavená, zobrazíme kalendár mesiaca aktuálneho dátumu. Ďalej naplníme polia názvami mesiacov roka a dní týždňa:

// názvy mesiacov roka – indexované od 1
$m = array (1 => „Január“, „Február“, „Marec“, „Apríl“, „Máj“, „Jún“, „Júl“, „August“, „September“, „Október“, „November“, „December“);
// názvy dní týždňa
$d = array („Po“, „Ut“, „St“, „Št“, „Pi“, „So“, „Ne“);

Začneme generovať prvý výstup, riadky tabuľky s názvami dní v týždni:

echo „<table border=’0′ cellspacing=’1′ cellpadding=’0’>“;
echo „<tr align=’center‘ valign=’middle’>“;
for ($i=0 ; $i<count($d) ; $i++) {
  $bgcolor = ($i < 5) ? $color->wday_txt : $color->wend_txt;
  echo „<td bgcolor=’$bgcolor‘ class=’cell’><b>“.$d[$i].“</b></td>“;
  }
echo „</tr>“;

Zistíme si prvý deň kalendára:

// prvý deň kalendára (pondelok)
// hodnota 0 namiesto parametra $day v mktime vráti posledný deň predchádzajúceho mesiaca
// date („w“) vráti [0-6] (Ne,Po,Ut,..)
$date->day = 1 – date („w“, mktime (0, 0, 0, $date->month, 0, $date->year));

V nasledujúcej ukážke kódu prechádzame všetky dni za dňom $date->day, čo je prvý deň kalendára. Na základe podmienok v cykle je vykonaný buď prechod na nový riadok, alebo celkové ukončenie cyklu. Naviac sa mení hodnota $bgcolor (pozadia bunky tabuľky) podľa dní aktuálneho, alebo iného mesiaca. Na začiatku definovaná premenná $act_month_end je pravdivá, ak bol zaznamenaný prechod cez mesiac, ktorý sa zobrazuje. Nekonečný cyklus for(;;) bude ukončený príkazom break, ktorý sa vyvolá po prechode zobrazovaného mesiaca a nájdení pondelka nasledujúceho mesiaca. Pripomeniem, že pri zavolaní funkcie mktime (0, 0, 0, $date->month, $date->day++ , $date->year) sa parameter $date->day inkrementuje až po vykonaní funkcie.

$act_month_end = false;
for (;;) {
  // deň za dňom
  $t = mktime (0, 0, 0, $date->month, $date->day++ , $date->year);
  // číslo mesiaca [1-12]
  $date_n = date („n“, $t);
  // číslo týždňa [0-6] (Ne,Po,Ut,..)
  $date_w = date („w“, $t);
  if ($date_n == $date->month)
    $act_month_end = true;
  // prvý pondelok v ďalšom mesiaci – ukonči riadok – koniec cyklu
  if ($act_month_end == true && $date_n != $date->month && $date_w == 1) {
    echo „</tr>“;
    break;
    }
  // pondelok – nový riadok
  else if ($date_w == 1)
    echo „<tr align=’center’>“;
  // predchádzajúci, alebo nasledujúci mesiac
  if ($date_n != $date->month)
    $bgcolor = ($date_w==0 || $date_w==6) ? $color->wend_noact : $color->wday_noact;
  // aktuálny mesiac
  else
    $bgcolor = ($date_w==0 || $date_w==6) ? $color->wend : $color->wday;
  if (date(„dmY“,$t) == date(„dmY“))
    $bgcolor = $color->today;
  $title = date („j.n. Y“, $t);
  echo „<td title=’$title‘ bgcolor=’$bgcolor‘ class=’cell’><a href=’go.php?time=$t’>“.date („d“, $t).“</a></td>“;
  // nedeľa – koniec riadku
  if ($date_w == 0)
    echo „</tr>“;
  }
echo „</table>“;

Dni kalendára obsahujú odkazy na skript go.php, ktorého vstupom bude premenné $time, ktorú prevedieme do čitateľného formátu:

<?= „Vybral si deň: “ . date („j.n. Y“, $time)?>

Detaily

Kalendár obohatíme o zobrazenie aktuálneho mesiaca a roka s odkazmi na predchádzajúci a nasledujúci mesiac:

$time = mktime (0, 0, 0, $date->month – 1, $date->day, $date->year);
echo „<a href=’index.php?time=$time’>&lt;&lt;</a>“;
echo „<b> [ „.$m[$date->month].“ $date->year ] </b>“;
$time = mktime (0, 0, 0, $date->month + 1, $date->day, $date->year);
echo „<a href=’index.php?time=$time’>&gt;&gt;</a>“;

Aby sa dal zobraziť kalendár ľubovoľného mesiaca a roku, vytvoríme si formulár s mesiacom, rokom a založíme konštanty umožňujúce zadávať hranice kalendára. Formulár nám bude predávať premenné $month a $year, ktoré v úvode skriptu premeníme na počet sekúnd od 1. 1. 1970. V ďalšom riadku prekontrolujeme začiatok a koniec roku požadovaného kalendára podľa konštánt YEAR_MIN a YEAR_MAX. Ak nevyhovuje podmienke, bude zobrazený kalendár s aktuálnym mesiacom. Konštanta YEAR_EXTENT určuje, v akom rozsahu (plus, mínus) sa zobrazia roky vo formulári od aktuálne zobrazovaného roku.

// integer v PHP je 32 bitové číslo so znamienkom = 2 ^ 31 sekúnd (pred alebo po) 1.1.1970
// spodná hranica niekde pri 14.12.1901
// horná hranica niekde pri 19.1.2038
// od ktorého roku bude možné zobrazovať kalendár
// rozsah <1902, 2037> pre Unix
// rozsah <1971, 2037> pre Windows
define (YEAR_MIN, 1980);
// do ktorého roku bude možné zobrazovať kalendár
// rozsah <1902, 2037> pre Unix
// rozsah <1971, 2037> pre Windows
define (YEAR_MAX, 2020);
// plus, mínus koľko rokov sa má zobraziť pri SELECT výbere
define (YEAR_EXTENT, 3);
// hodnoty z formulára, prevedieme ich na time formát
if (isset($month) && isset($year))
  $time = mktime (0, 0, 0, $month, 1, $year);
// kontrola začiatku a konca roka
if ($time < mktime (0, 0, 0, 1, 1, YEAR_MIN) || $time > mktime (0, 0, 0, 12, 31, YEAR_MAX))
  $time = time ();



echo „<form action=’index.php’>“;
echo „<select name=’month’>“;
for ($i=1 ; $i<=count($m) ; $i++)
  echo ($i == $date->month) ? „<option value=’$i‘ selected>$m[$i]“ : „<option value=’$i’>$m[$i]“;
echo „</select>“;
echo „<select name=’year’>“;
$i = ($date->year-YEAR_EXTENT < YEAR_MIN) ? YEAR_MIN : $date->year-YEAR_EXTENT;
for ( ; $i<=$date->year+YEAR_EXTENT && $i<=YEAR_MAX; $i++)
  echo ($i == $date->year) ? „<option value=’$i‘ selected>$i“ : „<option value=’$i’>$i“;
echo „</select>“;
echo „<input type=’submit‘ value=’Ok’>“;
echo „</form>“;

Funkčný príklad si môžete odskúšať, zabalené zdrojové kódy sú vám tiež k dispozícii. V ukážke sú definované štýly pre element „body“ a bunku tabuľky „.cell“, konečná vizuálna podoba kalendára je už na vás. Ako inšpiráciu si môžete prezrieť verziu s JavaScriptom, ktorý obsahuje aj zvýrazňovanie dní pri prechode kurzora myši. Zabezpečuje to nasledovná funkcia:

function Highlight (el_id, hl) {
  if (hl)
    old_background_color = document.getElementById(el_id).style.backgroundColor
  document.getElementById(el_id).style.backgroundColor = hl ? „#ffffff“ : old_background_color;
  }

Jedná sa o riešenie, kde si najskôr zapamätáme pôvodnú farbu elementu, nastavíme novú, a pri udalosti onmouseout vrátime pôvodnú farbu. Takto si zabezpečíme zvýrazňovanie elementov bez toho, aby sme funkciu volali s parametrom pôvodnej farby. Udalosti (onclick, onmouse) sa nachádzajú v bunkách tabuľky dní kalendára (upravené riadky pôvodného skriptu):

  $title = date („j.n. Y“, $t);
  $el_id = date („jn“, $t);
  echo „<td title=’$title‘ bgcolor=’$bgcolor‘ class=’cell‘ id=’$el_id‘ onmouseover=’Highlight ($el_id, true)‘ onmouseout=’Highlight ($el_id, false)’><a href=’go.php?time=$t’>“.date („d“, $t).“</a></td>“;

Kalendár zobrazuje odkazy aj na dni predchádzajúceho a nasledujúceho mesiaca, ak by to niekomu nevyhovovalo, stačí urobiť malé úpravy, nakoľko sa pri týchto dňoch zobrazuje iná farba pozadia bunky.

Starší komentáře ke článku

Pokud máte zájem o starší komentáře k tomuto článku, naleznete je zde.

3 Příspěvků v diskuzi

  1. Jeník má pravdu, este doplním, že treba doplniť aj:

    $time = $_GET[„time“];

    pre funkcnost posunu mesiacov cez šípky, čiže treba doplniť za:
    // hodnoty z formulára, prevedieme ich na time formát
    $month = $_GET[„month“];
    $year = $_GET[„year“];
    $time = $_GET[„time“];

Odpovědět