Use Cases (Actualizado: 2/6/2026)

Fechas y horas con Claude Code: guía práctica

Guía práctica para manejar zonas horarias, DST, Intl, pruebas y almacenamiento de fechas con Claude Code.

Fechas y horas con Claude Code: guía práctica

Por qué las fechas necesitan algo más que un prompt

Los errores de fecha y hora suelen pasar desapercibidos en revisión, pero en producción pueden romper reservas, facturación y notificaciones. Una pantalla puede verse correcta en España o Latinoamérica y fallar al llegar el cambio de horario de verano en Estados Unidos, el cierre de mes en Europa, un offset de 30 minutos en India o una diferencia entre la zona horaria del servidor y la del navegador. Si solo pides a Claude Code “implementa el manejo de fechas”, probablemente obtendrás formato bonito, pero no una política de almacenamiento, contratos de API ni pruebas de límites.

La idea es usar Claude Code como compañero para aclarar la especificación temporal, no solo como generador de código. Esta guía cubre aplicaciones web con reservas, facturación, avisos, SLA de soporte y paneles administrativos que cruzan zonas horarias. Para diseñar la suite completa de pruebas, revisa estrategias de testing con Claude Code. Para cambios de esquema, combina esta guía con migraciones de base de datos.

Antes de implementar, consulta fuentes oficiales. Para formato de salida usa MDN Intl.DateTimeFormat. Para el estado y los conceptos de Temporal, revisa TC39 Temporal proposal y MDN Temporal. Para PostgreSQL, la referencia es PostgreSQL Date/Time Types. Para ejecutar verificaciones automáticas después de ediciones, usa Claude Code hooks.

Fija vocabulario y política de almacenamiento

Antes de pedir código, fija las palabras del equipo. Si no, la revisión acaba en frases vagas como “guardamos UTC, entonces está bien” o “el producto es local, así que basta con la zona del país”.

TérminoSignificado simpleRegla práctica
instantUn momento único en el mundo, como 2026-06-02T00:00:00ZGuardar como timestamp basado en UTC
local dateFecha de calendario para una persona o negocioMantener como YYYY-MM-DD, separada de la hora
wall clock timeHora que ve una persona en el reloj, como una reunión a las 09:00Guardar junto con un ID IANA de zona horaria
IANA timezoneNombre regional como Europe/Madrid o America/New_YorkNo reemplazar por un offset fijo
DSTHorario de verano; algunos días tienen 23 o 25 horasProbar los días de transición

En proyectos reales, escribe estas reglas en AGENTS.md o CLAUDE.md antes de dejar que Claude Code toque la implementación.

  1. Los timestamps de eventos ya ocurridos deben ser cadenas ISO 8601 con Z o un offset explícito.
  2. Las reservas futuras deben guardar localDate, localTime y timeZone como campos separados.
  3. La UI debe pasar siempre locale y timeZone a Intl.DateTimeFormat; no debe depender del entorno.

La frontera mental debería verse así.

flowchart LR
  A["Client: local date/time input"] --> B["API contract: localDate + localTime + timeZone"]
  B --> C["Server: validate and convert when needed"]
  C --> D["Database: instant in timestamptz + original timeZone"]
  D --> E["UI: format with Intl.DateTimeFormat"]
  E --> A

Construye una base segura con Intl.DateTimeFormat

Para mostrar fechas, empieza con la API estándar Intl.DateTimeFormat. MDN la documenta como la API integrada para formatear fechas y horas según idioma, y permite indicar timeZone. A Claude Code debes darle dos restricciones: no usar toLocaleString() sin argumentos y no convertir con Date una cadena YYYY-MM-DD cuando representa una fecha local de calendario.

Este módulo no tiene dependencias. Guárdalo como src/lib/date-policy.ts y reutilízalo en UI, API y pruebas.

// src/lib/date-policy.ts
export const TIME_POLICY = {
  defaultLocale: 'es-ES',
  defaultTimeZone: 'Europe/Madrid',
} as const;

type FormatOptions = {
  locale?: string;
  timeZone?: string;
  includeWeekday?: boolean;
};

function toDate(input: string | Date): Date {
  const date = input instanceof Date ? input : new Date(input);

  if (!Number.isFinite(date.getTime())) {
    throw new Error(`Invalid date value: ${String(input)}`);
  }

  return date;
}

export function toUtcIso(input: string | Date): string {
  if (typeof input === 'string' && !/(Z|[+-]\d{2}:?\d{2})$/i.test(input)) {
    throw new Error('Timestamp must include Z or an explicit UTC offset.');
  }

  return toDate(input).toISOString();
}

function dateParts(date: Date, timeZone: string): Record<string, string> {
  const parts = new Intl.DateTimeFormat('en-US', {
    timeZone,
    year: 'numeric',
    month: '2-digit',
    day: '2-digit',
  }).formatToParts(date);

  return Object.fromEntries(
    parts
      .filter((part) => part.type !== 'literal')
      .map((part) => [part.type, part.value]),
  );
}

