import { DateTime } from "luxon"; export enum TimeType { UTC, BusTime, TimeAgo, } export function dateTimeFromWubloaderTime(wubloaderTime: string): DateTime | null { const dt = DateTime.fromISO(wubloaderTime, { zone: "UTC" }); if (dt.isValid) { return dt; } return null; } export function wubloaderTimeFromDateTime(dateTime: DateTime): string { // Not using ISO here because Luxon doesn't give us a quick way to print an ISO8601 string with no offset. return dateTime.toFormat("yyyy-LL-dd'T'HH:mm:ss.SSS"); } class DateTimeMathObject { hours: number; minutes: number; seconds: number; } function dateTimeMathObjectFromBusTime(busTime: string): DateTimeMathObject | null { // We need to handle inputs like "-0:10:15" in a way that consistently makes the time negative. // Since we can't assign the negative sign to any particular part, we'll check for the whole thing here. let direction = 1; if (busTime.startsWith("-")) { busTime = busTime.slice(1); direction = -1; } const parts = busTime.split(":", 3); const hours = parseInt(parts[0], 10) * direction; const minutes = parts.length > 1 ? parseInt(parts[1], 10) * direction : 0; const seconds = parts.length > 2 ? parseInt(parts[2], 10) * direction : 0; return { hours: hours, minutes: minutes, seconds: seconds }; } export function dateTimeFromBusTime(busStartTime: DateTime, busTime: string): DateTime | null { const busMathObject = dateTimeMathObjectFromBusTime(busTime); if (busMathObject === null) { return null; } return busStartTime.plus(busMathObject); } export function busTimeFromDateTime(busStartTime: DateTime, time: DateTime): string { const diff = time.diff(busStartTime); if (diff.milliseconds < 0) { const negativeInterval = diff.negate(); return `-${negativeInterval.toFormat("hh:mm:ss.SSS")}`; } return diff.toFormat("hh:mm:ss.SSS"); } export function dateTimeFromTimeAgo(timeAgo: string): DateTime | null { const parts = timeAgo.split(":"); const properties = ["hours", "minutes", "seconds"]; const mathObj = {}; while (parts.length > 0) { const nextPart = parts.pop(); if (properties.length === 0) { return null; } const nextProp = properties.pop(); const partNumber = parseInt(nextPart, 10); if (isNaN(partNumber)) { return null; } mathObj[nextProp] = partNumber; } const now = DateTime.utc(); return now.plus(mathObj); } export function timeAgoFromDateTime(dateTime: DateTime): string { const currentTime = DateTime.utc(); const interval = currentTime.diff(dateTime, "seconds"); let timeAgoSeconds = interval.seconds; let negative = ""; if (timeAgoSeconds < 0) { negative = "-"; timeAgoSeconds = -timeAgoSeconds; } const seconds = (((timeAgoSeconds % 60) * 1000) | 0) / 1000; const secondsString = seconds < 10 ? `0${seconds}` : seconds.toString(); const minutes = (timeAgoSeconds / 60) % 60 | 0; const minutesString = minutes < 10 ? `0${minutes}` : minutes.toString(); const hours = Math.floor(timeAgoSeconds / 3600); return `${negative}${hours}:${minutesString}:${secondsString}`; }