skok na hlavní menu | menu sekce Aktuality
V životě i v aplikacích často dochází k situacím, že je potřeba znát den, který bude například za 4 pracovní dny. Tedy za 4 dny, do kterých se nepočítá sobota, neděle, ale ani státní svátky a jiné významné dny. Tento článek popisuje naše PHP řešení, ve kterém si můžeme přesně definovat, které dny v roce považujeme za svátky (nepracovní dny). A to včetně dnů s nestálým datem v roce jako jsou například Velikonoce.
Předem bude asi správné poznamenat, že na Internetu je k nalezení řada řešení, ale povětšinou jste v nich v něčem limitováni. Buď mezi nepracovní dny řadí pouze víkendy, nebo mají státní svátky pouze napevno v kódu a podobně. V našem řešení za nepracovní dny považujeme:
Nyní již k samotnému řešení. To si rozdělíme na dvě části. Povíme si, co je třeba udělat na straně databáze, a pak si popíšeme, jak s údaji z databáze pracujeme v PHP.
Databáze je v našem případě potřeba, protože umožňuje uživatelům nastavit, které dny považují za nepracovní. Tohoto by šlo docílit i pomocí konfiguračních souborů přímo v PHP, ale není to tak pohodlné řešení, co se týče provádění změn.
Konkrétně jsme si v databázi vytvořili tabulku, kterou v tomto článku budeme nazývat holidays. Do té si ukládáme záznamy o dnech v měsíci, které považujeme za svátky. U každého svátku máme uloženo jeho jméno (v obrázku níže name), měsíc v roce (month), den v roce (day) a ještě dva další údaje, které souvisí s tím, že chceme evidovat i dny v roce, které nemají stálé datum. V obrázku níže je vidět, že tyto údaje jsou ve sloupcích float_module a float_method. Ve float_module si ukládáme název třídy (modulu), ve které se nachází metoda, která vrátí datum konkrétního dnu v roce. Název této metody je uložen ve sloupci float_module. Protože lze přesné datumy ke dnům dopočítávat, nemusí být měsíc ani den povinné údaje.

