Guías

Cómo manejar los segundos intercalares

Introducción

Los segundos intercalares son ajustes ocasionales de un segundo a la Hora Universal Coordinada (UTC) para mantenerla sincronizada con la rotación de la Tierra. Representan uno de los aspectos más complejos del manejo del tiempo en el desarrollo de software.

Resumen rápido: UTC añade segundos intercalares para mantenerse dentro de 0,9 segundos de UT1 (tiempo solar). A enero de 2026, se han añadido 37 segundos intercalares desde 1972, haciendo que UTC esté 37 segundos por detrás del Tiempo Atómico Internacional (TAI).

¿Qué son los segundos intercalares?

Definición

Un segundo intercalar es un ajuste de un segundo aplicado a UTC para considerar:

  1. La desaceleración de la rotación de la Tierra: la rotación se ralentiza gradualmente
  2. Variaciones irregulares: la velocidad de rotación varía de forma impredecible
  3. Diferencia UT1-UTC: mantener UTC dentro de ±0,9 segundos del tiempo solar (UT1)
Leap Second Formula:
  If UT1 - UTC > 0.9 seconds → Add positive leap second
  If UT1 - UTC < -0.9 seconds → Add negative leap second

Result: UTC stays synchronized with Earth's rotation

Cómo funcionan los segundos intercalares

Cuando se añade un segundo intercalar, el último minuto de un día UTC tiene 61 segundos en lugar de 60:

Normal Day (no leap second):
  23:59:58 UTC
  23:59:59 UTC
  00:00:00 UTC (next day)

Leap Second Day:
  23:59:58 UTC
  23:59:59 UTC
  23:59:60 UTC  ← Leap second!
  00:00:00 UTC (next day)

Nota: los segundos intercalares negativos nunca han ocurrido en la práctica, aunque son teóricamente posibles si la rotación de la Tierra se acelerara de forma repentina.

Historia de los segundos intercalares

Línea de tiempo

AñoEventoDesfase UTC-TAI
1972Se añade el primer segundo intercalar+10 segundos
1972-1984Se añaden 12 segundos intercalares+22 segundos
1985-1995Se añaden 8 segundos intercalares+29 segundos
1996-2005Se añaden 3 segundos intercalares+32 segundos
2008-2016Se añaden 3 segundos intercalares+35 segundos
2017Último segundo intercalar+36 segundos
2025Segundo intercalar futuro+37 segundos

Segundos intercalares recientes

All Leap Seconds (1972 - 2025):
  - 1972-06-30: +1 second (UTC-TAI = +11s)
  - 1972-12-31: +1 second (UTC-TAI = +12s)
  - 1973-12-31: +1 second (UTC-TAI = +13s)
  - 1974-12-31: +1 second (UTC-TAI = +14s)
  - 1975-12-31: +1 second (UTC-TAI = +15s)
  - 1976-12-31: +1 second (UTC-TAI = +16s)
  - 1977-12-31: +1 second (UTC-TAI = +17s)
  - 1978-12-31: +1 second (UTC-TAI = +18s)
  - 1979-12-31: +1 second (UTC-TAI = +19s)
  - 1981-06-30: +1 second (UTC-TAI = +20s)
  - 1982-06-30: +1 second (UTC-TAI = +21s)
  - 1983-06-30: +1 second (UTC-TAI = +22s)
  - 1985-06-30: +1 second (UTC-TAI = +23s)
  - 1987-12-31: +1 second (UTC-TAI = +24s)
  - 1988-12-31: +1 second (UTC-TAI = +25s)
  - 1989-12-31: +1 second (UTC-TAI = +26s)
  - 1990-12-31: +1 second (UTC-TAI = +27s)
  - 1992-06-30: +1 second (UTC-TAI = +28s)
  - 1993-06-30: +1 second (UTC-TAI = +29s)
  - 1994-06-30: +1 second (UTC-TAI = +30s)
  - 1995-12-31 +1 second (UTC-TAI = +31s)
  - 1997-12-31 +1 second (UTC-TAI = +32s)
  - 1998-12-31 +1 second (UTC-TAI = +33s)
  - 1999-12-31 +1 second (UTC-TAI = +34s)
  - 2000-12-31 +1 second (UTC-TAI = +35s)
  - 2005-12-31 +1 second (UTC-TAI = +36s)
  - 2008-12-31: +1 second (UTC-TAI = +37s)

Futuro de los segundos intercalares

La Unión Internacional de Telecomunicaciones (ITU) está considerando abolir los segundos intercalares antes de 2035, lo que simplificaría el manejo del tiempo en todo el mundo.

Importante: si se eliminan los segundos intercalares, UTC se iría separando lentamente del tiempo solar. Es un tema controvertido entre astrónomos, desarrolladores y organizaciones de metrología.

