fix: use Ecuador timezone (GMT-5) for all dates
This commit is contained in:
@@ -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>
|
||||
|
||||
@@ -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
@@ -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(),
|
||||
};
|
||||
}
|
||||
Reference in New Issue
Block a user