Que tipo de esquema SQL você sugeriria para armazenar horas e feriados, e que tipo de consulta verificar se um restaurante está aberto? Agora eu tenho isso:
CREATE TABLE hours (
"restaurant" integer NOT NULL REFERENCES restaurants ON DELETE CASCADE,
"dow" integer NOT NULL,
"open" time NOT NULL,
"close" time NOT NULL
);
CREATE FUNCTION is_open(r integer) RETURNS boolean AS $$
DECLARE
h record;
t time;
BEGIN
SELECT open, close INTO h
FROM hours WHERE restaurant = r AND dow = EXTRACT(dow FROM now());
IF NOT FOUND THEN
RETURN false;
END IF;
t := current_time;
IF h.close <= h.open THEN
RETURN (t < h.close OR t > h.open);
ELSE
RETURN (t > h.open AND t < h.close);
END IF;
END;
$$ LANGUAGE plpgsql;
Mas isso realmente não funciona, porque, por exemplo, um restaurante pode estar aberto até às 2:00 da manhã, altura em que eu precisaria verificar a anterior dow
.
Para tornar as coisas um pouco mais complicadas, tenho que lidar com feriados:
CREATE TABLE holidays (
"restaurant" integer NOT NULL REFERENCES restauraunts ON DELETE CASCADE,
"day" date NOT NULL
);
Que tem o mesmo problema - se um restaurante está aberto das 15:30 às 2:00, isso significa que eles também estão fechados para o bloco de meia-noite a duas.
Eu não fui capaz de envolver minha cabeçaencontrar uma solução limpa e elegante para isso (vários desarrumados vieram e se foram). Eu preciso ir dar um passeio e voltar a isso - nesse meio tempo, eu imaginei que eu deixaria vocês darem uma olhada nisso.
Respostas:
1 para resposta № 1Parece meio errado responder a minha própria pergunta, mas eu encontrei algo que parece funcionar, por mais confuso que seja:
CREATE FUNCTION is_open(r integer) RETURNS boolean AS $$
DECLARE
t time;
yesterday date;
dow_today integer;
dow_yesterday integer;
BEGIN
t := current_time;
yesterday := current_date - 1;
dow_today := EXTRACT(dow FROM current_date);
dow_yesterday := EXTRACT(dow FROM yesterday);
PERFORM * FROM hours
WHERE restaurant = r AND ((
dow = dow_today
AND NOT EXISTS(
SELECT * FROM holidays
WHERE restaurant = r AND day = current_date
) AND (
(open < close AND t > open AND t < close)
OR (open >= close AND t > open)
)
) OR (
open >= close AND dow = dow_yesterday
AND NOT EXISTS(
SELECT * FROM holidays
WHERE restaurant = r AND day = yesterday
) AND t < close
));
RETURN FOUND;
END;
$$ LANGUAGE plpgsql;
0 para resposta № 2
Para resumir os comentários:
1 - Use a estrutura de consulta geral desta questão.
2 - Adicione um sinalizador de bit à sua mesa para ClosePastMidnight
ou OpenPastMidnight
(O que for melhor para a sua maneira de pensar) que indica que o fechamento está no próximo dia de calendário e ajuste sua lógica de acordo.
0 para resposta № 3
Seu design de tabela tem informações que faltamtorna-se aparente quando você tenta desambiguar o que significa fechar e abrir. Tal como 12 a 2. Isso significa 2 horas ou 14? Sem fazer suposições, não há como resolvê-lo.
Use intervalos:
Por exemplo, se o seu restaurante é de 1:30 PM a 1:30 AM, isso pode verificar se now () está entre:
test=# select
now() > (now()::date || " 13:30")::timestamp
and now() < (now()::date || " 13:30")::timestamp + interval "12 hours";
?column?
----------
t
(1 row)
Dessa forma, apenas o início dow é importante e você não deve ter nenhum problema envolvente.