/** Bantu perbandingan tanggal trip (hari kalender UTC). */ /** Parse `YYYY-MM-DD` sebagai awal hari UTC (hindari ambiguitas `new Date(string)` di berbagai TZ). */ export function utcDayStartFromYmd(ymd: string): Date { const parts = ymd.split("-").map(Number); const y = parts[0]; const m = parts[1]; const d = parts[2]; if (!y || !m || !d) { return utcStartOfDay(new Date()); } return new Date(Date.UTC(y, m - 1, d, 0, 0, 0, 0)); } /** Akhir hari UTC untuk `YYYY-MM-DD`. */ export function utcDayEndFromYmd(ymd: string): Date { const parts = ymd.split("-").map(Number); const y = parts[0]; const m = parts[1]; const d = parts[2]; if (!y || !m || !d) { return utcStartOfDay(new Date()); } return new Date(Date.UTC(y, m - 1, d, 23, 59, 59, 999)); } export function maxUtcDate(a: Date, b: Date): Date { return a.getTime() >= b.getTime() ? a : b; } /** * Tanggal dari DatePicker = hari kalender **lokal** browser. * Encode `YYYY-MM-DD` dengan `getFullYear/getMonth/getDate` (bukan `toISOString()`) * supaya tidak bergeser ke hari UTC lain (mis. WIB malam → UTC hari sebelumnya). */ export function formatLocalCalendarYmd(d: Date): string { const y = d.getFullYear(); const m = String(d.getMonth() + 1).padStart(2, "0"); const day = String(d.getDate()).padStart(2, "0"); return `${y}-${m}-${day}`; } /** * Simpan `trip.date` / `trip.endDate`: string `YYYY-MM-DD` diartikan sebagai * **hari kalender UTC** yang sama (selaras dengan filter Open Trip). */ export function tripStoredInstantFromYmd(ymd: string): Date { return utcDayStartFromYmd(ymd.trim().slice(0, 10)); } export function utcStartOfDay(d: Date): Date { return new Date( Date.UTC(d.getUTCFullYear(), d.getUTCMonth(), d.getUTCDate()) ); } /** true jika hari berangkat trip sudah lewat dibanding hari ini (UTC). */ export function isTripDepartureDayPast(departureDate: Date): boolean { const today = utcStartOfDay(new Date()); const dep = utcStartOfDay(departureDate); return dep.getTime() < today.getTime(); } /** * true jika sudah lewat akhir hari terakhir trip (tanggal pulang atau tanggal berangkat). * Dipakai untuk membuka fitur ulasan. */ export function isPastTripLastDayForReview( tripDate: Date, tripEndDate: Date | null ): boolean { const last = tripEndDate ?? tripDate; const endMs = Date.UTC( last.getUTCFullYear(), last.getUTCMonth(), last.getUTCDate(), 23, 59, 59, 999 ); return Date.now() > endMs; } /** * Tampilkan tanggal trip untuk UI: pakai **kalender UTC** (sama dengan * `tripStoredInstantFromYmd` & filter Open Trip), supaya tidak bergeser * karena timezone runtime (server vs browser). */ export function formatTripCalendarDateLong( d: Date | string, locale = "id-ID" ): string { const date = typeof d === "string" ? new Date(d) : d; return new Intl.DateTimeFormat(locale, { dateStyle: "long", timeZone: "UTC", }).format(date); } export function formatTripCalendarDateRangeLong( start: Date | string, end?: Date | string | null, locale = "id-ID" ): string { const startStr = formatTripCalendarDateLong(start, locale); if (!end) return startStr; const endStr = formatTripCalendarDateLong(end, locale); return `${startStr} — ${endStr}`; }