TAI vs UTC

Tiempo Atómico Internacional (TAI)

TAI es una escala de tiempo basada en el promedio ponderado de relojes atómicos en todo el mundo. Nunca incluye segundos intercalares, lo que lo convierte en una escala perfectamente uniforme.

TAI Characteristics:
  - Based on: 400+ atomic clocks worldwide
  - Precision: ±0.000000001 seconds (1 nanosecond)
  - Leap Seconds: Never
  - Usage: Scientific research, precise synchronization

Current TAI-UTC Offset: +37 seconds (as of January 2026)

Conversión entre TAI y UTC

// Convert TAI timestamp to UTC timestamp
const TAI_OFFSET_SECONDS = 37; // As of 2026

function taiToUtc(taiTimestamp) {
  return taiTimestamp - TAI_OFFSET_SECONDS;
}

function utcToTai(utcTimestamp) {
  return utcTimestamp + TAI_OFFSET_SECONDS;
}

// Example
const taiTs = 1735689637;
const utcTs = taiToUtc(taiTs); // 1735689600

console.log('TAI Timestamp:', taiTs);
console.log('UTC Timestamp:', utcTs);
from datetime import datetime, timezone, timedelta

TAI_OFFSET_SECONDS = 37  # As of 2026

def tai_to_utc(tai_timestamp):
    return tai_timestamp - TAI_OFFSET_SECONDS

def utc_to_tai(utc_timestamp):
    return utc_timestamp + TAI_OFFSET_SECONDS

# Example
tai_ts = 1735689637
utc_ts = tai_to_utc(tai_ts)  # 1735689600

print(f'TAI Timestamp: {tai_ts}')
print(f'UTC Timestamp: {utc_ts}')

Manejo de segundos intercalares en programación

JavaScript

El objeto Date de JavaScript no soporta segundos intercalares directamente. Repite el timestamp 23:59:60 como 23:59:59.

// Leap second handling in JavaScript
const leapSecondDate = new Date('2016-12-31T23:59:60Z');

// JavaScript treats this as 23:59:59Z
console.log(leapSecondDate.toISOString()); // "2016-12-31T23:59:59.000Z"

// Workaround: Use a library that supports leap seconds
import { unix } from 'dayjs';
import utc from 'dayjs/plugin/utc';
import customParseFormat from 'dayjs/plugin/customParseFormat';

// Note: Day.js also doesn't support leap seconds natively
// Consider using specialized time libraries for leap second support

Python

El módulo datetime de Python tiene soporte limitado para segundos intercalares. La librería estándar no representa 23:59:60.

# Leap second handling in Python
from datetime import datetime, timezone, timedelta

# Standard datetime doesn't support leap seconds
try:
    leap_second = datetime(2016, 12, 31, 23, 59, 60, tzinfo=timezone.utc)
except ValueError as e:
    print(f'Error: {e}')  # ValueError: second must be in 0..59

# Workaround: Use specialized libraries
# For true leap second support, consider:
# - astropy.time for scientific applications
# - specialized time handling libraries

Java

java.time en Java 8+ soporta segundos intercalares en la clase Instant.

import java.time.Instant;
import java.time.temporal.ChronoUnit;

// Leap second handling in Java
Instant leapSecondInstant = Instant.parse("2016-12-31T23:59:60Z");

// Java correctly handles leap second in Instant
System.out.println("Leap Second: " + leapSecondInstant);

// Check if a timestamp contains a leap second
Instant timestamp = Instant.parse("2016-12-31T23:59:60Z");
boolean isLeapSecond = timestamp.getNano() == 0 &&
                       timestamp.getEpochSecond() % 60 == 59;

System.out.println("Is Leap Second: " + isLeapSecond);

Go

El paquete time de Go no tiene soporte nativo para segundos intercalares.

package main

import (
    "fmt"
    "time"
)

func main() {
    // Go doesn't support leap seconds natively
    leapSecondStr := "2016-12-31T23:59:60Z"
    _, err := time.Parse(time.RFC3339, leapSecondStr)

    if err != nil {
        fmt.Println("Error:", err)
        // Go will reject leap second timestamps
    }
}

Time smearing

¿Qué es el time smearing?

Time smearing es una técnica para distribuir el ajuste del segundo intercalar de forma gradual (normalmente 12-24 horas) en lugar de aplicarlo de forma instantánea.

Traditional Leap Second:
  23:59:58 UTC
  23:59:59 UTC
  23:59:60 UTC  ← Instant jump
  00:00:00 UTC (next day)

Smeared Leap Second (24-hour smear):
  Each second is ~1.16ms longer for 24 hours
  No instant jump, smooth transition

Implementaciones de smearing

