fix: use Ecuador timezone (GMT-5) for all dates

This commit is contained in:
2026-06-29 23:52:08 -05:00
parent d6ca208fc6
commit 7b72994d4a
3 changed files with 140 additions and 41 deletions
+9 -8
View File
@@ -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<SocialEvent[]>([]);
const [loading, setLoading] = useState(true);
@@ -167,7 +168,7 @@ export default function SocialEventsPage() {
<h2 style={{ margin: '0 0 0.5rem', color: navy, fontSize: '24px' }}>{event.name}</h2>
{event.date && (
<p style={{ margin: '0 0 0.5rem', color: warm, fontSize: '15px', fontWeight: 600 }}>
{new Date(event.date).toLocaleDateString(undefined, { weekday: 'long', year: 'numeric', month: 'long', day: 'numeric' })}
{formatDisplayDateFull(event.date)}
</p>
)}
<p style={{ color: '#555', lineHeight: 1.5, flex: 1 }}>{event.description}</p>
@@ -242,4 +243,4 @@ export default function SocialEventsPage() {
</div>
</main>
);
}
}
+6 -33
View File
@@ -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<CalendarEvent['type'], string> = {
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<CalendarEvent[]>([]);
const [weather, setWeather] = useState<WeatherData | null>(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 (
<div
@@ -239,7 +212,7 @@ export default function CalendarStrip() {
{e.description || ''}
</p>
<div style={{ marginTop: '0.8rem', fontSize: '12px', color: 'rgba(245,240,232,0.5)' }}>
{e.isWeather ? 'Otavalo now' : daysUntil(e.date)}
{e.isWeather ? 'Otavalo now' : daysUntilEcuador(e.date)}
</div>
</div>
);
+125
View File
@@ -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(),
};
}