export function dayKeyInTimeZone(
  input: string | Date,
  timeZone = TIME_POLICY.defaultTimeZone,
): string {
  const parts = dateParts(toDate(input), timeZone);
  return `${parts.year}-${parts.month}-${parts.day}`;
}

export function formatInstant(
  input: string | Date,
  {
    locale = TIME_POLICY.defaultLocale,
    timeZone = TIME_POLICY.defaultTimeZone,
    includeWeekday = true,
  }: FormatOptions = {},
): string {
  return new Intl.DateTimeFormat(locale, {
    timeZone,
    weekday: includeWeekday ? 'short' : undefined,
    year: 'numeric',
    month: 'short',
    day: '2-digit',
    hour: '2-digit',
    minute: '2-digit',
    hourCycle: 'h23',
    timeZoneName: 'short',
  }).format(toDate(input));
}

El objetivo no es esconder toda la lógica temporal en utilidades. El objetivo es separar un instant, una clave de fecha local y una etiqueta de presentación. Cuando Claude Code proponga otro helper, pídele que explique a qué categoría pertenece el requisito.

Convierte las horas locales futuras de forma explícita

Intl.DateTimeFormat es excelente para salida, pero no para convertir 2026-11-01 01:30 America/New_York en un instant UTC. Para eso conviene elegir una biblioteca en el proyecto y revisar su documentación vigente. Luxon y sus API docs documentan APIs explícitas de zona horaria. date-fns es útil para operaciones funcionales de fecha, pero las necesidades de zona horaria deben comprobarse en sus docs actuales. Temporal ya está en Stage 4 en TC39, pero sigue siendo necesario verificar compatibilidad del runtime y política de polyfill.

En una reserva, el usuario introduce fecha y hora local; el servidor convierte eso al instant que se guarda.

// src/lib/schedule-time.ts
import { DateTime } from 'luxon';

type LocalScheduleInput = {
  localDate: string; // YYYY-MM-DD
  localTime: string; // HH:mm
  timeZone: string; // IANA time zone, for example "America/New_York"
};

export function scheduleToUtcIso(input: LocalScheduleInput): string {
  const rawLocal = `${input.localDate}T${input.localTime}`;
  const local = DateTime.fromISO(rawLocal, { zone: input.timeZone });

  if (!local.isValid) {
    throw new Error(local.invalidExplanation ?? `Invalid local time: ${rawLocal}`);
  }

  const roundTrip = local.setZone(input.timeZone).toFormat("yyyy-MM-dd'T'HH:mm");
  if (roundTrip !== rawLocal) {
    throw new Error(`Nonexistent local time in ${input.timeZone}: ${rawLocal}`);
  }

  const iso = local.toUTC().toISO({ suppressMilliseconds: true });
  if (!iso) {
    throw new Error(`Could not convert local time to UTC: ${rawLocal}`);
  }

  return iso;
}

Esta función no decide por sí sola qué hacer con la hora 01:00 repetida cuando termina el horario de verano. El producto debe elegir: offset temprano, offset tardío o confirmación del usuario. Pide a Claude Code pruebas para horas locales inexistentes, horas ambiguas, cierre de mes y años bisiestos.

Separa cliente, servidor y base de datos

El error más común es pasar un valor a Date sin decidir si debe interpretarlo el navegador o el servidor. Inputs del navegador, payloads de API, columnas de DB, emails y CSV son fronteras distintas.

Revisa el diseño con al menos estos tres casos.

  • Sistema de reservas: la entrada usa la zona del local, el almacenamiento usa UTC instant, la salida usa la zona del visitante o del local.
  • Corte de facturación: “último día del mes a las 23:59” es una regla de fecha local en la zona contractual del cliente; no se calcula sumando bloques de 24 horas.
  • SLA de soporte: días laborables, festivos y horarios de oficina varían por región, así que guarda tanto el instant límite como el contexto local legible.

La documentación oficial de PostgreSQL explica que timestamp with time zone convierte la entrada a UTC y no conserva la zona original. Por eso timestamptz no basta para saber que el usuario eligió America/New_York. Si el producto lo necesita, guarda esa zona aparte.

create table scheduled_events (
  id uuid primary key,
  title text not null,
  starts_at timestamptz not null,
  original_time_zone text not null check (original_time_zone <> ''),
  local_date date not null,
  local_time time not null,
  created_at timestamptz not null default now()
);

create index scheduled_events_starts_at_idx
  on scheduled_events (starts_at);

No asumas que insertar 2026-06-02T09:00:00+09:00 en una columna timestamp without time zone conserva el offset. PostgreSQL decide el tipo primero; si el valor es without time zone, la indicación de zona se ignora. Añade “el tipo de columna coincide con el contrato de API” a la revisión de Claude Code.

Escribe pruebas de DST con instants fijos

Las pruebas no deben depender de “hoy”, del reloj actual ni de la zona horaria de la laptop. Fija la entrada como instant UTC y afirma la fecha local o etiqueta esperada. Con Vitest, empieza por este archivo. Para patrones más amplios, revisa Claude Code Vitest avanzado.