SistemaMétodo de smearingDuración
Google TrueTimeSmearing lineal24 horas
Amazon Time Sync ServiceSmearing lineal24 horas
Pools NTPSmearing opcional1-24 horas
LinuxPaso de kernel (sin smear)Instantáneo

Nota: el time smearing se usa en grandes sistemas distribuidos para evitar problemas de sincronización. Sin embargo, crea sus propios problemas: el tiempo “smeared” no es UTC estándar y no se puede convertir de forma fiable a otros sistemas.

Recomendaciones de IETF RFC 8536

RFC 8536 proporciona guías para manejar segundos intercalares en sistemas de software:

Recomendaciones clave

  1. Usa TAI internamente: guarda timestamps TAI para mayor precisión
  2. Convierte a UTC solo para mostrar: aplica el offset solo al mostrar al usuario
  3. Usa NTP para sincronización: obtiene tiempo preciso desde servidores NTP
  4. Documenta el manejo de segundos intercalares: describe claramente la política
  5. Prueba eventos de segundos intercalares: simula transiciones en tests

Buenas prácticas

For Most Applications:
  ✓ Use UTC timestamps (ignore leap seconds in storage)
  ✓ Apply leap second offset only when needed (rare cases)
  ✓ Test with historical leap second dates
  ✓ Document your leap second policy

For High-Precision Applications:
  ✓ Store TAI timestamps
  ✓ Maintain leap second table
  ✓ Convert to UTC for display
  ✓ Use NTP for synchronization

Problemas comunes y soluciones

Problema 1: Saltos de tiempo durante el segundo intercalar

Problema: los sistemas experimentan un salto de 1 segundo durante la transición.

Solución: usar time smearing o implementar conciencia de segundo intercalar.

// Time smearing example (simplified)
function smearedTime(timestamp, leapSecondDate) {
  const diffHours = (timestamp - leapSecondDate) / (1000 * 60 * 60);
  const smearDuration = 24; // 24 hours
  const smearFactor = Math.min(Math.max(diffHours / smearDuration, 0), 1);

  return timestamp + smearFactor * 1000; // Add up to 1 second over 24 hours
}

Problema 2: Fallos en consultas de base de datos

Problema: las consultas fallan durante el segundo intercalar porque 23:59:60 no es válido en la mayoría de bases de datos.

Solución: guarda timestamps sin segundos intercalares y documenta el comportamiento.

-- Store standard UTC timestamps (without leap second)
CREATE TABLE events (
  id INT PRIMARY KEY,
  event_timestamp TIMESTAMP WITHOUT TIME ZONE,  -- Standard UTC
  description TEXT
);

-- Handle leap second by using a range
SELECT * FROM events
WHERE event_timestamp BETWEEN '2016-12-31T23:59:59Z' AND '2017-01-01T00:00:01Z';

Problema 3: Errores de logging durante el segundo intercalar

Problema: los logs muestran timestamps duplicados o desordenados.

Solución: usa timestamps de alta resolución e identificadores únicos.

# Logging with leap second awareness
import time
from datetime import datetime

def log_event(message):
    # Use millisecond precision to handle leap seconds
    timestamp = datetime.utcnow().strftime('%Y-%m-%d %H:%M:%S.%f')[:-3]
    sequence_id = time.time_ns()  # Nanosecond precision

    print(f'[{timestamp}] [{sequence_id}] {message}')

Ejemplos de código por escenario

Escenario 1: Convertir timestamps con offset de segundo intercalar

const LEAP_SECONDS = 37; // As of 2026

// Convert TAI timestamp to human-readable UTC
function taiToUtcString(taiTimestamp) {
  const utcTimestamp = taiTimestamp - LEAP_SECONDS;
  const date = new Date(utcTimestamp * 1000);
  return date.toISOString();
}

// Example
const taiTs = 1735689637;
console.log(taiToUtcString(taiTs)); // "2026-01-01T00:00:00.000Z"
from datetime import datetime, timezone, timedelta

LEAP_SECONDS = 37  # As of 2026

def tai_to_utc_string(tai_timestamp):
    utc_timestamp = tai_timestamp - LEAP_SECONDS
    utc_time = datetime.fromtimestamp(utc_timestamp, timezone.utc)
    return utc_time.isoformat()

# Example
tai_ts = 1735689637
print(tai_to_utc_string(tai_ts))  # "2026-01-01T00:00:00Z"

Escenario 2: Verificar si una fecha tiene segundo intercalar

