UNPKG

@catbee/utils

Version:

A modular, production-grade utility toolkit for Node.js and TypeScript, designed for robust, scalable applications (including Express-based services). All utilities are tree-shakable and can be imported independently.

970 lines (964 loc) 26.4 kB
/* * The MIT License * * Copyright (c) 2026 Catbee Technologies. https://catbee.in/license * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in all * copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE * SOFTWARE. */ 'use strict'; var __defProp = Object.defineProperty; var __name = (target, value) => __defProp(target, "name", { value, configurable: true }); // src/date/date.utils.ts function formatDate(date, options = {}) { const { format = "yyyy-MM-dd", locale, timeZone } = options; const d = date instanceof Date ? date : new Date(date); if (format === "yyyy-MM-dd") { const yyyy = d.getFullYear(); const mm = String(d.getMonth() + 1).padStart(2, "0"); const dd = String(d.getDate()).padStart(2, "0"); return `${yyyy}-${mm}-${dd}`; } if (format === "yyyy-MM-dd HH:mm:ss") { const yyyy = d.getFullYear(); const mm = String(d.getMonth() + 1).padStart(2, "0"); const dd = String(d.getDate()).padStart(2, "0"); const HH = String(d.getHours()).padStart(2, "0"); const MM = String(d.getMinutes()).padStart(2, "0"); const SS = String(d.getSeconds()).padStart(2, "0"); return `${yyyy}-${mm}-${dd} ${HH}:${MM}:${SS}`; } if (format === "relative") { return formatRelativeTime(date); } return new Intl.DateTimeFormat(locale, { timeZone, year: format.includes("yyyy") || format.includes("y") ? "numeric" : void 0, month: format.includes("MM") || format.includes("M") ? "numeric" : void 0, day: format.includes("dd") || format.includes("d") ? "numeric" : void 0, hour: format.includes("HH") || format.includes("H") ? "numeric" : void 0, minute: format.includes("mm") || format.includes("m") ? "numeric" : void 0, second: format.includes("ss") || format.includes("s") ? "numeric" : void 0, hour12: !format.includes("HH") && !format.includes("H") }).format(d); } __name(formatDate, "formatDate"); function formatRelativeTime(date, now = /* @__PURE__ */ new Date(), locale) { const d1 = date instanceof Date ? date : new Date(date); const d2 = now instanceof Date ? now : new Date(now); const diffMs = d1.getTime() - d2.getTime(); const diffSecs = Math.round(diffMs / 1e3); const diffMins = Math.round(diffSecs / 60); const diffHours = Math.round(diffMins / 60); const diffDays = Math.round(diffHours / 24); const diffMonths = Math.round(diffDays / 30); const diffYears = Math.round(diffDays / 365); const formatter = new Intl.RelativeTimeFormat(locale, { numeric: "auto" }); if (Math.abs(diffSecs) < 60) return formatter.format(diffSecs, "second"); if (Math.abs(diffMins) < 60) return formatter.format(diffMins, "minute"); if (Math.abs(diffHours) < 24) return formatter.format(diffHours, "hour"); if (Math.abs(diffDays) < 30) return formatter.format(diffDays, "day"); if (Math.abs(diffMonths) < 12) return formatter.format(diffMonths, "month"); return formatter.format(diffYears, "year"); } __name(formatRelativeTime, "formatRelativeTime"); function parseDate(input, fallback) { if (typeof input === "number") { return new Date(input); } try { const date = new Date(input); if (Number.isNaN(date.getTime())) { return fallback || null; } return date; } catch { return fallback || null; } } __name(parseDate, "parseDate"); function dateDiff(date1, date2 = /* @__PURE__ */ new Date(), unit = "days") { const d1 = date1 instanceof Date ? date1 : new Date(date1); const d2 = date2 instanceof Date ? date2 : new Date(date2); const diffMs = d1.getTime() - d2.getTime(); switch (unit) { case "milliseconds": return diffMs; case "seconds": return diffMs / 1e3; case "minutes": return diffMs / (1e3 * 60); case "hours": return diffMs / (1e3 * 60 * 60); case "days": return diffMs / (1e3 * 60 * 60 * 24); case "months": return (d1.getFullYear() - d2.getFullYear()) * 12 + d1.getMonth() - d2.getMonth(); case "years": return d1.getFullYear() - d2.getFullYear(); default: throw new Error(`Unsupported unit: ${unit}`); } } __name(dateDiff, "dateDiff"); function addToDate(date, amount, unit) { const d = date instanceof Date ? new Date(date) : new Date(date); switch (unit) { case "milliseconds": d.setMilliseconds(d.getMilliseconds() + amount); break; case "seconds": d.setSeconds(d.getSeconds() + amount); break; case "minutes": d.setMinutes(d.getMinutes() + amount); break; case "hours": d.setHours(d.getHours() + amount); break; case "days": d.setDate(d.getDate() + amount); break; case "months": { const origDate = d.getDate(); const origMonth = d.getMonth(); d.setDate(1); d.setMonth(origMonth + amount); const lastDay = new Date(d.getFullYear(), d.getMonth() + 1, 0).getDate(); d.setDate(Math.min(origDate, lastDay)); break; } case "years": d.setFullYear(d.getFullYear() + amount); break; default: throw new Error(`Unsupported unit: ${unit}`); } return d; } __name(addToDate, "addToDate"); function startOf(date, unit) { const d = date instanceof Date ? new Date(date) : new Date(date); switch (unit) { case "second": d.setMilliseconds(0); break; case "minute": d.setSeconds(0, 0); break; case "hour": d.setMinutes(0, 0, 0); break; case "day": d.setHours(0, 0, 0, 0); break; case "week": d.setHours(0, 0, 0, 0); d.setDate(d.getDate() - d.getDay()); break; case "month": d.setDate(1); d.setHours(0, 0, 0, 0); break; case "quarter": d.setMonth(Math.floor(d.getMonth() / 3) * 3, 1); d.setHours(0, 0, 0, 0); break; case "year": d.setMonth(0, 1); d.setHours(0, 0, 0, 0); break; default: throw new Error(`Unsupported unit: ${unit}`); } return d; } __name(startOf, "startOf"); function endOf(date, unit) { const d = date instanceof Date ? new Date(date) : new Date(date); switch (unit) { case "second": d.setMilliseconds(999); break; case "minute": d.setSeconds(59, 999); break; case "hour": d.setMinutes(59, 59, 999); break; case "day": d.setHours(23, 59, 59, 999); break; case "week": d.setDate(d.getDate() - d.getDay() + 6); d.setHours(23, 59, 59, 999); break; case "month": d.setMonth(d.getMonth() + 1, 0); d.setHours(23, 59, 59, 999); break; case "quarter": d.setMonth(Math.floor(d.getMonth() / 3) * 3 + 3, 0); d.setHours(23, 59, 59, 999); break; case "year": d.setMonth(11, 31); d.setHours(23, 59, 59, 999); break; default: throw new Error(`Unsupported unit: ${unit}`); } return d; } __name(endOf, "endOf"); function isBetween(date, start, end, inclusive = true) { const d = date instanceof Date ? date.getTime() : date; const s = start instanceof Date ? start.getTime() : start; const e = end instanceof Date ? end.getTime() : end; return inclusive ? d >= s && d <= e : d > s && d < e; } __name(isBetween, "isBetween"); function isLeapYear(year) { const y = year instanceof Date ? year.getFullYear() : year; return y % 4 === 0 && y % 100 !== 0 || y % 400 === 0; } __name(isLeapYear, "isLeapYear"); function daysInMonth(year, month) { if (year instanceof Date) { month = year.getMonth(); year = year.getFullYear(); } if (month === void 0) { throw new Error("Month is required when year is a number"); } return new Date(year, month + 1, 0).getDate(); } __name(daysInMonth, "daysInMonth"); function formatDuration(ms) { if (!Number.isFinite(ms) || ms <= 0) return "0ms"; const parts = []; const days = Math.floor(ms / (24 * 60 * 60 * 1e3)); if (days) { parts.push(`${days}d`); ms -= days * 24 * 60 * 60 * 1e3; } const hours = Math.floor(ms / (60 * 60 * 1e3)); if (hours) { parts.push(`${hours}h`); ms -= hours * 60 * 60 * 1e3; } const minutes = Math.floor(ms / (60 * 1e3)); if (minutes) { parts.push(`${minutes}m`); ms -= minutes * 60 * 1e3; } const seconds = Math.floor(ms / 1e3); if (seconds) { parts.push(`${seconds}s`); ms -= seconds * 1e3; } if (ms > 0) parts.push(`${ms}ms`); return parts.join(" "); } __name(formatDuration, "formatDuration"); function parseDuration(value) { const MAX_SAFE = Number.MAX_SAFE_INTEGER; if (typeof value === "number" && Number.isFinite(value)) return Math.min(value, MAX_SAFE); if (typeof value !== "string" || !value.trim()) return 0; if (/^\d+$/.test(value)) return Math.min(Number(value), MAX_SAFE); const val = value.replace(/\s+/g, "").toLowerCase(); const unitMap = { y: 31536e6, w: 6048e5, d: 864e5, h: 36e5, m: 6e4, s: 1e3, ms: 1 }; const unitKeys = Object.keys(unitMap).sort((a, b) => b.length - a.length); let total = 0; let i = 0; while (i < val.length) { const start = i; while (i < val.length && /\d/.test(val[i])) i++; if (start === i) { throw new Error(`Invalid duration at offset ${i} in "${value}"`); } const num = Number(val.slice(start, i)); if (!Number.isSafeInteger(num)) throw new Error(`Invalid number at offset ${start} in "${value}"`); const unit = unitKeys.find((u) => val.startsWith(u, i)); if (!unit) throw new Error(`Unknown unit at offset ${i} in "${value}"`); const add = num * unitMap[unit]; total = Math.min(total + add, MAX_SAFE); i += unit.length; } return total; } __name(parseDuration, "parseDuration"); function getDateFromDuration(input) { const ms = parseDuration(input); if (ms <= 0) { throw new Error(`Invalid duration: "${input}". Duration must be a positive value.`); } return new Date(Date.now() + ms); } __name(getDateFromDuration, "getDateFromDuration"); function isWeekend(date) { const d = date instanceof Date ? date : new Date(date); const day = d.getDay(); return day === 0 || day === 6; } __name(isWeekend, "isWeekend"); function isToday(date) { const d = date instanceof Date ? date : new Date(date); const today = /* @__PURE__ */ new Date(); return d.getFullYear() === today.getFullYear() && d.getMonth() === today.getMonth() && d.getDate() === today.getDate(); } __name(isToday, "isToday"); function isFuture(date) { const d = date instanceof Date ? date : new Date(date); return d.getTime() > Date.now(); } __name(isFuture, "isFuture"); function isPast(date) { const d = date instanceof Date ? date : new Date(date); return d.getTime() < Date.now(); } __name(isPast, "isPast"); function addDays(date, days) { const d = new Date(date instanceof Date ? date : new Date(date)); d.setDate(d.getDate() + days); return d; } __name(addDays, "addDays"); function addMonths(date, months) { const d = new Date(date instanceof Date ? date : new Date(date)); d.setMonth(d.getMonth() + months); return d; } __name(addMonths, "addMonths"); function addYears(date, years) { const d = new Date(date instanceof Date ? date : new Date(date)); d.setFullYear(d.getFullYear() + years); return d; } __name(addYears, "addYears"); function quarterOf(date) { const d = date instanceof Date ? date : new Date(date); return Math.floor(d.getMonth() / 3) + 1; } __name(quarterOf, "quarterOf"); function weekOfYear(date) { const d = new Date(date instanceof Date ? date : new Date(date)); d.setHours(0, 0, 0, 0); d.setDate(d.getDate() + 4 - (d.getDay() || 7)); const yearStart = new Date(d.getFullYear(), 0, 1); return Math.ceil(((d.getTime() - yearStart.getTime()) / 864e5 + 1) / 7); } __name(weekOfYear, "weekOfYear"); // src/date/date.builder.ts var DateBuilder = class _DateBuilder { static { __name(this, "DateBuilder"); } date; /** * Creates a new DateBuilder instance. * @param date - Initial date (default: current date/time) */ constructor(date) { if (date === void 0) { this.date = /* @__PURE__ */ new Date(); } else if (date instanceof Date) { this.date = new Date(date); } else if (typeof date === "number") { this.date = new Date(date); } else { this.date = new Date(date); } } /** * Create a DateBuilder from an existing date. * @param date - Date to build from */ static from(date) { return new _DateBuilder(date); } /** * Create a DateBuilder for the current moment. */ static now() { return new _DateBuilder(); } /** * Create a DateBuilder for a specific date. * @param year - Year * @param month - Month (1-12, not 0-11) * @param day - Day of month * @param hour - Hour (default: 0) * @param minute - Minute (default: 0) * @param second - Second (default: 0) * @param millisecond - Millisecond (default: 0) */ static of(year, month, day, hour = 0, minute = 0, second = 0, millisecond = 0) { const d = new Date(year, month - 1, day, hour, minute, second, millisecond); return new _DateBuilder(d); } /** * Parse a date string into a DateBuilder. * @param dateString - Date string to parse */ static parse(dateString) { return new _DateBuilder(new Date(dateString)); } /** * Create a DateBuilder from a duration string (e.g., "5m", "2h", "1d" from now). * @param duration - Duration string */ static fromDuration(duration) { return new _DateBuilder(getDateFromDuration(duration)); } /** * Clone this DateBuilder instance. */ clone() { return new _DateBuilder(this.date); } // ========== Setters (return new instance) ========== /** * Set the year. * @param year - Year to set */ year(year) { const newDate = new Date(this.date); newDate.setFullYear(year); return new _DateBuilder(newDate); } /** * Set the month (1-12, not 0-11). * @param month - Month to set (1-12) */ month(month) { const newDate = new Date(this.date); newDate.setMonth(month - 1); return new _DateBuilder(newDate); } /** * Set the day of the month. * @param day - Day to set */ day(day) { const newDate = new Date(this.date); newDate.setDate(day); return new _DateBuilder(newDate); } /** * Set the hour. * @param hour - Hour to set */ hour(hour) { const newDate = new Date(this.date); newDate.setHours(hour); return new _DateBuilder(newDate); } /** * Set the minute. * @param minute - Minute to set */ minute(minute) { const newDate = new Date(this.date); newDate.setMinutes(minute); return new _DateBuilder(newDate); } /** * Set the second. * @param second - Second to set */ second(second) { const newDate = new Date(this.date); newDate.setSeconds(second); return new _DateBuilder(newDate); } /** * Set the millisecond. * @param millisecond - Millisecond to set */ millisecond(millisecond) { const newDate = new Date(this.date); newDate.setMilliseconds(millisecond); return new _DateBuilder(newDate); } // ========== Addition/Subtraction Methods ========== /** * Add years to the date. * @param years - Number of years to add (can be negative) */ addYears(years) { return new _DateBuilder(addToDate(this.date, years, "years")); } /** * Add months to the date. * @param months - Number of months to add (can be negative) */ addMonths(months) { return new _DateBuilder(addToDate(this.date, months, "months")); } /** * Add days to the date. * @param days - Number of days to add (can be negative) */ addDays(days) { return new _DateBuilder(addToDate(this.date, days, "days")); } /** * Add hours to the date. * @param hours - Number of hours to add (can be negative) */ addHours(hours) { return new _DateBuilder(addToDate(this.date, hours, "hours")); } /** * Add minutes to the date. * @param minutes - Number of minutes to add (can be negative) */ addMinutes(minutes) { return new _DateBuilder(addToDate(this.date, minutes, "minutes")); } /** * Add seconds to the date. * @param seconds - Number of seconds to add (can be negative) */ addSeconds(seconds) { return new _DateBuilder(addToDate(this.date, seconds, "seconds")); } /** * Add milliseconds to the date. * @param milliseconds - Number of milliseconds to add (can be negative) */ addMilliseconds(milliseconds) { return new _DateBuilder(addToDate(this.date, milliseconds, "milliseconds")); } /** * Subtract years from the date. * @param years - Number of years to subtract */ subtractYears(years) { return this.addYears(-years); } /** * Subtract months from the date. * @param months - Number of months to subtract */ subtractMonths(months) { return this.addMonths(-months); } /** * Subtract days from the date. * @param days - Number of days to subtract */ subtractDays(days) { return this.addDays(-days); } /** * Subtract hours from the date. * @param hours - Number of hours to subtract */ subtractHours(hours) { return this.addHours(-hours); } /** * Subtract minutes from the date. * @param minutes - Number of minutes to subtract */ subtractMinutes(minutes) { return this.addMinutes(-minutes); } /** * Subtract seconds from the date. * @param seconds - Number of seconds to subtract */ subtractSeconds(seconds) { return this.addSeconds(-seconds); } // ========== Start/End of Period Methods ========== /** * Set to the start of the second. */ startOfSecond() { return new _DateBuilder(startOf(this.date, "second")); } /** * Set to the start of the minute. */ startOfMinute() { return new _DateBuilder(startOf(this.date, "minute")); } /** * Set to the start of the hour. */ startOfHour() { return new _DateBuilder(startOf(this.date, "hour")); } /** * Set to the start of the day (midnight). */ startOfDay() { return new _DateBuilder(startOf(this.date, "day")); } /** * Set to the start of the week (Sunday). */ startOfWeek() { return new _DateBuilder(startOf(this.date, "week")); } /** * Set to the start of the month. */ startOfMonth() { return new _DateBuilder(startOf(this.date, "month")); } /** * Set to the start of the quarter. */ startOfQuarter() { return new _DateBuilder(startOf(this.date, "quarter")); } /** * Set to the start of the year. */ startOfYear() { return new _DateBuilder(startOf(this.date, "year")); } /** * Set to the end of the second. */ endOfSecond() { return new _DateBuilder(endOf(this.date, "second")); } /** * Set to the end of the minute. */ endOfMinute() { return new _DateBuilder(endOf(this.date, "minute")); } /** * Set to the end of the hour. */ endOfHour() { return new _DateBuilder(endOf(this.date, "hour")); } /** * Set to the end of the day (23:59:59.999). */ endOfDay() { return new _DateBuilder(endOf(this.date, "day")); } /** * Set to the end of the week (Saturday). */ endOfWeek() { return new _DateBuilder(endOf(this.date, "week")); } /** * Set to the end of the month. */ endOfMonth() { return new _DateBuilder(endOf(this.date, "month")); } /** * Set to the end of the quarter. */ endOfQuarter() { return new _DateBuilder(endOf(this.date, "quarter")); } /** * Set to the end of the year. */ endOfYear() { return new _DateBuilder(endOf(this.date, "year")); } // ========== Comparison Methods ========== /** * Check if the date is before another date. * @param other - Date to compare with */ isBefore(other) { const otherDate = other instanceof _DateBuilder ? other.toDate() : other; return this.date.getTime() < otherDate.getTime(); } /** * Check if the date is after another date. * @param other - Date to compare with */ isAfter(other) { const otherDate = other instanceof _DateBuilder ? other.toDate() : other; return this.date.getTime() > otherDate.getTime(); } /** * Check if the date is the same as another date. * @param other - Date to compare with */ isSame(other) { const otherDate = other instanceof _DateBuilder ? other.toDate() : other; return this.date.getTime() === otherDate.getTime(); } /** * Check if the date is between two dates. * @param start - Start date * @param end - End date * @param inclusive - Whether to include boundaries */ isBetween(start, end, inclusive = true) { const startDate = start instanceof _DateBuilder ? start.toDate() : start; const endDate = end instanceof _DateBuilder ? end.toDate() : end; return isBetween(this.date, startDate, endDate, inclusive); } /** * Check if the date is today. */ isToday() { return isToday(this.date); } /** * Check if the date is in the future. */ isFuture() { return isFuture(this.date); } /** * Check if the date is in the past. */ isPast() { return isPast(this.date); } /** * Check if the date is on a weekend. */ isWeekend() { return isWeekend(this.date); } /** * Check if the year is a leap year. */ isLeapYear() { return isLeapYear(this.date); } // ========== Getters ========== /** * Get the year. */ getYear() { return this.date.getFullYear(); } /** * Get the month (1-12, not 0-11). */ getMonth() { return this.date.getMonth() + 1; } /** * Get the day of the month. */ getDay() { return this.date.getDate(); } /** * Get the day of the week (0-6, Sunday is 0). */ getDayOfWeek() { return this.date.getDay(); } /** * Get the hour. */ getHour() { return this.date.getHours(); } /** * Get the minute. */ getMinute() { return this.date.getMinutes(); } /** * Get the second. */ getSecond() { return this.date.getSeconds(); } /** * Get the millisecond. */ getMillisecond() { return this.date.getMilliseconds(); } /** * Get the quarter (1-4). */ getQuarter() { return quarterOf(this.date); } /** * Get the ISO week number. */ getWeekOfYear() { return weekOfYear(this.date); } /** * Get the number of days in the current month. */ getDaysInMonth() { return daysInMonth(this.date); } /** * Get the Unix timestamp in milliseconds. */ getTime() { return this.date.getTime(); } /** * Get the Unix timestamp in seconds. */ getUnixTimestamp() { return Math.floor(this.date.getTime() / 1e3); } // ========== Difference Methods ========== /** * Calculate the difference from another date. * @param other - Date to compare with * @param unit - Unit of time */ diff(other, unit = "milliseconds") { const otherDate = other instanceof _DateBuilder ? other.toDate() : other; return dateDiff(this.date, otherDate, unit); } /** * Get the difference from now. * @param unit - Unit of time */ diffFromNow(unit = "milliseconds") { return dateDiff(this.date, /* @__PURE__ */ new Date(), unit); } // ========== Formatting Methods ========== /** * Format the date. * @param options - Formatting options */ format(options) { if (typeof options === "string") { return formatDate(this.date, { format: options }); } return formatDate(this.date, options); } /** * Format as relative time (e.g., "5 minutes ago"). * @param locale - Locale for formatting */ formatRelative(locale) { return formatRelativeTime(this.date, /* @__PURE__ */ new Date(), locale); } /** * Format as ISO 8601 string. */ toISOString() { return this.date.toISOString(); } /** * Format as UTC string. */ toUTCString() { return this.date.toUTCString(); } /** * Format as date string. */ toDateString() { return this.date.toDateString(); } /** * Format as time string. */ toTimeString() { return this.date.toTimeString(); } /** * Format as locale string. */ toLocaleString(locales, options) { return this.date.toLocaleString(locales, options); } /** * Convert to JSON string. */ toJSON() { return this.date.toJSON(); } // ========== Conversion Methods ========== /** * Build and return the Date object. */ build() { return new Date(this.date); } /** * Get the Date object (alias for build). */ toDate() { return new Date(this.date); } /** * Get the internal Date object value. */ valueOf() { return this.date.valueOf(); } /** * Convert to string. */ toString() { return this.date.toString(); } }; exports.DateBuilder = DateBuilder; exports.addDays = addDays; exports.addMonths = addMonths; exports.addToDate = addToDate; exports.addYears = addYears; exports.dateDiff = dateDiff; exports.daysInMonth = daysInMonth; exports.endOf = endOf; exports.formatDate = formatDate; exports.formatDuration = formatDuration; exports.formatRelativeTime = formatRelativeTime; exports.getDateFromDuration = getDateFromDuration; exports.isBetween = isBetween; exports.isFuture = isFuture; exports.isLeapYear = isLeapYear; exports.isPast = isPast; exports.isToday = isToday; exports.isWeekend = isWeekend; exports.parseDate = parseDate; exports.parseDuration = parseDuration; exports.quarterOf = quarterOf; exports.startOf = startOf; exports.weekOfYear = weekOfYear;