140 lines
4.2 KiB
JavaScript
140 lines
4.2 KiB
JavaScript
// date-utils.js — Date handling utilities for the calendar
|
|
const monthAbbr = ['jan','feb','mar','apr','may','jun','jul','aug','sep','oct','nov','dec']
|
|
const DAY_MS = 86400000
|
|
const WEEK_MS = 7 * DAY_MS
|
|
|
|
/**
|
|
* Get ISO week information for a given date
|
|
* @param {Date} date - The date to get week info for
|
|
* @returns {Object} Object containing week number and year
|
|
*/
|
|
const isoWeekInfo = date => {
|
|
const d = new Date(Date.UTC(date.getFullYear(), date.getMonth(), date.getDate()))
|
|
const day = d.getUTCDay() || 7
|
|
d.setUTCDate(d.getUTCDate() + 4 - day)
|
|
const year = d.getUTCFullYear()
|
|
const yearStart = new Date(Date.UTC(year, 0, 1))
|
|
const diffDays = Math.floor((d - yearStart) / DAY_MS) + 1
|
|
return { week: Math.ceil(diffDays / 7), year }
|
|
}
|
|
|
|
/**
|
|
* Convert a Date object to a local date string (YYYY-MM-DD format)
|
|
* @param {Date} date - The date to convert (defaults to new Date())
|
|
* @returns {string} Date string in YYYY-MM-DD format
|
|
*/
|
|
function toLocalString(date = new Date()) {
|
|
const pad = n => String(Math.floor(Math.abs(n))).padStart(2, '0')
|
|
return `${date.getFullYear()}-${pad(date.getMonth() + 1)}-${pad(date.getDate())}`
|
|
}
|
|
|
|
/**
|
|
* Convert a local date string (YYYY-MM-DD) to a Date object
|
|
* @param {string} dateString - Date string in YYYY-MM-DD format
|
|
* @returns {Date} Date object
|
|
*/
|
|
function fromLocalString(dateString) {
|
|
const [year, month, day] = dateString.split('-').map(Number)
|
|
return new Date(year, month - 1, day)
|
|
}
|
|
|
|
/**
|
|
* Get the index of Monday for a given date (0-6, where Monday = 0)
|
|
* @param {Date} d - The date
|
|
* @returns {number} Monday index (0-6)
|
|
*/
|
|
const mondayIndex = d => (d.getDay() + 6) % 7
|
|
|
|
/**
|
|
* Pad a number with leading zeros to make it 2 digits
|
|
* @param {number} n - Number to pad
|
|
* @returns {string} Padded string
|
|
*/
|
|
const pad = n => String(n).padStart(2, '0')
|
|
|
|
/**
|
|
* Calculate number of days between two date strings (inclusive)
|
|
* @param {string} aStr - First date string (YYYY-MM-DD)
|
|
* @param {string} bStr - Second date string (YYYY-MM-DD)
|
|
* @returns {number} Number of days inclusive
|
|
*/
|
|
function daysInclusive(aStr, bStr) {
|
|
const a = fromLocalString(aStr)
|
|
const b = fromLocalString(bStr)
|
|
const A = new Date(a.getFullYear(), a.getMonth(), a.getDate()).getTime()
|
|
const B = new Date(b.getFullYear(), b.getMonth(), b.getDate()).getTime()
|
|
return Math.floor(Math.abs(B - A) / DAY_MS) + 1
|
|
}
|
|
|
|
/**
|
|
* Add days to a date string
|
|
* @param {string} str - Date string in YYYY-MM-DD format
|
|
* @param {number} n - Number of days to add (can be negative)
|
|
* @returns {string} New date string
|
|
*/
|
|
function addDaysStr(str, n) {
|
|
const d = fromLocalString(str)
|
|
d.setDate(d.getDate() + n)
|
|
return toLocalString(d)
|
|
}
|
|
|
|
/**
|
|
* Get localized weekday names starting from Monday
|
|
* @returns {Array<string>} Array of localized weekday names
|
|
*/
|
|
function getLocalizedWeekdayNames() {
|
|
const res = []
|
|
const base = new Date(2025, 0, 6) // A Monday
|
|
for (let i = 0; i < 7; i++) {
|
|
const d = new Date(base)
|
|
d.setDate(base.getDate() + i)
|
|
res.push(d.toLocaleDateString(undefined, { weekday: 'short' }))
|
|
}
|
|
return res
|
|
}
|
|
|
|
/**
|
|
* Get localized month name
|
|
* @param {number} idx - Month index (0-11)
|
|
* @param {boolean} short - Whether to return short name
|
|
* @returns {string} Localized month name
|
|
*/
|
|
function getLocalizedMonthName(idx, short = false) {
|
|
const d = new Date(2025, idx, 1)
|
|
return d.toLocaleDateString(undefined, { month: short ? 'short' : 'long' })
|
|
}
|
|
|
|
/**
|
|
* Format a date range for display
|
|
* @param {Date} startDate - Start date
|
|
* @param {Date} endDate - End date
|
|
* @returns {string} Formatted date range string
|
|
*/
|
|
function formatDateRange(startDate, endDate) {
|
|
if (toLocalString(startDate) === toLocalString(endDate)) return toLocalString(startDate)
|
|
const startISO = toLocalString(startDate)
|
|
const endISO = toLocalString(endDate)
|
|
const [sy, sm] = startISO.split('-')
|
|
const [ey, em, ed] = endISO.split('-')
|
|
if (sy === ey && sm === em) return `${startISO}/${ed}`
|
|
if (sy === ey) return `${startISO}/${em}-${ed}`
|
|
return `${startISO}/${endISO}`
|
|
}
|
|
|
|
// Export all functions and constants
|
|
export {
|
|
monthAbbr,
|
|
DAY_MS,
|
|
WEEK_MS,
|
|
isoWeekInfo,
|
|
toLocalString,
|
|
fromLocalString,
|
|
mondayIndex,
|
|
pad,
|
|
daysInclusive,
|
|
addDaysStr,
|
|
getLocalizedWeekdayNames,
|
|
getLocalizedMonthName,
|
|
formatDateRange
|
|
}
|