No SQL, CURRENT_DATE e CURRENT_TIME nao sao funcoes, e sim valores especiais: devolvem a data de hoje e a hora atual do dia sem um unico parenteses. Parece detalhe, mas e justamente a ausencia de () e o tipo do resultado que mais confundem as pessoas.
Valores especiais, sem parenteses
O padrao SQL define um punhado de "registradores especiais": CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIME, LOCALTIMESTAMP. Eles se escrevem como palavra-chave nua — nunca CURRENT_DATE().
SELECT CURRENT_DATE, CURRENT_TIME;
CURRENT_DATE produz um date — apenas ano, mes e dia.
CURRENT_TIME produz time with time zone (timetz, abreviado) — uma hora do dia mais o deslocamento de fuso.
CURRENT_TIMESTAMP combina os dois: data e hora completas com fuso (timestamptz).
Todos leem o instante em que a transacao comecou, e nao o instante em que a linha executa. Dentro de uma mesma transacao o CURRENT_DATE nao vai "pular" a meia-noite entre consultas — isso e comportamento do padrao, nao um bug.
Hoje no WHERE e em relatorios
O uso mais comum e selecionar as linhas de hoje. Voce pode comparar created_at (um timestamptz) direto com CURRENT_DATE, mas atencao aos limites: prefira um intervalo semiaberto [hoje, amanha).
SELECT id, email, created_at
FROM users
WHERE created_at >= CURRENT_DATE
AND created_at < CURRENT_DATE + INTERVAL '1 day';
Pedidos dos ultimos 30 dias — a aritmetica sobre CURRENT_DATE fica limpa, sem conversoes:
SELECT id, user_id, amount, status
FROM orders
WHERE status = 'paid'
AND created_at >= CURRENT_DATE - INTERVAL '30 days';
Pegadinha: nao escreva DATE(created_at) = CURRENT_DATE sobre um conjunto grande. Envolver a coluna em DATE(...) mata qualquer indice sobre created_at — o planejador nao consegue usa-lo e cai num seq scan. O intervalo >= ... AND < ... mantem o indice em jogo.
CURRENT_DATE como DEFAULT
CURRENT_DATE e um valor padrao perfeito para uma coluna de data: ele e avaliado a cada insercao.
CREATE TABLE signups (
user_id bigint PRIMARY KEY,
signed_on date NOT NULL DEFAULT CURRENT_DATE
);
INSERT INTO signups (user_id) VALUES (101);
Para campos de "criado em" voce geralmente prefere CURRENT_TIMESTAMP (ou now()), para guardar tambem a hora e o fuso:
ALTER TABLE orders
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP;
A diferenca chave: CURRENT_DATE guarda apenas o dia e descarta a hora, enquanto CURRENT_TIMESTAMP preserva o instante completo. Escolha o tipo da coluna com criterio — date contra timestamptz.
CURRENT_TIME contra um timestamp completo
CURRENT_TIME e a hora do dia, sem data associada. O tipo timetz carrega um deslocamento de fuso, mas nao uma data, e e ai que ele fica propenso a erro.
SELECT CURRENT_TIME AS clock,
CURRENT_TIMESTAMP AS full_moment,
CURRENT_TIMESTAMP::time AS local_clock;
- Voce nao consegue subtrair de forma confiavel um
timetz de outro cruzando uma fronteira de fuso — sem data, atravessar a meia-noite e ambiguo.
- Para a logica de "horario comercial" voce normalmente usa
CURRENT_TIMESTAMP::time (um time sem fuso), e nao CURRENT_TIME.
- Quando precisa de data e hora, a resposta certa quase sempre e
CURRENT_TIMESTAMP, e nao um par CURRENT_DATE + CURRENT_TIME.
Exemplo: conte um pedido como diurno se ele foi criado entre 09:00 e 18:00 no horario local.
SELECT id, amount
FROM orders
WHERE created_at::time BETWEEN TIME '09:00' AND TIME '18:00';
Diferencas entre bancos
- PostgreSQL: totalmente padrao.
CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP nao levam parenteses; now() e sinonimo de CURRENT_TIMESTAMP.
- MySQL:
CURRENT_DATE e CURDATE() sao equivalentes, assim como CURRENT_TIME e CURTIME(). O tipo TIME do MySQL nao guarda fuso, entao aqui nao existe timetz.
- ClickHouse: oferece
today() e now(); CURRENT_TIMESTAMP e aceito como alias, mas nao ha um tipo proprio de "hora do dia com fuso".
Se o codigo precisa ser portavel, fique com CURRENT_DATE/CURRENT_TIMESTAMP sem parenteses — a forma mais compreendida — e nao dependa da semantica de timetz fora do PostgreSQL.
No SQL,
CURRENT_DATEeCURRENT_TIMEnao sao funcoes, e sim valores especiais: devolvem a data de hoje e a hora atual do dia sem um unico parenteses. Parece detalhe, mas e justamente a ausencia de()e o tipo do resultado que mais confundem as pessoas.Valores especiais, sem parenteses
O padrao SQL define um punhado de "registradores especiais":
CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,LOCALTIME,LOCALTIMESTAMP. Eles se escrevem como palavra-chave nua — nuncaCURRENT_DATE().SELECT CURRENT_DATE, CURRENT_TIME; -- 2026-06-17 | 18:42:07.512+02CURRENT_DATEproduz umdate— apenas ano, mes e dia.CURRENT_TIMEproduztime with time zone(timetz, abreviado) — uma hora do dia mais o deslocamento de fuso.CURRENT_TIMESTAMPcombina os dois: data e hora completas com fuso (timestamptz).Todos leem o instante em que a transacao comecou, e nao o instante em que a linha executa. Dentro de uma mesma transacao o
CURRENT_DATEnao vai "pular" a meia-noite entre consultas — isso e comportamento do padrao, nao um bug.Hoje no WHERE e em relatorios
O uso mais comum e selecionar as linhas de hoje. Voce pode comparar
created_at(umtimestamptz) direto comCURRENT_DATE, mas atencao aos limites: prefira um intervalo semiaberto[hoje, amanha).SELECT id, email, created_at FROM users WHERE created_at >= CURRENT_DATE AND created_at < CURRENT_DATE + INTERVAL '1 day';Pedidos dos ultimos 30 dias — a aritmetica sobre
CURRENT_DATEfica limpa, sem conversoes:SELECT id, user_id, amount, status FROM orders WHERE status = 'paid' AND created_at >= CURRENT_DATE - INTERVAL '30 days';CURRENT_DATE como DEFAULT
CURRENT_DATEe um valor padrao perfeito para uma coluna de data: ele e avaliado a cada insercao.CREATE TABLE signups ( user_id bigint PRIMARY KEY, signed_on date NOT NULL DEFAULT CURRENT_DATE ); INSERT INTO signups (user_id) VALUES (101); -- signed_on = today, filled automaticallyPara campos de "criado em" voce geralmente prefere
CURRENT_TIMESTAMP(ounow()), para guardar tambem a hora e o fuso:ALTER TABLE orders ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP;A diferenca chave:
CURRENT_DATEguarda apenas o dia e descarta a hora, enquantoCURRENT_TIMESTAMPpreserva o instante completo. Escolha o tipo da coluna com criterio —datecontratimestamptz.CURRENT_TIME contra um timestamp completo
CURRENT_TIMEe a hora do dia, sem data associada. O tipotimetzcarrega um deslocamento de fuso, mas nao uma data, e e ai que ele fica propenso a erro.SELECT CURRENT_TIME AS clock, -- 18:42:07.512+02 (timetz) CURRENT_TIMESTAMP AS full_moment, -- 2026-06-17 18:42:07.512+02 CURRENT_TIMESTAMP::time AS local_clock; -- 18:42:07.512 (plain time)timetzde outro cruzando uma fronteira de fuso — sem data, atravessar a meia-noite e ambiguo.CURRENT_TIMESTAMP::time(umtimesem fuso), e naoCURRENT_TIME.CURRENT_TIMESTAMP, e nao um parCURRENT_DATE+CURRENT_TIME.Exemplo: conte um pedido como diurno se ele foi criado entre 09:00 e 18:00 no horario local.
SELECT id, amount FROM orders WHERE created_at::time BETWEEN TIME '09:00' AND TIME '18:00';Diferencas entre bancos
CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMPnao levam parenteses;now()e sinonimo deCURRENT_TIMESTAMP.CURRENT_DATEeCURDATE()sao equivalentes, assim comoCURRENT_TIMEeCURTIME(). O tipoTIMEdo MySQL nao guarda fuso, entao aqui nao existetimetz.today()enow();CURRENT_TIMESTAMPe aceito como alias, mas nao ha um tipo proprio de "hora do dia com fuso".Se o codigo precisa ser portavel, fique com
CURRENT_DATE/CURRENT_TIMESTAMPsem parenteses — a forma mais compreendida — e nao dependa da semantica detimetzfora do PostgreSQL.