UNPKG

tiny-essentials

Version:

Collection of small, essential scripts designed to be used across various projects. These simple utilities are crafted for speed, ease of use, and versatility.

218 lines (193 loc) 6.82 kB
'use strict'; /** * Calculates the time duration between the current time and a given time offset. * * @param {Date} timeData - The target time as a Date object. * @param {string} [durationType='asSeconds'] - The type of duration to return. Can be 'asMilliseconds', 'asSeconds', 'asMinutes', 'asHours', 'asDays'. * @param {Date|null} [now=null] - The current time as a Date object. Defaults to the current date and time if not provided. * @returns {number|null} The calculated duration in the specified unit, or `null` if `timeData` is not provided. */ function getTimeDuration(timeData = new Date(), durationType = 'asSeconds', now = null) { if (timeData instanceof Date) { const currentTime = now instanceof Date ? now : new Date(); /** @type {number} */ const diffMs = timeData.getTime() - currentTime.getTime(); switch (durationType) { case 'asMilliseconds': return diffMs; case 'asSeconds': return diffMs / 1000; case 'asMinutes': return diffMs / (1000 * 60); case 'asHours': return diffMs / (1000 * 60 * 60); case 'asDays': return diffMs / (1000 * 60 * 60 * 24); default: return diffMs / 1000; // default to seconds } } return null; } /** * Formats a custom timer string based on total seconds and a detail level. * Includes proper reallocation of lower units into higher ones, ensuring consistent hierarchy. * * @param {number} totalSeconds - The total amount of seconds to convert. * @param {'seconds'|'minutes'|'hours'|'days'|'months'|'years'} [level] - The highest level to calculate and display. * @param {string} [format='{time}'] - Output template with placeholders like {years}, {months}, {days}, {hours}, {minutes}, {seconds}, {time}, {total}. * @returns {string} The formatted timer string. */ function formatCustomTimer(totalSeconds, level = 'seconds', format = '{time}') { totalSeconds = Math.max(0, Math.floor(totalSeconds)); const levels = ['seconds', 'minutes', 'hours', 'days', 'months', 'years']; const index = levels.indexOf(level); const include = { years: index >= 5, months: index >= 4, days: index >= 3, hours: index >= 2, minutes: index >= 1, seconds: index >= 0, }; /** * @type {{ * years: number|NaN, * months: number|NaN, * days: number|NaN, * hours: number|NaN, * minutes: number|NaN, * seconds: number|NaN, * total: number|NaN * }} */ const parts = { years: include.years ? 0 : NaN, months: include.months ? 0 : NaN, days: include.days ? 0 : NaN, hours: include.hours ? 0 : NaN, minutes: include.minutes ? 0 : NaN, seconds: include.seconds ? 0 : NaN, total: NaN, }; let remaining = totalSeconds; if (include.years || include.months || include.days) { const baseDate = new Date(1980, 0, 1); const targetDate = new Date(baseDate.getTime() + remaining * 1000); const workingDate = new Date(baseDate); // Years if (include.years) { while ( new Date( workingDate.getFullYear() + 1, workingDate.getMonth(), workingDate.getDate(), ).getTime() <= targetDate.getTime() ) { workingDate.setFullYear(workingDate.getFullYear() + 1); parts.years++; } } // Months if (include.months) { while ( new Date( workingDate.getFullYear(), workingDate.getMonth() + 1, workingDate.getDate(), ).getTime() <= targetDate.getTime() ) { workingDate.setMonth(workingDate.getMonth() + 1); parts.months++; } } // Days if (include.days) { while ( new Date( workingDate.getFullYear(), workingDate.getMonth(), workingDate.getDate() + 1, ).getTime() <= targetDate.getTime() ) { workingDate.setDate(workingDate.getDate() + 1); parts.days++; } } remaining = Math.floor((targetDate.getTime() - workingDate.getTime()) / 1000); } if (include.hours) { parts.hours = Math.floor(remaining / 3600); remaining %= 3600; } if (include.minutes) { parts.minutes = Math.floor(remaining / 60); remaining %= 60; } if (include.seconds) { parts.seconds = remaining; } // Calculate total const totalMap = { seconds: include.seconds ? totalSeconds : NaN, minutes: include.minutes ? totalSeconds / 60 : NaN, hours: include.hours ? totalSeconds / 3600 : NaN, days: include.days ? totalSeconds / 86400 : NaN, months: include.months ? parts.years * 12 + parts.months + (parts.days || 0) / 30 : NaN, years: include.years ? parts.years + (parts.months || 0) / 12 + (parts.days || 0) / 365 : NaN, }; parts.total = +(totalMap[level] || 0).toFixed(2).replace(/\.00$/, ''); /** * Pads a number to ensure it is at least two digits long, using leading zeros if necessary. * * @param {number|string} n - The number or string to pad. * @returns {string} The padded string. */ const pad = (n) => { const num = typeof n === 'string' ? parseInt(n) : n; return Number.isNaN(num) ? 'NaN' : String(num).padStart(2, '0'); }; const timeString = [ include.hours ? pad(parts.hours) : null, include.minutes ? pad(parts.minutes) : null, include.seconds ? pad(parts.seconds) : null, ] .filter((v) => v !== null) .join(':'); return format .replace(/\{years\}/g, String(parts.years)) .replace(/\{months\}/g, String(parts.months)) .replace(/\{days\}/g, String(parts.days)) .replace(/\{hours\}/g, pad(parts.hours)) .replace(/\{minutes\}/g, pad(parts.minutes)) .replace(/\{seconds\}/g, pad(parts.seconds)) .replace(/\{time\}/g, timeString) .replace(/\{total\}/g, String(parts.total)) .trim(); } /** * Formats a duration (in seconds) into a timer string showing only hours, minutes, and seconds. * * Example output: "05:32:10" * * @param {number} seconds - The total number of seconds to format. * @returns {string} The formatted timer string in "HH:MM:SS" format. */ function formatTimer(seconds) { return formatCustomTimer(seconds, 'hours', '{hours}:{minutes}:{seconds}'); } /** * Formats a duration (in seconds) into a timer string including days, hours, minutes, and seconds. * * Example output: "2d 05:32:10" * * @param {number} seconds - The total number of seconds to format. * @returns {string} The formatted timer string in "Xd HH:MM:SS" format. */ function formatDayTimer(seconds) { return formatCustomTimer(seconds, 'days', '{days}d {hours}:{minutes}:{seconds}'); } exports.formatCustomTimer = formatCustomTimer; exports.formatDayTimer = formatDayTimer; exports.formatTimer = formatTimer; exports.getTimeDuration = getTimeDuration;