81 lines
2.1 KiB
TypeScript
81 lines
2.1 KiB
TypeScript
import { type ClassValue, clsx } from "clsx";
|
|
import { twMerge } from "tailwind-merge";
|
|
|
|
export type Coordinates = {
|
|
lat: number;
|
|
lng: number;
|
|
};
|
|
|
|
export function cn(...inputs: ClassValue[]): string {
|
|
return twMerge(clsx(inputs));
|
|
}
|
|
|
|
export function formatCoordinate(value: number, locale = "en-US"): string {
|
|
const formatter = new Intl.NumberFormat(locale, {
|
|
minimumFractionDigits: 3,
|
|
maximumFractionDigits: 3,
|
|
});
|
|
return formatter.format(value);
|
|
}
|
|
|
|
export function formatRelativeTime(dateIso: string, locale = "en-US"): string {
|
|
const date = new Date(dateIso);
|
|
const now = new Date();
|
|
const diff = Math.max(0, now.getTime() - date.getTime());
|
|
|
|
const seconds = Math.floor(diff / 1000);
|
|
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: "auto" });
|
|
|
|
if (seconds < 60) {
|
|
return rtf.format(-seconds, "second");
|
|
}
|
|
|
|
const minutes = Math.floor(seconds / 60);
|
|
if (minutes < 60) {
|
|
return rtf.format(-minutes, "minute");
|
|
}
|
|
|
|
const hours = Math.floor(minutes / 60);
|
|
if (hours < 24) {
|
|
return rtf.format(-hours, "hour");
|
|
}
|
|
|
|
const days = Math.floor(hours / 24);
|
|
if (days < 7) {
|
|
return rtf.format(-days, "day");
|
|
}
|
|
|
|
const weeks = Math.floor(days / 7);
|
|
return rtf.format(-weeks, "week");
|
|
}
|
|
|
|
export function formatTimestamp(dateIso: string, locale = "en-US"): string {
|
|
const date = new Date(dateIso);
|
|
return new Intl.DateTimeFormat(locale, {
|
|
month: "short",
|
|
day: "numeric",
|
|
hour: "numeric",
|
|
minute: "2-digit",
|
|
}).format(date);
|
|
}
|
|
|
|
const EARTH_RADIUS_KM = 6371;
|
|
|
|
export function distanceInKm(a: Coordinates, b: Coordinates): number {
|
|
const lat1 = toRadians(a.lat);
|
|
const lat2 = toRadians(b.lat);
|
|
const deltaLat = toRadians(b.lat - a.lat);
|
|
const deltaLng = toRadians(b.lng - a.lng);
|
|
|
|
const haversine =
|
|
Math.sin(deltaLat / 2) * Math.sin(deltaLat / 2) +
|
|
Math.cos(lat1) * Math.cos(lat2) * Math.sin(deltaLng / 2) * Math.sin(deltaLng / 2);
|
|
|
|
const c = 2 * Math.atan2(Math.sqrt(haversine), Math.sqrt(1 - haversine));
|
|
return EARTH_RADIUS_KM * c;
|
|
}
|
|
|
|
function toRadians(value: number): number {
|
|
return (value * Math.PI) / 180;
|
|
}
|