En SQL, CURRENT_DATE y CURRENT_TIME no son funciones sino valores especiales: devuelven la fecha de hoy y la hora actual del dia sin un solo parentesis. Parece una nimiedad, pero precisamente la ausencia de () y el tipo del resultado son lo que mas confunde.
Valores especiales, sin parentesis
El estandar SQL define un punado de "registros especiales": CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP, LOCALTIME, LOCALTIMESTAMP. Se escriben como palabra clave desnuda — nunca CURRENT_DATE().
SELECT CURRENT_DATE, CURRENT_TIME;
CURRENT_DATE produce un date — solo ano, mes y dia.
CURRENT_TIME produce time with time zone (timetz en corto) — una hora del dia mas un desfase de zona.
CURRENT_TIMESTAMP combina ambos: fecha y hora completas con zona (timestamptz).
Todos leen el instante en que empezo la transaccion, no el instante en que se ejecuta la fila. Dentro de una misma transaccion CURRENT_DATE no "saltara" la medianoche entre consultas — eso es comportamiento del estandar, no un fallo.
El uso mas habitual es seleccionar las filas de hoy. Puedes comparar created_at (un timestamptz) directamente con CURRENT_DATE, pero cuidado con los limites: mejor un rango semiabierto [hoy, manana).
SELECT id, email, created_at
FROM users
WHERE created_at >= CURRENT_DATE
AND created_at < CURRENT_DATE + INTERVAL '1 day';
Pedidos de los ultimos 30 dias — la aritmetica sobre CURRENT_DATE se lee limpia, sin conversiones:
SELECT id, user_id, amount, status
FROM orders
WHERE status = 'paid'
AND created_at >= CURRENT_DATE - INTERVAL '30 days';
Cuidado: no escribas DATE(created_at) = CURRENT_DATE sobre un conjunto grande. Envolver la columna en DATE(...) anula cualquier indice sobre created_at — el planificador no puede usarlo y cae en un seq scan. El rango >= ... AND < ... mantiene el indice en juego.
CURRENT_DATE como DEFAULT
CURRENT_DATE es un valor por defecto perfecto para una columna de fecha: se evalua en cada insercion.
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 "creado el" sueles recurrir mejor a CURRENT_TIMESTAMP (o now()), para conservar tambien la hora y la zona:
ALTER TABLE orders
ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP;
La diferencia clave: CURRENT_DATE guarda solo el dia y descarta la hora, mientras que CURRENT_TIMESTAMP conserva el instante completo. Elige el tipo de columna a conciencia — date frente a timestamptz.
CURRENT_TIME frente a un timestamp completo
CURRENT_TIME es la hora del dia, sin fecha asociada. El tipo timetz lleva un desfase de zona pero no una fecha, y ahi es justo donde se vuelve propenso a errores.
SELECT CURRENT_TIME AS clock,
CURRENT_TIMESTAMP AS full_moment,
CURRENT_TIMESTAMP::time AS local_clock;
- No puedes restar de forma fiable un
timetz de otro cruzando una frontera de zona — sin fecha, pasar la medianoche es ambiguo.
- Para la logica de "horario laboral" sueles tomar
CURRENT_TIMESTAMP::time (un time sin zona), no CURRENT_TIME.
- Cuando necesitas fecha y hora, la respuesta correcta casi siempre es
CURRENT_TIMESTAMP, no un par CURRENT_DATE + CURRENT_TIME.
Ejemplo: cuenta un pedido como diurno si se creo entre las 09:00 y las 18:00 hora local.
SELECT id, amount
FROM orders
WHERE created_at::time BETWEEN TIME '09:00' AND TIME '18:00';
Diferencias entre motores
- PostgreSQL: totalmente estandar.
CURRENT_DATE, CURRENT_TIME, CURRENT_TIMESTAMP no llevan parentesis; now() es sinonimo de CURRENT_TIMESTAMP.
- MySQL:
CURRENT_DATE y CURDATE() son equivalentes, igual que CURRENT_TIME y CURTIME(). El tipo TIME de MySQL no guarda zona, asi que aqui no existe timetz.
- ClickHouse: ofrece
today() y now(); CURRENT_TIMESTAMP se admite como alias, pero no hay un tipo independiente de "hora del dia con zona".
Si el codigo debe ser portable, quedate con CURRENT_DATE/CURRENT_TIMESTAMP sin parentesis — la forma mas entendida — y no te apoyes en la semantica de timetz fuera de PostgreSQL.
En SQL,
CURRENT_DATEyCURRENT_TIMEno son funciones sino valores especiales: devuelven la fecha de hoy y la hora actual del dia sin un solo parentesis. Parece una nimiedad, pero precisamente la ausencia de()y el tipo del resultado son lo que mas confunde.Valores especiales, sin parentesis
El estandar SQL define un punado de "registros especiales":
CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMP,LOCALTIME,LOCALTIMESTAMP. Se escriben como palabra clave desnuda — nuncaCURRENT_DATE().SELECT CURRENT_DATE, CURRENT_TIME; -- 2026-06-17 | 18:42:07.512+02CURRENT_DATEproduce undate— solo ano, mes y dia.CURRENT_TIMEproducetime with time zone(timetzen corto) — una hora del dia mas un desfase de zona.CURRENT_TIMESTAMPcombina ambos: fecha y hora completas con zona (timestamptz).Todos leen el instante en que empezo la transaccion, no el instante en que se ejecuta la fila. Dentro de una misma transaccion
CURRENT_DATEno "saltara" la medianoche entre consultas — eso es comportamiento del estandar, no un fallo.Hoy en WHERE y en informes
El uso mas habitual es seleccionar las filas de hoy. Puedes comparar
created_at(untimestamptz) directamente conCURRENT_DATE, pero cuidado con los limites: mejor un rango semiabierto[hoy, manana).SELECT id, email, created_at FROM users WHERE created_at >= CURRENT_DATE AND created_at < CURRENT_DATE + INTERVAL '1 day';Pedidos de los ultimos 30 dias — la aritmetica sobre
CURRENT_DATEse lee limpia, sin conversiones:SELECT id, user_id, amount, status FROM orders WHERE status = 'paid' AND created_at >= CURRENT_DATE - INTERVAL '30 days';CURRENT_DATE como DEFAULT
CURRENT_DATEes un valor por defecto perfecto para una columna de fecha: se evalua en cada insercion.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 "creado el" sueles recurrir mejor a
CURRENT_TIMESTAMP(onow()), para conservar tambien la hora y la zona:ALTER TABLE orders ALTER COLUMN created_at SET DEFAULT CURRENT_TIMESTAMP;La diferencia clave:
CURRENT_DATEguarda solo el dia y descarta la hora, mientras queCURRENT_TIMESTAMPconserva el instante completo. Elige el tipo de columna a conciencia —datefrente atimestamptz.CURRENT_TIME frente a un timestamp completo
CURRENT_TIMEes la hora del dia, sin fecha asociada. El tipotimetzlleva un desfase de zona pero no una fecha, y ahi es justo donde se vuelve propenso a errores.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 otro cruzando una frontera de zona — sin fecha, pasar la medianoche es ambiguo.CURRENT_TIMESTAMP::time(untimesin zona), noCURRENT_TIME.CURRENT_TIMESTAMP, no un parCURRENT_DATE+CURRENT_TIME.Ejemplo: cuenta un pedido como diurno si se creo entre las 09:00 y las 18:00 hora local.
SELECT id, amount FROM orders WHERE created_at::time BETWEEN TIME '09:00' AND TIME '18:00';Diferencias entre motores
CURRENT_DATE,CURRENT_TIME,CURRENT_TIMESTAMPno llevan parentesis;now()es sinonimo deCURRENT_TIMESTAMP.CURRENT_DATEyCURDATE()son equivalentes, igual queCURRENT_TIMEyCURTIME(). El tipoTIMEde MySQL no guarda zona, asi que aqui no existetimetz.today()ynow();CURRENT_TIMESTAMPse admite como alias, pero no hay un tipo independiente de "hora del dia con zona".Si el codigo debe ser portable, quedate con
CURRENT_DATE/CURRENT_TIMESTAMPsin parentesis — la forma mas entendida — y no te apoyes en la semantica detimetzfuera de PostgreSQL.