From 7b72994d4a73427157442d7bcb0c0d9356fb4b12 Mon Sep 17 00:00:00 2001 From: muken Date: Mon, 29 Jun 2026 23:52:08 -0500 Subject: [PATCH] fix: use Ecuador timezone (GMT-5) for all dates --- src/app/social-events/page.tsx | 17 +++-- src/components/CalendarStrip.tsx | 39 ++-------- src/lib/date.ts | 125 +++++++++++++++++++++++++++++++ 3 files changed, 140 insertions(+), 41 deletions(-) create mode 100644 src/lib/date.ts diff --git a/src/app/social-events/page.tsx b/src/app/social-events/page.tsx index 908ecf2..0733a1e 100644 --- a/src/app/social-events/page.tsx +++ b/src/app/social-events/page.tsx @@ -1,6 +1,12 @@ 'use client'; -import { useEffect, useState } from 'react'; +import { useState, useEffect } from 'react'; +import { formatDisplayDateFull } from '@/lib/date'; + +const gold = '#E8A849'; +const navy = '#010D1E'; +const cream = '#f5f0e8'; +const warm = '#a6683c'; interface SocialEvent { id: number; @@ -23,11 +29,6 @@ interface User { comments_disabled?: boolean; } -const gold = '#E8A849'; -const navy = '#010D1E'; -const cream = '#f5f0e8'; -const warm = '#a6683c'; - export default function SocialEventsPage() { const [events, setEvents] = useState([]); const [loading, setLoading] = useState(true); @@ -167,7 +168,7 @@ export default function SocialEventsPage() {

{event.name}

{event.date && (

- {new Date(event.date).toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })} + {formatDisplayDateFull(event.date)}

)}

{event.description}

@@ -242,4 +243,4 @@ export default function SocialEventsPage() { ); -} +} \ No newline at end of file diff --git a/src/components/CalendarStrip.tsx b/src/components/CalendarStrip.tsx index 6ce4f28..850ec9c 100644 --- a/src/components/CalendarStrip.tsx +++ b/src/components/CalendarStrip.tsx @@ -1,6 +1,7 @@ 'use client'; import { useEffect, useRef, useState } from 'react'; +import { getEcuadorToday, getEcuadorDateFromToday, daysUntilEcuador, getMonthDayEcuador } from '@/lib/date'; interface CalendarEvent { id: number; @@ -50,34 +51,6 @@ const typeEmoji: Record = { event: '📌', }; -function parseDateLocal(dateStr: string): Date { - // Parse date as local time to avoid timezone shifts - // Input format: YYYY-MM-DD or YYYY-MM-DDTHH:MM:SS - const parts = dateStr.split('T')[0].split('-'); - const year = parseInt(parts[0], 10); - const month = parseInt(parts[1], 10) - 1; // JS months are 0-indexed - const day = parseInt(parts[2], 10); - return new Date(year, month, day); -} - -function formatMonthDay(dateStr: string) { - const d = parseDateLocal(dateStr); - return { month: d.toLocaleString('en-US', { month: 'short' }), day: d.getDate() }; -} - -function daysUntil(dateStr: string) { - const today = new Date(); - today.setHours(0, 0, 0, 0); - const d = parseDateLocal(dateStr); - d.setHours(0, 0, 0, 0); - const diff = Math.ceil((d.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)); - if (isNaN(diff)) return 'Unknown'; - if (diff === 0) return 'Today'; - if (diff === 1) return 'Tomorrow'; - if (diff < 0) return 'Past'; - return `in ${diff} days`; -} - export default function CalendarStrip() { const [events, setEvents] = useState([]); const [weather, setWeather] = useState(null); @@ -101,7 +74,7 @@ export default function CalendarStrip() { ? [ { id: 9997, - date: new Date().toISOString().split('T')[0], + date: getEcuadorToday(), title: `${getWeatherIcon(weather.today?.weathercode)} ${weather.current?.celsius ?? '--'}°C`, type: 'weather' as const, description: `Today: ${weather.today?.maxC ?? '--'}° / ${weather.today?.minC ?? '--'}°C · ${weather.today?.summary ?? ''}`, @@ -110,7 +83,7 @@ export default function CalendarStrip() { }, { id: 9998, - date: new Date(Date.now() + 86400000).toISOString().split('T')[0], + date: getEcuadorDateFromToday(1), title: `${getWeatherIcon(weather.tomorrow?.weathercode)} ${weather.tomorrow?.maxC ?? '--'}° / ${weather.tomorrow?.minC ?? '--'}°C`, type: 'weather' as const, description: `Tomorrow: ${weather.tomorrow?.summary ?? ''}`, @@ -119,7 +92,7 @@ export default function CalendarStrip() { }, { id: 9999, - date: new Date(Date.now() + 172800000).toISOString().split('T')[0], + date: getEcuadorDateFromToday(2), title: `${getWeatherIcon(weather.dayAfter?.weathercode)} ${weather.dayAfter?.maxC ?? '--'}° / ${weather.dayAfter?.minC ?? '--'}°C`, type: 'weather' as const, description: `Day After: ${weather.dayAfter?.summary ?? ''}`, @@ -184,7 +157,7 @@ export default function CalendarStrip() { ref={trackRef} > {[...cards, ...cards].map((e, i) => { - const { month, day } = formatMonthDay(e.date); + const { month, day } = getMonthDayEcuador(e.date); const accent = e.color || gold; return (
- {e.isWeather ? 'Otavalo now' : daysUntil(e.date)} + {e.isWeather ? 'Otavalo now' : daysUntilEcuador(e.date)}
); diff --git a/src/lib/date.ts b/src/lib/date.ts new file mode 100644 index 0000000..79bf4e9 --- /dev/null +++ b/src/lib/date.ts @@ -0,0 +1,125 @@ +// Date utilities for La Huasca - Ecuador is GMT-5 (no DST) + +/** + * Get current date/time in Ecuador timezone (GMT-5) + */ +export function getEcuadorDate(): Date { + const now = new Date(); + // Ecuador is UTC-5, so subtract 5 hours from UTC + const ecuadorOffset = -5 * 60; // minutes + const utcOffset = now.getTimezoneOffset(); // local offset from UTC + const ecuadorTime = new Date(now.getTime() + (utcOffset + ecuadorOffset) * 60000); + return ecuadorTime; +} + +/** + * Get today's date in Ecuador (YYYY-MM-DD format) + */ +export function getEcuadorToday(): string { + return formatDateEcuador(getEcuadorDate()); +} + +/** + * Format a date as YYYY-MM-DD in Ecuador timezone + */ +export function formatDateEcuador(date: Date): string { + const ecuadorOffset = -5 * 60; // minutes + const utc = date.getTime() + date.getTimezoneOffset() * 60000; + const ecuadorTime = new Date(utc + ecuadorOffset * 60000); + + const year = ecuadorTime.getFullYear(); + const month = String(ecuadorTime.getMonth() + 1).padStart(2, '0'); + const day = String(ecuadorTime.getDate()).padStart(2, '0'); + + return `${year}-${month}-${day}`; +} + +/** + * Get a date N days from today in Ecuador timezone + */ +export function getEcuadorDateFromToday(days: number): string { + const today = getEcuadorDate(); + today.setDate(today.getDate() + days); + return formatDateEcuador(today); +} + +/** + * Format a date string (YYYY-MM-DD or ISO) for display in Ecuador timezone + */ +export function formatDisplayDate(dateStr: string, options?: Intl.DateTimeFormatOptions): string { + const date = parseDateAsEcuador(dateStr); + const defaultOptions: Intl.DateTimeFormatOptions = { + weekday: 'short', + month: 'short', + day: 'numeric', + timeZone: 'America/Guayaquil', + }; + return date.toLocaleDateString('en-US', options || defaultOptions); +} + +/** + * Format a date with full weekday name + */ +export function formatDisplayDateFull(dateStr: string): string { + return formatDisplayDate(dateStr, { + weekday: 'long', + year: 'numeric', + month: 'long', + day: 'numeric', + timeZone: 'America/Guayaquil', + }); +} + +/** + * Format a datetime string for display + */ +export function formatDisplayDateTime(dateStr: string): string { + const date = new Date(dateStr); + return date.toLocaleString('en-US', { + month: 'short', + day: 'numeric', + hour: '2-digit', + minute: '2-digit', + timeZone: 'America/Guayaquil', + }); +} + +/** + * Parse a date string (YYYY-MM-DD) as Ecuador date, avoiding timezone shifts + */ +export function parseDateAsEcuador(dateStr: string): Date { + // Parse as local components to avoid timezone shifts + const parts = dateStr.split('T')[0].split('-'); + const year = parseInt(parts[0], 10); + const month = parseInt(parts[1], 10) - 1; // JS months are 0-indexed + const day = parseInt(parts[2], 10); + return new Date(year, month, day); +} + +/** + * Calculate days until a date (in Ecuador timezone) + */ +export function daysUntilEcuador(dateStr: string): string { + const today = getEcuadorDate(); + today.setHours(0, 0, 0, 0); + const target = parseDateAsEcuador(dateStr); + target.setHours(0, 0, 0, 0); + const diff = Math.ceil((target.getTime() - today.getTime()) / (1000 * 60 * 60 * 24)); + + if (isNaN(diff)) return 'Unknown'; + if (diff === 0) return 'Today'; + if (diff === 1) return 'Tomorrow'; + if (diff < 0) return 'Past'; + return `in ${diff} days`; +} + +/** + * Get month and day for display (e.g., "Jun 29") + */ +export function getMonthDayEcuador(dateStr: string): { month: string; day: number } { + const date = parseDateAsEcuador(dateStr); + return { + month: date.toLocaleString('en-US', { month: 'short' }), + day: date.getDate(), + }; +} \ No newline at end of file