Contracts — Módulo temporal compartido (lib/ventanas.ts)

Sin endpoints ni tablas (006 FR-011). Estos contratos son firmas TypeScript puras que las specs consumidoras referencian por nombre (006 FR-010). Zona Europe/Madrid, precisión de minuto, textos es-ES. Implementación apoyada en lib/tiempo.ts (ZONA_HORARIA, truncarAMinuto, formatear*, aMinutos) e lib/instantes.ts (instanteLocal).

/** Captura única por operación (FR-001/FR-002). Reloj del servidor, truncado a minuto. */
export function capturarAhora(fecha?: Date): Date;

/** FR-004 RN2. true ⇔ inicio estrictamente anterior a ahora. Igual ⇒ false. */
export function esPasado(inicio: Date, ahora: Date): boolean;

/** FR-005. true ⇔ inicio − ahora ≥ 24 h (borde incluido, sin tope). */
export function cancelacionEnPlazo(inicio: Date, ahora: Date): boolean;

/** FR-006. true ⇔ 24 h ≤ inicio − ejecucion ≤ 48 h (ambos incluidos). */
export function enVentanaRecordatorios(inicio: Date, ejecucion: Date): boolean;

export type SemanaEtiquetada = { n: 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8; inicio: Date; fin: Date; etiqueta: string };
// etiqueta = `S-N: DD/MM/AAAA–DD/MM/AAAA` derivada de la ventana (FR-009, Q4/A).

/**
 * FR-007. Semana en curso = semana natural lunes–domingo
 * que contiene `hoy` en Europe/Madrid, recalculada en cada llamada; devuelve las 8
 * anteriores cerradas S-8 (más antigua) … S-1 (más reciente). La en curso nunca entra.
 * `diasLaborables` no altera los bordes semanales (solo parametriza FR-008).
 */
export function semanasAnalitica(hoy: Date, diasLaborables?: number[]): SemanaEtiquetada[];

/** FR-008. Denominador con la configuración vigente; nunca la constante 26.400 salvo defecto. */
export function denominadorOcupacion(
  jornadaInicio: string, // "HH:MM"
  jornadaFin: string,    // "HH:MM"
  diasLaborables?: number[],
  semanas?: number,      // defecto 8
): { minutos: number; etiquetaJornada: string }; // e.g. { minutos: 26400, etiquetaJornada: "09:00–20:00, Lun–Vie" }

/** FR-009. Etiqueta de tramo Q4/A. Delega en formatearTramo existente. */
export function etiquetarTramo(inicio: Date, fin: Date): string; // "DD/MM/AAAA, HH:MM–HH:MM"

Reglas de uso (vinculantes para consumidoras)

  1. Cada operación captura ahora = capturarAhora() una vez y lo propaga; prohibido new Date(), now() o CURRENT_TIMESTAMP dentro de reglas de negocio, salvo dentro de capturarAhora() (FR-001/FR-010).
  2. Toda entrada se trunca a minuto antes de comparar (FR-002).
  3. Ninguna consumidora redefine bordes ni ventanas; cita 006 FR-00X por nombre (FR-010). Una definición temporal fuera de lib/(tiempo|ventanas).ts es defecto.
  4. La BD recibe el «ahora» como parámetro (Prisma); RN1-exclusión sigue en BD pero con valores ya truncados desde app.
  5. diasLaborables: días ISO 1–7, defecto [1,2,3,4,5] (enmienda S-06 a Clinica de la 001).

Trazabilidad

Firma Spec Tests (quickstart)
capturarAhora FR-001/FR-002, US1 QS-1, QS-2
esPasado FR-004 QS-2
cancelacionEnPlazo FR-005 QS-3
enVentanaRecordatorios FR-006 QS-4
semanasAnalitica FR-007 QS-5
denominadorOcupacion FR-008 QS-6
etiquetarTramo / etiquetas S-N FR-003/FR-009 QS-5, QS-6