To, jak do databáze dostat konkrétní záznamy, není součástí tohoto řešení. Někomu může stačit vykonat přímo v databázi potřebné INSERT scripty, někdo si bude chtít vyvinou rozhraní, přes které umožní uživatelům si vše naklikat. V první případě by scripty mohly vypadat takto:
INSERT INTO `holidays`(`name`,`mounth`,`day`,`float_module`,`float_method`) VALUES
("Velikonoce",NULL,NULL,"Holidays","getEaster"),
("Štědrý den",12, 24,NULL,NULL),
("1. svátek vánoční",12,25,NULL,NULL),
("2. svátek vánoční",12,26,NULL,NULL);
Vysvětlení: Pro získání datumu Velikonoc používáme třídu Holidays, ve které je metoda getEaster().
V této části uvedeme několik PHP metod, které používáme při práci se svátky. Všechny máme uloženy v jedné třídě (modulu) s funkcionalitami pro práci se svátky.
Ke zjištění, na jaký den v roce přichází Velikonoce, existuje v PHP funkce easter_date(). My si její výstup pouze ukládáme do jiného formátu.
public function getEaster(){
$velikonoce=(easter_date());
$mesic=date("m",$velikonoce);
$den=date("d",$velikonoce);
$ar=array();
$ar["day"]=$den;
$ar["month"]=$mesic;
return $ar;
}
V následující metodě getHolidays() získáme z databáze záznamy ke svátkům a tam, kde je nastaveno float_method a float_module, dopočteme konkrétní datum. V kódu je použita třída DB, která zastupuje nějakou třídu umožňující přístup k databázi a práci s ní. V této třídě máme metodu query, která provede databázový dotaz. Dále pak máme třídu, jejíž instance vrátí pomocí metody fetchAll() v poli všechny výsledky výše uvedeného databázového dotazu. K tomu existuje řada podobných řešení, proto není třeba uvádět zrovna to naše.
Dále v kódu uvádíme metodu getModule(). V té je vytvořena a vrácena instance potřebné třídy. Její tělo si naimplementujte tak, jak je pro vaši aplikaci nejvýhodnější.
public function getHolidays(){
$db=new DB();
$holidays= $db->query("SELECT * FROM holidays ORDER BY month,day")->fetchAll();
//projdeme dny, kde je nastavena float_method, k nim budeme muset datum jeste dopocist
$output=array();
foreach($holidays as $day){
if(isset($day["float_method"]) && isset($day["float_module"])){
$m=$day["float_module"];
$method=$day["float_method"];
$module=$this->getModule($m);
$retDay=$module->$method();
$day["day"]=$retDay["day"];
$day["month"]=$retDay["month"];
}
$output[]=$day;
}
return $output;
}
Jako odpověď na tuto otázku najdete na Internetu řadu algoritmů. My jsme použili řešení popsané v článku na stackoverflow.com , které jsme drobně upravili.
public function dateFromBusinessDays($days, $dateTime=null) {
$dateTime = is_null($dateTime) ? time() : $dateTime;
$_day = 0;
$_direction = $days == 0 ? 0 : intval($days/abs($days));
$_day_value = (60 * 60 * 24);
while($_day != $days) {
$dateTime += $_direction * $_day_value;
$_day_w = date("w", $dateTime);
if ($_day_w > 0 && $_day_w < 6) {
$_day += $_direction * 1;
}
}
return $dateTime;
}
Toto se v našem případě řeší rekurzivní metodou getWorkingDay(), která používá metody uvedené výše. Spočteme si, jaký pracovní den bude za X pracovních dnů bez uvažování svátků (uvažují se tedy jen víkendy), a pak zjišťujeme, jestli na získaný časový úsek nepřipadají nějaké svátky. Pokud ano, datum se opět posune. Může se ovšem posunout na datum, které je opět o víkendu – potenciálně se tedy kvůli víkendům a svátkům může posunutí opakovat a skončí až tehdy, když už není posunutí třeba.
/**
* vrati den, ktery bude od zadaneho datumu vzdaleny zadany pocet dni
* @param - now - den, ktery je vztazny pro vypocet, format: Y-m-d
* @param - offset - pocet pracovnich dni, ktere mame k zadanemu datumu pricist
*/
public function getWorkingDay($now,$offset){
$feed_holidays=$this->getHolidays();//seznam svatku
$finishDate=date("Y-m-d",$this->dateFromBusinessDays($offset,strtotime($now)));//mame koncovy datum, ale neuvazovali jsme svatky
$parts=split("-",$finishDate);
$mounthTo=$parts[1];
$dayTo=$parts[2];
$yearTo=$parts[0];
$parts=split("-",$now);
$mounthFrom=$parts[1];
$dayFrom=$parts[2];
$yearFrom=$parts[0];
$pricistKvuliSvatkum=0;
$from=strtotime($now);
$to=strtotime($finishDate);
foreach($feed_holidays as $day){//pokud svatek lezi mezi nasimi daty, tak pricteme den
$d=$yearFrom."-".$day["mounth"]."-".$day["day"];//datum, kdy je svatek
$timestamp=strtotime($d);//timestamp daneho svatku
if($timestamp>$from && $timestamp<=$to && !$this->isInWeekend($timestamp)){ //svatek nesmi byt o vikendu, jinak je jiz zapocten
$pricistKvuliSvatkum++;
}
}
//pokud mame kvuli svatkum pripocist urcity pocet dnu, tak hrozi, ze po dnu je zase vikend, coz nas vede vlastne k rekurzivnimu chovani
if($pricistKvuliSvatkum==0) return $finishDate;
else {
//k datumu pripocteme pocet svatku
return $this->getWorkingDay($finishDate,$pricistKvuliSvatkum);
}
}
| E-mail: info@tovarna.cz |
| Telefon: +420 274 776 344 |
| Mobil: +420 739 654 469 |
| Doubravčická 1474/21 |
| 100 00 Praha 10 |
| Telefon: +420 274 776 344 |
Podmínky užití | O aplikaci | Prohlášení o přístupnosti | Mapa stránek | ISO 9001:2008 | FAQ | Slovník pojmů
Pečlivě vyrobila TOVARNA.CZ, s.r.o. | Tato prezentace využívá systém pro správu obsahu WebRedie.