{ "$schema": "https://ui.shadcn.com/schema/registry-item.json", "name": "time-ago", "files": [ { "path": "registry/lib/time-ago.ts", "content": "type TimeUnit = \"year\" | \"month\" | \"week\" | \"day\" | \"hour\" | \"minute\" | \"second\"\n\ninterface TimeInterval {\n seconds: number\n label: TimeUnit\n}\n\ninterface TimeAgoOptions {\n short?: boolean\n maxUnits?: number\n future?: boolean\n round?: boolean\n}\n\nexport const timeAgo = (date: Date | string | number): string => {\n const inputDate = date instanceof Date ? date : new Date(date)\n const now = new Date()\n\n const seconds = Math.floor((now.getTime() - inputDate.getTime()) / 1000)\n const isNegative = seconds < 0\n const absoluteSeconds = Math.abs(seconds)\n\n const intervals: TimeInterval[] = [\n { seconds: 31536000, label: \"year\" },\n { seconds: 2592000, label: \"month\" },\n { seconds: 604800, label: \"week\" },\n { seconds: 86400, label: \"day\" },\n { seconds: 3600, label: \"hour\" },\n { seconds: 60, label: \"minute\" },\n { seconds: 1, label: \"second\" },\n ]\n\n if (absoluteSeconds < 10) return \"just now\"\n if (absoluteSeconds < 60) return `${absoluteSeconds} seconds ago`\n\n const interval = intervals.find((int) => absoluteSeconds >= int.seconds)\n if (!interval) return \"just now\"\n\n const value = Math.floor(absoluteSeconds / interval.seconds)\n\n const specialSingular: Record string> = {\n hour: (val) => (val === 1 ? \"an hour\" : `${val} hours`),\n day: (val) => (val === 1 ? \"a day\" : `${val} days`),\n week: (val) => (val === 1 ? \"a week\" : `${val} weeks`),\n month: (val) => (val === 1 ? \"a month\" : `${val} months`),\n year: (val) => (val === 1 ? \"a year\" : `${val} years`),\n minute: (val) => `${val} minute${val === 1 ? \"\" : \"s\"}`,\n second: (val) => `${val} second${val === 1 ? \"\" : \"s\"}`,\n }\n\n const timeString = specialSingular[interval.label](value)\n return isNegative ? `in ${timeString}` : `${timeString} ago`\n}\n\nexport const formatTimeAgo = (\n date: Date | string | number,\n options: TimeAgoOptions = {}\n): string => {\n const { short = false, maxUnits = 1, future = true, round = true } = options\n\n const inputDate = date instanceof Date ? date : new Date(date)\n const now = new Date()\n\n const seconds = Math.floor((now.getTime() - inputDate.getTime()) / 1000)\n const isNegative = seconds < 0\n const absoluteSeconds = Math.abs(seconds)\n\n type UnitConfig = [string, number] // [shortLabel, seconds]\n const units: Record = {\n year: [\"y\", 31536000],\n month: [\"mo\", 2592000],\n week: [\"w\", 604800],\n day: [\"d\", 86400],\n hour: [\"h\", 3600],\n minute: [\"m\", 60],\n second: [\"s\", 1],\n }\n\n if (absoluteSeconds < 10) return \"just now\"\n\n let remaining = absoluteSeconds\n const parts: string[] = []\n\n for (const [unit, [shortLabel, secondsInUnit]] of Object.entries(units)) {\n if (parts.length >= maxUnits) break\n\n const value = Math.floor(remaining / secondsInUnit)\n if (value >= 1) {\n const formatted =\n short ?\n `${value}${shortLabel}`\n : `${value} ${unit}${value === 1 ? \"\" : \"s\"}`\n parts.push(formatted)\n remaining -= value * secondsInUnit\n }\n }\n\n if (!parts.length) return \"just now\"\n\n const timeString = parts.join(short ? \" \" : \", \")\n return isNegative && future ? `in ${timeString}` : `${timeString} ago`\n}\n", "type": "registry:lib", "target": "lib/time-ago.ts" } ], "type": "registry:lib" }