Určení intervalu mezi dvěma časy v rozsahu pracovní doby
Skočit na navigaci
Skočit na vyhledávání
Jednou z úloh s časem je určení reakční doby. Pokud je reakční doba počítaná bez omezení, tak se jedná o jednoduchou úlohu. Komplikace nastávají, když do reakční doby počítáme pouze čas omezený pracovní dobou. Pro výpočet mohu použít vzorec rc = start - konec - pocet_prac_dnu(start, konec)*abs(zacatek_prac_doby - konec_prac_doby) - pocet_svatku_a_dnu_prac_volna*24. Určení počtu pracovních dnů je řešeno v článku PL/pgSQL - funkce generující tabulky.
-- vraci prvni pracovni okamzik po zadanem okammziku vcetne CREATE OR REPLACE FUNCTION FirstWTimestamp(ts timestamp, start_tm time) RETURNS timestamp AS $$ DECLARE holidays date[]; aux_day date; BEGIN aux_day := CAST(ts AS date); holidays := ARRAY(SELECT termin FROM StatniSvatky(aux_day, aux_day + 10)); IF NOT (isodow(aux_day) IN (6,7) OR aux_day = ANY(holidays)) THEN RETURN ts; -- nedochazi k posunu, vraci puvodni cas END IF; LOOP aux_day := aux_day + 1; EXIT WHEN NOT (isodow(aux_day) IN (6,7) OR aux_day = ANY(holidays)); END LOOP; RETURN aux_day + start_tm; END; $$ LANGUAGE plpgsql STABLE STRICT; -- vraci posledni pracovni okamzik pred zadanym okamzikem vcetne CREATE OR REPLACE FUNCTION LastWTimestamp(ts timestamp, end_tm time) RETURNS timestamp AS $$ DECLARE holidays date[]; aux_day date; BEGIN aux_day := CAST(ts AS date); holidays := ARRAY(SELECT termin FROM StatniSvatky(aux_day, aux_day + 10)); IF NOT (isodow(aux_day) IN (6,7) OR aux_day = ANY(holidays)) THEN RETURN ts; -- nedochazi k posunu, vraci puvodni cas END IF; LOOP aux_day := aux_day - 1; EXIT WHEN NOT (isodow(aux_day) IN (6,7) OR aux_day = ANY(holidays)); END LOOP; RETURN aux_day + end_tm; END; $$ LANGUAGE plpgsql STABLE STRICT; CREATE OR REPLACE FUNCTION DiffWTime(start_ts timestamp, end_ts timestamp, start_wt time, end_wt time) RETURNS interval AS $$ DECLARE nwh interval := justify_interval(start_wt - end_wt + interval '1day'); _start_ts timestamp := FirstWTimestamp(start_ts, start_wt); _end_ts timestamp := LastWTimestamp(end_ts, end_wt); svatky date[]; _start_d date; _end_d date; result interval; BEGIN -- jednoduchy algoritmus, nepredpokladam prilis velky pocet cyklu _start_d := CAST(_start_ts AS date); _end_d := CAST(_end_ts AS date); svatky := ARRAY(SELECT termin FROM StatniSvatky(_start_d, _end_d)); result := _end_ts - _start_ts; WHILE _start_d <> _end_d LOOP IF isodow(_start_d) IN (6,7) OR _start_d = ANY(svatky) THEN result := result - interval '24hours'; ELSE result := result - nwh; END IF; _start_d := _start_d + 1; END LOOP; -- prepocet na hodiny RETURN EXTRACT (epoch FROM justify_interval(result)) / 3600.0 * interval '1hour'; END; $$ LANGUAGE plpgsql IMMUTABLE STRICT;
Použití:
postgres=# select diffwtime(current_timestamp::timestamp+interval '2hours', current_timestamp::timestamp + interval '1day', '8:00:00','16:30:00'); diffwtime ----------- 06:30:00 (1 row)
V praxi se mi osvědčilo pro hodnoty související s pracovní dobou používat vždy timestamp a nikoliv date - právě z důvodu korektního a jednoduchého výpočtu reakční doby.