// src/lib/date-policy.test.ts
import { describe, expect, it } from 'vitest';
import { dayKeyInTimeZone, formatInstant, toUtcIso } from './date-policy';

describe('date/time policy', () => {
  it('requires an explicit offset for API timestamps', () => {
    expect(() => toUtcIso('2026-06-02T09:00:00')).toThrow(/offset/);
    expect(toUtcIso('2026-06-02T09:00:00+09:00')).toBe('2026-06-02T00:00:00.000Z');
  });

  it('calculates a local day key across the UTC date boundary', () => {
    expect(dayKeyInTimeZone('2026-03-31T15:01:00Z', 'Asia/Tokyo')).toBe('2026-04-01');
    expect(dayKeyInTimeZone('2026-04-01T00:30:00Z', 'America/Los_Angeles')).toBe('2026-03-31');
  });

  it('formats a DST transition instant in the requested time zone', () => {
    const label = formatInstant('2026-03-08T07:30:00Z', {
      locale: 'en-US',
      timeZone: 'America/New_York',
    });

    expect(label).toMatch(/03:30|3:30/);
    expect(label).toMatch(/EDT|GMT-4/);
  });
});

La prueba permite EDT o GMT-4 porque los datos ICU pueden variar entre entornos. En cambio, la clave de fecha local y la conversión UTC son reglas de negocio y deben mantenerse estrictas.

Evita regresiones con prompts y hooks

Entrega las restricciones a Claude Code antes de implementar. Un prompt largo es aceptable si nombra las fronteras peligrosas.

Before implementing date/time changes, read date-policy.ts and the database schema.

Constraints:
- API timestamps for completed events must be ISO strings with Z or an explicit offset
- Future bookings must keep localDate, localTime, and timeZone separate
- Intl.DateTimeFormat must always receive locale and timeZone
- Do not use new Date('YYYY-MM-DD') for local calendar dates
- Add Vitest cases for DST start, DST end, month end, and leap day
- Explain the PostgreSQL timestamp/timestamptz difference in the review notes

Done when:
- npm test -- --run date-time passes
- Updated API response examples are added to README

En equipo, usa Claude Code hooks para ejecutar pruebas deterministas tras las ediciones. La documentación oficial muestra que hooks puede ejecutar comandos shell en eventos del ciclo de vida. Para el flujo completo, consulta la guía de hooks de Claude Code.

{
  "hooks": {
    "PostToolUse": [
      {
        "matcher": "Edit|Write",
        "hooks": [
          {
            "type": "command",
            "command": "npm test -- --run date-time"
          }
        ]
      }
    ]
  }
}

Elige biblioteca según el tipo de tiempo

No elijas una biblioteca de fechas solo por popularidad. Elígela por el tipo de tiempo que maneja tu producto. Intl.DateTimeFormat sirve para salida localizada, date-fns para utilidades de fecha enfocadas, Luxon para reservas con IANA timezone y Temporal cuando quieres tipos como Instant, PlainDate y ZonedDateTime. Aunque Temporal esté en Stage 4, confirma soporte de runtime y polyfill antes de pedir a Claude Code que reescriba código de producción.

OpciónMejor usoCuidado
IntlFormato localizado con zona horaria explícitaNo convierte hora local a instant
date-fnsMatemática de fechas, imports por función, utilidades pequeñasRevisa docs oficiales para timezone
LuxonReservas con IANA timezone, tiempo relativo, conversión ISOLas horas ambiguas de DST requieren regla de producto
TemporalSeparar Instant, PlainDate y ZonedDateTimeImportan compatibilidad y polyfill

Desde el punto de vista de contenido y monetización, una lista genérica de bibliotecas aporta poco. El lector necesita saber qué guardar, dónde convertir y qué probar. Si quieres insertar esta política en CLAUDE.md, hooks, PR review y migraciones de tu equipo, la página de formación y consultoría de Claude Code es el siguiente paso natural. Para trabajo individual, añade el prompt anterior a la chuleta gratuita y úsalo como guardarraíl repetible.

Nota de verificación práctica

Nota de Masa: la comprobación más útil fue fijar un instant UTC, formatearlo en Tokio, Nueva York y Los Ángeles, y afirmar la clave de fecha local al cruzar fronteras de día. El bug más fácil de reproducir fue tratar YYYY-MM-DD como fecha local del usuario, convertirlo a Date y verlo como el día anterior en otra zona. La política de este artículo mantiene las fechas locales como cadenas y convierte explícitamente solo los instants guardados y las etiquetas de presentación, lo que hace que los cambios de Claude Code sean mucho más fáciles de revisar.

#Claude Code #fechas #zonas horarias #Intl #testing
Gratis

PDF gratis: cheatsheet de Claude Code

Introduce tu email y descarga una hoja con comandos, hábitos de revisión y flujos seguros.

Cuidamos tus datos y no enviamos spam.

Masa

Sobre el autor

Masa

Ingeniero enfocado en workflows prácticos con Claude Code.