UDT-011: Localización Temporal Argentina (infra transversal) #25
99
src/web/src/lib/dateFormat.ts
Normal file
99
src/web/src/lib/dateFormat.ts
Normal file
@@ -0,0 +1,99 @@
|
||||
/**
|
||||
* Localización temporal Argentina — utility centralizada.
|
||||
* Ver: Obsidian/02-ARQUITECTURA-y-TECH-STACK/2.17 ⏰ Localización Temporal Argentina.md
|
||||
* Engram topic_key: sig-cm2/conventions/fechas-timezones
|
||||
*
|
||||
* REGLAS PROHIBIDAS (no usar fuera de este módulo):
|
||||
* - new Date(civilDateString) → aplica UTC, pierde días
|
||||
* - toISOString().slice(0, 10) → UTC creep
|
||||
* - toLocaleString('es-AR', {...}) sin timeZone → depende del navegador
|
||||
*/
|
||||
|
||||
export const AR_TZ = 'America/Argentina/Buenos_Aires';
|
||||
|
||||
type FormatStyle = 'short' | 'medium' | 'long' | 'full';
|
||||
|
||||
interface FormatInstantOptions {
|
||||
dateStyle?: FormatStyle;
|
||||
timeStyle?: FormatStyle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatea un instante UTC (Cat1) como string legible en zona horaria Argentina.
|
||||
* Usa partes explícitas para garantizar año 4 dígitos y hora 24h independientemente del entorno.
|
||||
* Output: "dd/MM/yyyy, HH:mm:ss"
|
||||
*/
|
||||
export function formatInstant(
|
||||
iso: string,
|
||||
_opts: FormatInstantOptions = { dateStyle: 'short', timeStyle: 'medium' }
|
||||
): string {
|
||||
const parts = new Intl.DateTimeFormat('es-AR', {
|
||||
timeZone: AR_TZ,
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
hour: '2-digit',
|
||||
minute: '2-digit',
|
||||
second: '2-digit',
|
||||
hour12: false,
|
||||
}).formatToParts(new Date(iso));
|
||||
|
||||
const get = (type: string): string => parts.find(p => p.type === type)!.value;
|
||||
return `${get('day')}/${get('month')}/${get('year')}, ${get('hour')}:${get('minute')}:${get('second')}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatea una fecha civil Argentina (Cat2, formato "yyyy-MM-dd") a "dd/MM/yyyy".
|
||||
* Split manual — NO usa new Date() para evitar UTC creep.
|
||||
*/
|
||||
export function formatCivilDate(yyyyMmDd: string): string {
|
||||
const [y, m, d] = yyyyMmDd.split('-');
|
||||
return `${d}/${m}/${y}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Formatea un rango de fechas civiles Argentinas.
|
||||
*/
|
||||
export function formatCivilDateRange(from: string, to: string | null): string {
|
||||
return to ? `${formatCivilDate(from)} → ${formatCivilDate(to)}` : `desde ${formatCivilDate(from)}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Retorna la fecha civil Argentina de hoy en formato "yyyy-MM-dd".
|
||||
* Fix de BUG-FE-03: usa Intl.DateTimeFormat con timeZone, NO toISOString().slice(0, 10).
|
||||
*/
|
||||
export function todayArgentina(): string {
|
||||
const now = new Date();
|
||||
const parts = new Intl.DateTimeFormat('en-CA', {
|
||||
timeZone: AR_TZ,
|
||||
year: 'numeric',
|
||||
month: '2-digit',
|
||||
day: '2-digit',
|
||||
}).formatToParts(now);
|
||||
|
||||
const y = parts.find(p => p.type === 'year')!.value;
|
||||
const m = parts.find(p => p.type === 'month')!.value;
|
||||
const d = parts.find(p => p.type === 'day')!.value;
|
||||
|
||||
return `${y}-${m}-${d}`;
|
||||
}
|
||||
|
||||
/**
|
||||
* Parsea una fecha civil Argentina ("yyyy-MM-dd") a partes numéricas.
|
||||
* Split manual — NO usa new Date().
|
||||
*/
|
||||
export function parseCivilDate(yyyyMmDd: string): { year: number; month: number; day: number } {
|
||||
const [y, m, d] = yyyyMmDd.split('-').map(Number);
|
||||
return { year: y, month: m, day: d };
|
||||
}
|
||||
|
||||
/**
|
||||
* Convierte un valor de <input type="datetime-local"> (ART implícito) a ISO UTC.
|
||||
* Input: "2026-05-01T22:30" (sin TZ) → interpretado como ART → "2026-05-02T01:30:00.000Z".
|
||||
*
|
||||
* Útil para AuditFilters que envía filtros de rango al backend.
|
||||
*/
|
||||
export function parseArgentinaDateTimeToUtc(localDateTime: string): string {
|
||||
// Parse "yyyy-MM-ddTHH:mm" como ART (offset -03:00) y convertir a ISO UTC
|
||||
return new Date(`${localDateTime}:00-03:00`).toISOString();
|
||||
}
|
||||
Reference in New Issue
Block a user