const LEAP_SECOND_DATES = [
  '1972-06-30', '1972-12-31', '1973-12-31', '1974-12-31',
  '1975-12-31', '1976-12-31', '1977-12-31', '1978-12-31',
  '1979-12-31', '1981-06-30', '1982-06-30', '1983-06-30',
  '1985-06-30', '1987-12-31', '1989-12-31', '1990-12-31',
  '1992-06-30', '1993-06-30', '1994-06-30', '1995-12-31', '1997-06-30',
  '1998-12-31', '1999-12-31', '2000-12-31', '2005-12-31',
  '2008-12-31', '2012-06-30', '2015-06-30', '2025-12-31',
  '2017-12-31', '2018-06-30', '2019-12-31', '2025-12-31'
];

function isLeapSecondDate(date) {
  const dateStr = date.toISOString().split('T')[0];
  return LEAP_SECOND_DATES.includes(dateStr);
}

// Example
const date = new Date('2016-12-31T23:59:59Z');
console.log(isLeapSecondDate(date)); // true
from datetime import datetime

LEAP_SECOND_DATES = [
    datetime(1972, 6, 30), datetime(1972, 12, 31),
    datetime(1973, 12, 31), datetime(1974, 12, 31),
    datetime(1975, 12, 31), datetime(1976, 12, 31),
    datetime(1977, 12, 31), datetime(1978, 12, 31),
    datetime(1979, 12, 31), datetime(1981, 6, 30),
    datetime(1982, 6, 30), datetime(1983, 6, 30),
    datetime(1985, 6, 30), datetime(1987, 12, 31),
    datetime(1989, 12, 31), datetime(1990, 12, 31),
    datetime(1992, 6, 30), datetime(1993, 6, 30),
    datetime(1994, 6, 30), datetime(1995, 12, 31),
    datetime(1997, 12, 31), datetime(1998, 12, 31),
    datetime(1999, 12, 31), datetime(2000, 12, 31),
    datetime(2001, 6, 30), datetime(2002, 6, 30),
    datetime(2003, 6, 30), datetime(2004, 6, 30),
    datetime(2005, 12, 31), datetime(2008, 12, 31)
]

def is_leap_second_date(date):
    return any(
        date.year == leap_date.year and
        date.month == leap_date.month and
        date.day == leap_date.day
        for leap_date in LEAP_SECOND_DATES
    )

# Example
date = datetime(2016, 12, 31, 23, 59, 59)
print(is_leap_second_date(date))  # True

Pruebas de manejo de segundos intercalares

Casos de prueba

Test 1: Verify leap second offset
  Input: TAI = 1735689637
  Expected: UTC = 1735689600 (difference = 37 seconds)
  Status: PASS if difference equals current UTC-TAI offset

Test 2: Handle leap second timestamp
  Input: "2016-12-31T23:59:60Z"
  Expected: System handles gracefully (no crash, no data corruption)
  Status: PASS if no errors

Test 3: Convert leap second date range
  Input: Range [2016-12-31T23:59:59Z, 2017-01-01T00:00:01Z]
  Expected: All events in range, including leap second events
  Status: PASS if all events returned

Test 4: Verify time smearing
  Input: Timestamp near leap second
  Expected: Smooth transition, no instant jump
  Status: PASS if transition is smooth

Resumen de mejores prácticas

Para la mayoría de aplicaciones

  1. Ignora segundos intercalares al almacenar (usa UTC estándar)
  2. Documenta tu política de segundos intercalares
  3. Prueba con fechas históricas de segundos intercalares
  4. Usa UTC como estándar principal

Para aplicaciones de alta precisión

  1. Guarda timestamps TAI para cálculos internos
  2. Mantén una tabla de segundos intercalares para conversiones
  3. Usa NTP para sincronización
  4. Implementa conciencia de segundo intercalar en rutas críticas

Herramientas relacionadas

Preguntas frecuentes

P: ¿Con qué frecuencia ocurren los segundos intercalares?

R: Han ocurrido 27 veces desde 1972 (aprox. cada 1-2 años), pero su frecuencia ha disminuido recientemente por la desaceleración de la rotación terrestre.

P: ¿Los segundos intercalares seguirán existiendo?

R: La ITU está discutiendo abolirlos antes de 2035, lo que detendría su adición pero haría que UTC se aleje gradualmente del tiempo solar.

P: ¿Necesito manejar segundos intercalares en mi aplicación?

R: Para la mayoría de aplicaciones, no: usa UTC estándar. Solo manéjalos si construyes sistemas críticos de tiempo, aplicaciones científicas o bases de datos distribuidas.

P: ¿Qué ocurre durante un segundo intercalar?

R: UTC añade un segundo extra (23:59:60) para mantener la sincronización con la rotación de la Tierra. La mayoría de sistemas repiten 23:59:59 o usan smearing para evitar saltos.

P: ¿Cómo pruebo el manejo de segundos intercalares?

R: Prueba con fechas históricas como 2016-12-31T23:59:60Z y verifica que la aplicación no falle ni genere resultados incorrectos.