Openstatus www.openstatus.dev
at 4c0f4c00a38753a5d0dfd7e7b7b7706dec6f1503 108 lines 3.2 kB view raw
1import { format, getTimezoneOffset, utcToZonedTime } from "date-fns-tz"; 2import { headers } from "next/headers"; 3 4export async function getRequestHeaderTimezone() { 5 const headersList = await headers(); 6 7 /** 8 * Vercel includes ip timezone to request header 9 */ 10 const requestTimezone = headersList.get("x-vercel-ip-timezone"); 11 12 return requestTimezone; 13} 14 15export async function convertTimezoneToGMT(defaultTimezone?: string) { 16 const requestTimezone = await getRequestHeaderTimezone(); 17 18 /** 19 * Server region timezone as fallback 20 */ 21 const clientTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; 22 23 const timezone = defaultTimezone || requestTimezone || clientTimezone; 24 25 if (!supportedTimezones.includes(timezone)) return "Etc/UTC"; 26 27 const msOffset = getTimezoneOffset(timezone); 28 29 if (Number.isNaN(msOffset)) return "Etc/UTC"; 30 31 const hrOffset = Math.round(msOffset / (1000 * 60 * 60)); // avoid weird 30min timezones 32 const offset = hrOffset >= 0 ? `-${hrOffset}` : `+${Math.abs(hrOffset)}`; 33 34 return `Etc/GMT${offset}` as const; 35} 36 37export function getServerTimezoneUTCFormat() { 38 const now = new Date(); 39 const now_utc = new Date(now.toUTCString().slice(0, -4)); // remove the GMT end 40 41 return format(now_utc, "LLL dd, y HH:mm:ss (z)", { timeZone: "UTC" }); 42} 43 44export function getServerTimezoneFormat() { 45 return format(new Date(), "LLL dd, y HH:mm:ss (z)"); 46} 47 48export function formatDate(date: Date) { 49 return format(date, "LLL dd, y", { timeZone: "UTC" }); 50} 51 52export function formatDateTime(date: Date) { 53 return format(date, "LLL dd, y HH:mm:ss zzz", { timeZone: "UTC" }); 54} 55 56export function formatTime(date: Date) { 57 return format(date, "HH:mm:ss zzz", { timeZone: "UTC" }); 58} 59 60/** 61 * All supported browser / node timezones 62 */ 63export const supportedTimezones = Intl.supportedValuesOf("timeZone"); 64 65export async function getClosestTimezone(defaultTimezone?: string) { 66 const requestTimezone = await getRequestHeaderTimezone(); 67 68 /** 69 * Server region timezone as fallback 70 */ 71 const clientTimezone = Intl.DateTimeFormat().resolvedOptions().timeZone; 72 73 const timezone = defaultTimezone || requestTimezone || clientTimezone; 74 75 if (!supportedTimezones.includes(timezone)) return "CET"; 76 77 const now = new Date(); 78 79 const estTime = utcToZonedTime(now, "America/New_York"); 80 const pstTime = utcToZonedTime(now, "America/Los_Angeles"); 81 const cetTime = utcToZonedTime(now, "Europe/Paris"); 82 const utcTime = utcToZonedTime(now, "UTC"); 83 84 const timeDifferences = { 85 EST: Math.abs(now.getTime() - estTime.getTime()), 86 PST: Math.abs(now.getTime() - pstTime.getTime()), 87 CET: Math.abs(now.getTime() - cetTime.getTime()), 88 UTC: Math.abs(now.getTime() - utcTime.getTime()), 89 }; 90 91 const closestTimezone = Object.keys(timeDifferences).reduce( 92 (prev, curr) => { 93 if ( 94 timeDifferences[curr as keyof typeof timeDifferences] < 95 prev.minDifference 96 ) { 97 return { 98 timezone: curr, 99 minDifference: timeDifferences[curr as keyof typeof timeDifferences], 100 }; 101 } 102 return prev; 103 }, 104 { timezone: "UTC", minDifference: Number.POSITIVE_INFINITY }, 105 ); 106 107 return closestTimezone.timezone as keyof typeof timeDifferences; 108}