Instruction file imported from rabil007/HRM (
.cursor/rules/date-timezone.mdc). Copyright stays with the author.
Date format and timezone
Platform Default Date Format and the company → platform timezone hierarchy must stay authoritative for display. Do not invent new formatters or hardcode d-m-Y / d M Y / toLocaleDateString for calendar dates.
Full guide: docs/architecture/date-and-timezone.md
Frontend (React)
- Date-only display:
useFormatDate()/useDateFormat()from@/lib/format-date. Never call bareformatDisplayDate(value)without a format in components. - Pure helpers:
formatDisplayDate(value, format?)stays pure (nousePage). Pass format from the hook, or rely on the layout-synced module cache only when a non-React.tsmodule cannot accept a format argument. - Company “today” for form defaults:
nowInCompanyDate(useCompanyTimezone()). Nevernew Date().toISOString().slice(0, 10)/.split('T')[0]for operational defaults (attendance, recruitment, payroll payment date). - Calendar-day arithmetic:
previousIsoCalendarDay/addIsoCalendarDaysfrom@/lib/calendar-date. Nevernew Date(local).toISOString().slice(0, 10)aftersetDate. - Absolute ISO datetimes on org screens:
useCompanyTimezone()+formatDisplayDateTimeInTimezone/formatDisplayDateTime12hInTimezone(date portion uses platform format). Do not use browser-localformatDisplayDateTimefor company data. - Date-only label from an absolute timestamp:
formatDisplayDate(toCompanyDateLocal(iso, useCompanyTimezone()), useDateFormat())— do not feed UTC ISO intoformatDisplayDatedirectly. - Crew wall-clock / movement timestamps: keep existing company-timezone helpers (
toCompanyDateTimeLocal, movement parsing). Do not double-convert. - Month headers like “October 2026” may stay locale month-year chrome; full calendar dates must use platform format.
- Storage /
<input type="date">/ ISO props: keepY-m-d. PreferAppDateFieldwhen the screen already uses it; do not mass-replace native date inputs unless the task asks.
Backend (PHP)
- Human dates for Inertia
*_formattedprops, emails, PDFs, report-only exports:PlatformDateFormatter::make()->date()/dateTime(). - Re-importable export date columns (employees, sea services, trainings, contracts): keep ISO
Y-m-dso export→import cannot corrupt ambiguous05/06/2026values. - No new hardcoded
->format('d-m-Y')or->format('d M Y')for display. - Storage, imports, APIs, validation: keep ISO
Y-m-d/ Carbon; do not parse platform display formats as input. - Inertia / JSON calendar-date props: always
?->toDateString()(orformat('Y-m-d')) for Eloquentdatecasts. Never pass a Carbon date object into an Inertia payload — withAPP_TIMEZONE=Asia/Dubai, JSON becomes the previous UTC day andformatDisplayDateshows the wrong calendar date. - Business “today”, payroll periods, leave year, crew movement clocks:
CompanyTimezone(company first, platform fallback). Never force platform timezone onto companies that already havecompanies.timezone. - Absolute ISO expiry/datetimes on UI: convert to company-local calendar day (
toCompanyDateLocal/ company TZ) before platform date formatting; do not take the UTCYYYY-MM-DDprefix.
Anti-patterns
toLocaleDateString()for operational calendar dates- Bare
formatDisplayDate(iso)in React components (skips settings unless cache was synced) - Browser-local datetime for attendance/leave/activity ISO instants
- UTC “today” via
toISOString().slice(0, 10)for company form defaults - Passing Eloquent
datecasts into Inertia withouttoDateString()(including nested$model->relation?->start_date) - Rewiring crew wall-clock, payroll boundaries, or leave rollover to “browser local” or “platform timezone only”
