docs-site/typedoc-src/utils/date.tstypescript
/**
* Date and time utility functions
*
* @remarks
* Provides common date manipulation and formatting utilities.
*
* @packageDocumentation
*/
/**
* Date format options
*
* @public
*/
export interface DateFormatOptions {
/**
* Locale for formatting
* @defaultValue 'en-US'
*/
locale?: string
/**
* Timezone for display
* @defaultValue undefined (local timezone)
*/
timezone?: string
}
/**
* Time units in milliseconds
*
* @public
*/
export const TIME_UNITS = {
/** Milliseconds in a second */
SECOND: 1000,
/** Milliseconds in a minute */
MINUTE: 60 * 1000,
/** Milliseconds in an hour */
HOUR: 60 * 60 * 1000,
/** Milliseconds in a day */
DAY: 24 * 60 * 60 * 1000,
/** Milliseconds in a week */
WEEK: 7 * 24 * 60 * 60 * 1000,
/** Average milliseconds in a month (30 days) */
MONTH: 30 * 24 * 60 * 60 * 1000,
/** Average milliseconds in a year (365 days) */
YEAR: 365 * 24 * 60 * 60 * 1000,
} as const
/**
* Formats a date using Intl.DateTimeFormat
*
* @param date - Date to format
* @param format - Format style or options
* @param options - Additional options
* @returns Formatted date string
*
* @example
* ```typescript
* formatDate(new Date(), 'short') // '12/15/2024'
* formatDate(new Date(), 'long') // 'December 15, 2024'
* formatDate(new Date(), 'full', { locale: 'de-DE' }) // 'Sonntag, 15. Dezember 2024'
* ```
*
* @public
*/
export function formatDate(
date: Date | number | string,
format: 'short' | 'medium' | 'long' | 'full' = 'medium',
options: DateFormatOptions = {}
): string {
const d = date instanceof Date ? date : new Date(date)
const { locale = 'en-US', timezone } = options
const formatOptions: Intl.DateTimeFormatOptions = {
dateStyle: format,
timeZone: timezone,
}
return new Intl.DateTimeFormat(locale, formatOptions).format(d)
}
/**
* Formats a time using Intl.DateTimeFormat
*
* @param date - Date to format
* @param format - Format style
* @param options - Additional options
* @returns Formatted time string
*
* @example
* ```typescript
* formatTime(new Date(), 'short') // '2:30 PM'
* formatTime(new Date(), 'medium') // '2:30:45 PM'
* formatTime(new Date(), 'long') // '2:30:45 PM EST'
* ```
*
* @public
*/
export function formatTime(
date: Date | number | string,
format: 'short' | 'medium' | 'long' = 'short',
options: DateFormatOptions = {}
): string {
const d = date instanceof Date ? date : new Date(date)
const { locale = 'en-US', timezone } = options
const formatOptions: Intl.DateTimeFormatOptions = {
timeStyle: format,
timeZone: timezone,
}
return new Intl.DateTimeFormat(locale, formatOptions).format(d)
}
/**
* Formats a date and time together
*
* @param date - Date to format
* @param dateFormat - Date format style
* @param timeFormat - Time format style
* @param options - Additional options
* @returns Formatted datetime string
*
* @example
* ```typescript
* formatDateTime(new Date(), 'short', 'short')
* // '12/15/2024, 2:30 PM'
* ```
*
* @public
*/
export function formatDateTime(
date: Date | number | string,
dateFormat: 'short' | 'medium' | 'long' | 'full' = 'medium',
timeFormat: 'short' | 'medium' | 'long' = 'short',
options: DateFormatOptions = {}
): string {
const d = date instanceof Date ? date : new Date(date)
const { locale = 'en-US', timezone } = options
const formatOptions: Intl.DateTimeFormatOptions = {
dateStyle: dateFormat,
timeStyle: timeFormat,
timeZone: timezone,
}
return new Intl.DateTimeFormat(locale, formatOptions).format(d)
}
/**
* Returns a relative time string (e.g., "3 days ago")
*
* @param date - Date to compare
* @param baseDate - Base date for comparison (default: now)
* @param options - Format options
* @returns Relative time string
*
* @example
* ```typescript
* const yesterday = new Date(Date.now() - 86400000)
* getRelativeTime(yesterday) // '1 day ago'
*
* const nextWeek = new Date(Date.now() + 7 * 86400000)
* getRelativeTime(nextWeek) // 'in 1 week'
* ```
*
* @public
*/
export function getRelativeTime(
date: Date | number | string,
baseDate: Date = new Date(),
options: DateFormatOptions = {}
): string {
const d = date instanceof Date ? date : new Date(date)
const { locale = 'en-US' } = options
const diff = d.getTime() - baseDate.getTime()
const absDiff = Math.abs(diff)
let value: number
let unit: Intl.RelativeTimeFormatUnit
if (absDiff < TIME_UNITS.MINUTE) {
value = Math.round(diff / TIME_UNITS.SECOND)
unit = 'second'
} else if (absDiff < TIME_UNITS.HOUR) {
value = Math.round(diff / TIME_UNITS.MINUTE)
unit = 'minute'
} else if (absDiff < TIME_UNITS.DAY) {
value = Math.round(diff / TIME_UNITS.HOUR)
unit = 'hour'
} else if (absDiff < TIME_UNITS.WEEK) {
value = Math.round(diff / TIME_UNITS.DAY)
unit = 'day'
} else if (absDiff < TIME_UNITS.MONTH) {
value = Math.round(diff / TIME_UNITS.WEEK)
unit = 'week'
} else if (absDiff < TIME_UNITS.YEAR) {
value = Math.round(diff / TIME_UNITS.MONTH)
unit = 'month'
} else {
value = Math.round(diff / TIME_UNITS.YEAR)
unit = 'year'
}
const rtf = new Intl.RelativeTimeFormat(locale, { numeric: 'auto' })
return rtf.format(value, unit)
}
/**
* Adds a duration to a date
*
* @param date - Base date
* @param amount - Amount to add
* @param unit - Unit of time
* @returns New date with added duration
*
* @example
* ```typescript
* const now = new Date()
* addTime(now, 1, 'day') // Tomorrow
* addTime(now, -1, 'week') // Last week
* addTime(now, 2, 'hours') // 2 hours from now
* ```
*
* @public
*/
export function addTime(
date: Date,
amount: number,
unit: 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months' | 'years'
): Date {
const result = new Date(date)
switch (unit) {
case 'seconds':
result.setSeconds(result.getSeconds() + amount)
break
case 'minutes':
result.setMinutes(result.getMinutes() + amount)
break
case 'hours':
result.setHours(result.getHours() + amount)
break
case 'days':
result.setDate(result.getDate() + amount)
break
case 'weeks':
result.setDate(result.getDate() + amount * 7)
break
case 'months':
result.setMonth(result.getMonth() + amount)
break
case 'years':
result.setFullYear(result.getFullYear() + amount)
break
}
return result
}
/**
* Gets the start of a time period
*
* @param date - Input date
* @param unit - Time unit
* @returns Start of the period
*
* @example
* ```typescript
* startOf(new Date('2024-03-15 14:30:00'), 'day')
* // 2024-03-15 00:00:00
*
* startOf(new Date('2024-03-15'), 'month')
* // 2024-03-01 00:00:00
* ```
*
* @public
*/
export function startOf(date: Date, unit: 'day' | 'week' | 'month' | 'year'): Date {
const result = new Date(date)
switch (unit) {
case 'day':
result.setHours(0, 0, 0, 0)
break
case 'week':
result.setHours(0, 0, 0, 0)
result.setDate(result.getDate() - result.getDay())
break
case 'month':
result.setHours(0, 0, 0, 0)
result.setDate(1)
break
case 'year':
result.setHours(0, 0, 0, 0)
result.setMonth(0, 1)
break
}
return result
}
/**
* Gets the end of a time period
*
* @param date - Input date
* @param unit - Time unit
* @returns End of the period
*
* @example
* ```typescript
* endOf(new Date('2024-03-15'), 'day')
* // 2024-03-15 23:59:59.999
*
* endOf(new Date('2024-03-15'), 'month')
* // 2024-03-31 23:59:59.999
* ```
*
* @public
*/
export function endOf(date: Date, unit: 'day' | 'week' | 'month' | 'year'): Date {
const result = new Date(date)
switch (unit) {
case 'day':
result.setHours(23, 59, 59, 999)
break
case 'week':
result.setHours(23, 59, 59, 999)
result.setDate(result.getDate() + (6 - result.getDay()))
break
case 'month':
result.setMonth(result.getMonth() + 1, 0)
result.setHours(23, 59, 59, 999)
break
case 'year':
result.setMonth(11, 31)
result.setHours(23, 59, 59, 999)
break
}
return result
}
/**
* Checks if two dates are on the same day
*
* @param date1 - First date
* @param date2 - Second date
* @returns True if dates are on the same day
*
* @example
* ```typescript
* isSameDay(new Date('2024-03-15 10:00'), new Date('2024-03-15 22:00')) // true
* isSameDay(new Date('2024-03-15'), new Date('2024-03-16')) // false
* ```
*
* @public
*/
export function isSameDay(date1: Date, date2: Date): boolean {
return (
date1.getFullYear() === date2.getFullYear() &&
date1.getMonth() === date2.getMonth() &&
date1.getDate() === date2.getDate()
)
}
/**
* Checks if a date is between two other dates
*
* @param date - Date to check
* @param start - Start of range
* @param end - End of range
* @param inclusive - Whether to include boundaries
* @returns True if date is in range
*
* @example
* ```typescript
* const date = new Date('2024-03-15')
* const start = new Date('2024-03-01')
* const end = new Date('2024-03-31')
*
* isBetween(date, start, end) // true
* ```
*
* @public
*/
export function isBetween(date: Date, start: Date, end: Date, inclusive = true): boolean {
const time = date.getTime()
const startTime = start.getTime()
const endTime = end.getTime()
if (inclusive) {
return time >= startTime && time <= endTime
}
return time > startTime && time < endTime
}
/**
* Gets the difference between two dates in a specific unit
*
* @param date1 - First date
* @param date2 - Second date
* @param unit - Unit of measurement
* @returns Difference in the specified unit
*
* @example
* ```typescript
* const start = new Date('2024-01-01')
* const end = new Date('2024-03-15')
*
* dateDiff(end, start, 'days') // 74
* dateDiff(end, start, 'months') // 2.5 (approximate)
* ```
*
* @public
*/
export function dateDiff(
date1: Date,
date2: Date,
unit: 'seconds' | 'minutes' | 'hours' | 'days' | 'weeks' | 'months' | 'years'
): number {
const diff = date1.getTime() - date2.getTime()
switch (unit) {
case 'seconds':
return diff / TIME_UNITS.SECOND
case 'minutes':
return diff / TIME_UNITS.MINUTE
case 'hours':
return diff / TIME_UNITS.HOUR
case 'days':
return diff / TIME_UNITS.DAY
case 'weeks':
return diff / TIME_UNITS.WEEK
case 'months':
return diff / TIME_UNITS.MONTH
case 'years':
return diff / TIME_UNITS.YEAR
default:
return diff
}
}
/**
* Parses an ISO 8601 duration string
*
* @param duration - ISO 8601 duration string
* @returns Duration in milliseconds
*
* @example
* ```typescript
* parseISODuration('PT1H30M') // 5400000 (1.5 hours in ms)
* parseISODuration('P1D') // 86400000 (1 day in ms)
* parseISODuration('P1Y2M3D') // ~36720000000
* ```
*
* @public
*/
export function parseISODuration(duration: string): number {
const regex = /P(?:(\d+)Y)?(?:(\d+)M)?(?:(\d+)D)?(?:T(?:(\d+)H)?(?:(\d+)M)?(?:(\d+)S)?)?/
const matches = duration.match(regex)
if (!matches) {
throw new Error(`Invalid ISO 8601 duration: ${duration}`)
}
const [, years, months, days, hours, minutes, seconds] = matches
let ms = 0
if (years) ms += parseInt(years) * TIME_UNITS.YEAR
if (months) ms += parseInt(months) * TIME_UNITS.MONTH
if (days) ms += parseInt(days) * TIME_UNITS.DAY
if (hours) ms += parseInt(hours) * TIME_UNITS.HOUR
if (minutes) ms += parseInt(minutes) * TIME_UNITS.MINUTE
if (seconds) ms += parseInt(seconds) * TIME_UNITS.SECOND
return ms
}
/**
* Formats a duration in milliseconds to a human-readable string
*
* @param ms - Duration in milliseconds
* @param options - Formatting options
* @returns Formatted duration string
*
* @example
* ```typescript
* formatDuration(3661000) // '1h 1m 1s'
* formatDuration(86400000) // '1d'
* formatDuration(3600000, { verbose: true }) // '1 hour'
* ```
*
* @public
*/
export function formatDuration(ms: number, options: { verbose?: boolean } = {}): string {
const { verbose = false } = options
const units = [
{ value: TIME_UNITS.DAY, short: 'd', long: 'day' },
{ value: TIME_UNITS.HOUR, short: 'h', long: 'hour' },
{ value: TIME_UNITS.MINUTE, short: 'm', long: 'minute' },
{ value: TIME_UNITS.SECOND, short: 's', long: 'second' },
]
const parts: string[] = []
let remaining = ms
for (const unit of units) {
const count = Math.floor(remaining / unit.value)
if (count > 0) {
if (verbose) {
parts.push(`${count} ${unit.long}${count !== 1 ? 's' : ''}`)
} else {
parts.push(`${count}${unit.short}`)
}
remaining %= unit.value
}
}
return parts.length > 0 ? parts.join(' ') : verbose ? '0 seconds' : '0s'
}