UTC_TIMESTAMP

La función UTC_TIMESTAMP devuelve la fecha y hora actuales en formato UTC (Tiempo Universal Coordinado), combinando en un solo valor lo que UTC_DATE y UTC_TIME ofrecen por separado. Es la función más recomendada para registrar marcas de tiempo en aplicaciones globales, ya que proporciona un punto de referencia universal e inequívoco para cada evento.

A diferencia de NOW(), que devuelve la fecha y hora según la zona horaria de la sesión, UTC_TIMESTAMP siempre refleja el momento exacto en la escala de tiempo universal. Esto significa que si dos servidores en continentes diferentes ejecutan UTC_TIMESTAMP en el mismo instante, ambos obtendrán el mismo valor, algo que no se puede garantizar con NOW().

Sintaxis

UTC_TIMESTAMP
UTC_TIMESTAMP([fsp])

El parámetro opcional fsp (fractional seconds precision) acepta valores de 0 a 6 y determina la cantidad de decimales en los segundos. Sin argumento, devuelve DATETIME sin fracciones. En contexto de cadena el formato es YYYY-MM-DD HH:MM:SS, y en contexto numérico es YYYYMMDDHHMMSS.

Comportamiento básico

La diferencia entre UTC_TIMESTAMP y NOW se hace evidente cuando la sesión utiliza una zona horaria distinta de UTC.

SET time_zone = 'America/Argentina/Buenos_Aires';
SELECT
    UTC_TIMESTAMP() AS utc,
    NOW() AS local_buenos_aires;
utclocal_buenos_aires
2026-02-14 22:30:152026-02-14 19:30:15

Buenos Aires está en UTC-3, por lo que la hora local es tres horas menor. Si cambias la zona horaria de la sesión, NOW() cambia pero UTC_TIMESTAMP() permanece constante.

SET time_zone = 'Asia/Tokyo';
SELECT
    UTC_TIMESTAMP() AS utc,
    NOW() AS local_tokyo;
utclocal_tokyo
2026-02-14 22:30:152026-02-15 07:30:15

Observa cómo en Tokio (UTC+9) ya es el día siguiente, mientras que UTC sigue en el 14 de febrero. Este ejemplo demuestra por qué almacenar siempre en UTC evita confusiones cuando los datos se consultan desde diferentes regiones.

Para obtener precisión de milisegundos, que es lo más habitual en aplicaciones modernas, se utiliza UTC_TIMESTAMP(3).

SELECT UTC_TIMESTAMP(3) AS utc_milisegundos;
utc_milisegundos
2026-02-14 22:30:15.847

Caso práctico: columnas created_at y updated_at

El uso más extendido de UTC_TIMESTAMP es como valor por defecto en las columnas de auditoría de las tablas. Esta práctica garantiza que cada registro tenga una marca de tiempo consistente.

CREATE TABLE productos (
    id INT AUTO_INCREMENT PRIMARY KEY,
    nombre VARCHAR(200) NOT NULL,
    precio DECIMAL(10,2) NOT NULL,
    stock INT DEFAULT 0,
    created_at DATETIME(3) DEFAULT (UTC_TIMESTAMP(3)),
    updated_at DATETIME(3) DEFAULT (UTC_TIMESTAMP(3)) ON UPDATE CURRENT_TIMESTAMP(3)
);
 
INSERT INTO productos (nombre, precio, stock)
VALUES
    ('Laptop ThinkPad X1', 1299.99, 45),
    ('Monitor LG 4K 27"', 449.99, 120),
    ('Teclado mecánico Keychron', 89.99, 300);
 
SELECT nombre, precio, created_at
FROM productos;
nombrepreciocreated_at
Laptop ThinkPad X11299.992026-02-14 22:30:15.847
Monitor LG 4K 27"449.992026-02-14 22:30:15.912
Teclado mecánico Keychron89.992026-02-14 22:30:15.934

Cada inserción queda marcada con el momento UTC exacto en que ocurrió, con precisión de milisegundos para distinguir inserciones en lote.

Caso práctico: auditoría de cambios de precio

Un sistema de comercio electrónico registra cada cambio de precio para análisis histórico y cumplimiento regulatorio. Todas las marcas de tiempo se almacenan en UTC.

CREATE TABLE historial_precios (
    id INT AUTO_INCREMENT PRIMARY KEY,
    producto_id INT NOT NULL,
    precio_anterior DECIMAL(10,2),
    precio_nuevo DECIMAL(10,2) NOT NULL,
    cambiado_por VARCHAR(100),
    cambiado_en DATETIME(3) DEFAULT (UTC_TIMESTAMP(3))
);
 
SELECT
    p.nombre,
    hp.precio_anterior,
    hp.precio_nuevo,
    ROUND(
        (hp.precio_nuevo - hp.precio_anterior) / hp.precio_anterior * 100, 1
    ) AS variacion_porcentaje,
    hp.cambiado_por,
    hp.cambiado_en
