feat(web/udt-011): dateFormat.ts utility (formatInstant, formatCivilDate, todayArgentina, etc.)
This commit is contained in:
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