UNPKG

elliotisms

Version:

reusable atomic functions for the dumb shit i reuse all the time. only about half are jokes.

183 lines (171 loc) 6.36 kB
/** * Calculates and returns a string representing the time elapsed since a given date. * * @param {Date|string|number} data - The date to compare against the current time. Can be a Date object, a timestamp, or a string that can be parsed into a date. * @param {Object} [options] - Configuration options for formatting the output. * @param {boolean} [options.compact=false] - If true, returns a compact representation of the time difference. * @param {Object} [options.customStrings={}] - Object containing custom strings to override default language strings. * @param {string} [options.granularity='auto'] - Determines the level of detail in the output. Options: 'auto', 'detailed'. * @returns {string|null} A string representing the time difference (e.g., "2 days ago") or null if no date is provided. * * @example * const date = new Date('2023-01-01T12:00:00Z'); * const result = timeAgo(date); * console.log(result); // Output could be "9 months ago" depending on the current date. * * @example * const timestamp = 1672444800000; // January 1, 2023 * const result = timeAgo(timestamp, { compact: true }); * console.log(result); // Output could be "9mo" depending on the current date. * * @example * const invalidDate = 'not_a_date'; * const result = timeAgo(invalidDate); * console.log(result); // Output: '' */ const timeAgo = ( data, options = { compact: false, customStrings: {}, granularity: "auto" // options: 'auto', 'detailed' } ) => { if (!data) return null // Convert string date to timestamp if (typeof data === "string") { const parsedDate = Date.parse(data) if (isNaN(parsedDate)) { console.error("Error with timeAgo: Invalid date") return "" } data = parsedDate } const now = Date.now() const difference = Math.round((now - data) / 1000) // Language strings with defaults, can be overridden by custom strings const langStrings = { daysAgo: "days ago", hourAgo: "1 hour ago", hoursAgo: "hours ago", inFuture: "in the future", justNow: "just now", justNowCompact: "now", minuteAgo: "1 minute ago", minutesAgo: "minutes ago", monthAgo: "1 month ago", monthsAgo: "months ago", secondsAgo: "seconds ago", weekAgo: "1 week ago", weeksAgo: "weeks ago", yearAgo: "1 year ago", yearsAgo: "years ago", yesterday: "Yesterday", ...options.customStrings } const thresholds = { century: 3_153_600_000, // 100 years day: 86_400, decade: 315_360_000, // 10 years hour: 3600, minute: 60, month: 2_592_000, // 30 days week: 604_800, year: 31_536_000 // 365 days } const getUnits = () => { if (Math.abs(difference) < thresholds.minute) return { unit: "seconds", value: difference } if (Math.abs(difference) < thresholds.hour) return { unit: "minutes", value: Math.round(difference / thresholds.minute) } if (Math.abs(difference) < thresholds.day) return { unit: "hours", value: Math.round(difference / thresholds.hour) } if (Math.abs(difference) < thresholds.week) return { unit: "days", value: Math.round(difference / thresholds.day) } if (Math.abs(difference) < thresholds.month) return { unit: "weeks", value: Math.round(difference / thresholds.week) } if (Math.abs(difference) < thresholds.year) return { unit: "months", value: Math.round(difference / thresholds.month) } if (Math.abs(difference) < thresholds.decade) return { unit: "years", value: Math.round(difference / thresholds.year) } if (Math.abs(difference) < thresholds.century) return { unit: "decade", value: Math.round(difference / thresholds.decade) } return { unit: "centuries", value: Math.round(difference / thresholds.century) } } const { unit, value } = getUnits() if (difference < 0) { return options.compact ? `${Math.abs(value)}${unit[0]}` : `in ${Math.abs(value)} ${Math.abs(value) === 1 ? unit.slice(0, -1) : unit} ago` } const detailedUnitMapping = { centuries: `1 century ago`, days: value === 1 ? langStrings.yesterday : `${value} ${langStrings.daysAgo}`, decade: value === 1 ? `1 decade ago` : `${value} decades ago`, hours: value === 1 ? langStrings.hourAgo : `${value} ${langStrings.hoursAgo}`, minutes: value === 1 ? langStrings.minuteAgo : `${value} ${langStrings.minutesAgo}`, months: value === 1 ? langStrings.monthAgo : `${value} ${langStrings.monthsAgo}`, seconds: langStrings.justNow, weeks: value === 1 ? langStrings.weekAgo : `${value} ${langStrings.weeksAgo}`, years: value === 1 ? langStrings.yearAgo : `${value} ${langStrings.yearsAgo}` } if (options.compact) { return value === 1 && unit === "seconds" ? langStrings.justNowCompact : `${value}${unit[0]}` } else if (options.granularity === "detailed") { return detailedUnitMapping[unit] } else { // Auto granularity if (value === 1 && unit === "seconds") { return langStrings.justNow } // Use the appropriate langStrings key based on singular/plural if (value === 1) { // Special handling for decade (already singular) if (unit === "decade") { return "1 decade ago" } // For singular, check if there's a custom string for the plural form const pluralKey = `${unit}Ago` // e.g., "daysAgo" if (langStrings[pluralKey] && langStrings[pluralKey] !== `${unit} ago`) { // If there's a custom plural string, use it with singular value return `${value} ${langStrings[pluralKey]}` } else { // Use the singular form from langStrings (already includes "1") const singularKey = `${unit.slice(0, -1)}Ago` // e.g., "dayAgo" return langStrings[singularKey] || `${value} ${unit.slice(0, -1)} ago` } } else { // Special handling for decade (plural form) if (unit === "decade") { return `${value} decades ago` } // For plural, use the plural form from langStrings (needs number prefix) const pluralKey = `${unit}Ago` // e.g., "minutesAgo" return `${value} ${langStrings[pluralKey] || `${unit} ago`}` } } } export default timeAgo