FROM historial_precios hp
JOIN productos p ON hp.producto_id = p.id
WHERE hp.cambiado_en >= UTC_TIMESTAMP() - INTERVAL 7 DAY
ORDER BY hp.cambiado_en DESC;
nombreprecio_anteriorprecio_nuevovariacion_porcentajecambiado_porcambiado_en
Laptop ThinkPad X11399.991299.99-7.1admin@tienda.com2026-02-14 15:20:33.412
Monitor LG 4K 27"399.99449.9912.5admin@tienda.com2026-02-12 09:45:12.738

Al filtrar con UTC_TIMESTAMP() - INTERVAL 7 DAY, obtienes exactamente los cambios de la última semana en tiempo universal, sin importar desde qué zona horaria se ejecute la consulta.

Caso práctico: comparar tiempos entre zonas horarias

Cuando tienes usuarios distribuidos globalmente y quieres mostrar cuánto tiempo ha pasado desde un evento, trabajar en UTC simplifica enormemente los cálculos.

SELECT
    u.nombre,
    u.pais,
    u.ultimo_acceso_utc,
    TIMESTAMPDIFF(MINUTE, u.ultimo_acceso_utc, UTC_TIMESTAMP()) AS minutos_inactivo,
    CASE
        WHEN TIMESTAMPDIFF(MINUTE, u.ultimo_acceso_utc, UTC_TIMESTAMP()) < 5 THEN 'En línea'
        WHEN TIMESTAMPDIFF(MINUTE, u.ultimo_acceso_utc, UTC_TIMESTAMP()) < 30 THEN 'Reciente'
        WHEN TIMESTAMPDIFF(HOUR, u.ultimo_acceso_utc, UTC_TIMESTAMP()) < 24 THEN 'Hoy'
        ELSE 'Inactivo'
    END AS estado
FROM usuarios u
WHERE u.activo = 1
ORDER BY u.ultimo_acceso_utc DESC
LIMIT 5;
nombrepaisultimo_acceso_utcminutos_inactivoestado
Yuki TanakaJapón2026-02-14 22:28:10.0002En línea
María RodríguezEspaña2026-02-14 22:15:45.00015Reciente
John SmithEE.UU.2026-02-14 18:30:00.000240Hoy
Ana LópezMéxico2026-02-13 14:20:00.0001930Inactivo
Hans MüllerAlemania2026-02-13 08:10:00.0002300Inactivo

Dado que tanto ultimo_acceso_utc como UTC_TIMESTAMP() están en la misma referencia temporal, la diferencia calculada por TIMESTAMPDIFF es siempre precisa, sin importar la zona horaria del usuario o del servidor.

Manejo de NULL

UTC_TIMESTAMP nunca devuelve NULL por sí misma. Sin embargo, al compararla con columnas que pueden ser nulas, el resultado de la expresión puede verse afectado.

SELECT
    nombre,
    fecha_vencimiento_utc,
    CASE
        WHEN fecha_vencimiento_utc IS NULL THEN 'Sin vencimiento'
        WHEN fecha_vencimiento_utc < UTC_TIMESTAMP() THEN 'Vencido'
        ELSE 'Vigente'
    END AS estado
FROM licencias;
nombrefecha_vencimiento_utcestado
Plan Básico2026-01-31 23:59:59.000Vencido
Plan Profesional2026-12-31 23:59:59.000Vigente
Plan EnterpriseNULLSin vencimiento

El CASE evalúa IS NULL antes de la comparación, evitando que una comparación con NULL produzca un resultado indeterminado.

Combinación con otras funciones

UTC_TIMESTAMP se integra naturalmente con funciones de extracción y aritmética de fechas.

SELECT
    UTC_TIMESTAMP() AS ahora_utc,
    DATE(UTC_TIMESTAMP()) AS solo_fecha,
    TIME(UTC_TIMESTAMP()) AS solo_hora,
    DATE_ADD(UTC_TIMESTAMP(), INTERVAL 48 HOUR) AS en_48_horas,
    UNIX_TIMESTAMP(UTC_TIMESTAMP()) AS epoch;
ahora_utcsolo_fechasolo_horaen_48_horasepoch
2026-02-14 22:30:152026-02-1422:30:152026-02-16 22:30:151771111815

También puedes combinarla con DATE_FORMAT para generar representaciones legibles y con CONVERT_TZ para mostrar al usuario la hora en su zona local.

SELECT
    UTC_TIMESTAMP() AS utc,
    CONVERT_TZ(UTC_TIMESTAMP(), '+00:00', 'Europe/Madrid') AS hora_madrid,
    CONVERT_TZ(UTC_TIMESTAMP(), '+00:00', 'America/Lima') AS hora_lima;
utchora_madridhora_lima
2026-02-14 22:30:152026-02-14 23:30:152026-02-14 17:30:15

Esta combinación de almacenar en UTC y convertir al mostrar es el patrón estándar en la industria para el manejo correcto de zonas horarias.

En el siguiente artículo veremos CONVERT_TZ para convertir entre zonas horarias.

Escrito por Eduardo Lázaro