"use client"; import { useEffect, useMemo, useState } from "react"; import type { Destination } from "@/lib/types"; const TZ_OFFSETS: Record = { bali: 8, lisbon: 0, chiangmai: 7, mexico: -6, barcelona: 1, tokyo: 9, }; const HOME_TZ = 8; // China UTC+8 const WORK_START = 9; const WORK_END = 18; interface Props { destinations: Destination[]; } function getLocalHour(utcOffset: number): number { const now = new Date(); const utc = now.getUTCHours() + now.getUTCMinutes() / 60; return Math.floor((utc + utcOffset + 24) % 24); } function formatTime(hour: number, min: number): string { return `${String(hour).padStart(2, "0")}:${String(min).padStart(2, "0")}`; } function getOverlapHours(destOffset: number, homeStart: number, homeEnd: number): number { let overlap = 0; for (let h = homeStart; h < homeEnd; h++) { const destHour = (h - HOME_TZ + destOffset + 24) % 24; if (destHour >= 8 && destHour < 20) overlap++; } return overlap; } function overlapLabel(hours: number): { text: string; class: string } { if (hours >= 7) return { text: "极佳", class: "excellent" }; if (hours >= 5) return { text: "良好", class: "good" }; if (hours >= 3) return { text: "一般", class: "fair" }; return { text: "困难", class: "poor" }; } export default function TimezoneBoard({ destinations }: Props) { const [now, setNow] = useState(new Date()); const [homeStart, setHomeStart] = useState(WORK_START); const [homeEnd, setHomeEnd] = useState(WORK_END); useEffect(() => { const timer = setInterval(() => setNow(new Date()), 1000); return () => clearInterval(timer); }, []); const cities = useMemo(() => destinations .filter((d) => TZ_OFFSETS[d.slug] !== undefined) .map((d) => { const offset = TZ_OFFSETS[d.slug]; const localHour = getLocalHour(offset); const overlap = getOverlapHours(offset, homeStart, homeEnd); const label = overlapLabel(overlap); const isWorkTime = localHour >= 9 && localHour < 18; return { ...d, offset, localHour, overlap, label, isWorkTime }; }) .sort((a, b) => b.overlap - a.overlap), [destinations, homeStart, homeEnd]); const homeTime = formatTime(now.getHours(), now.getMinutes()); const homeSeconds = now.getSeconds(); return (
🕐 TIMEZONE

时区工作看板

实时查看各城市当地时间,评估与国内团队的工作时间重叠

🇨🇳 北京时间 {homeTime}:{String(homeSeconds).padStart(2, "0")}
开始 setHomeStart(Math.min(+e.target.value, homeEnd - 1))} /> {homeStart}:00
结束 setHomeEnd(Math.max(+e.target.value, homeStart + 1))} /> {homeEnd}:00
{cities.map((city) => { const localMin = now.getUTCMinutes(); const localTime = formatTime(city.localHour, localMin); const angle = ((city.localHour % 12) + localMin / 60) * 30; return (
{city.emoji}
{city.name} UTC{city.offset >= 0 ? "+" : ""}{city.offset}
{city.label.text}
{[0, 3, 6, 9].map((n) => ( ))} {localTime}
重叠 {city.overlap}h / {homeEnd - homeStart}h
{city.isWorkTime ? "💼 当地工作时间" : "🌙 当地非工作时间"}
); })}
); }