Display national holidays on the calendar.

This commit is contained in:
Leo Vasanko
2025-08-23 14:03:48 -06:00
parent 916d1d100a
commit 90dcdec386
8 changed files with 707 additions and 51 deletions

179
src/utils/holidays.js Normal file
View File

@@ -0,0 +1,179 @@
// holidays.js — Holiday utilities using date-holidays package
import Holidays from 'date-holidays'
let holidaysInstance = null
let currentCountry = null
let currentState = null
let currentRegion = null
let holidayCache = new Map()
let yearCache = new Map()
/**
* Initialize holidays for a specific country/region
* @param {string} country - Country code (e.g., 'US', 'GB', 'DE')
* @param {string} [state] - State/province code (e.g., 'CA' for California)
* @param {string} [region] - Region code
*/
export function initializeHolidays(country, state = null, region = null) {
if (!country) {
console.warn('No country provided for holiday initialization')
holidaysInstance = null
return false
}
try {
holidaysInstance = new Holidays(country, state, region)
currentCountry = country
currentState = state
currentRegion = region
holidayCache.clear()
yearCache.clear()
return true
} catch (error) {
console.warn('Failed to initialize holidays for', country, state, region, error)
holidaysInstance = null
return false
}
}
/**
* Get holidays for a specific year
* @param {number} year - The year to get holidays for
* @returns {Array} Array of holiday objects
*/
export function getHolidaysForYear(year) {
if (!holidaysInstance) {
return []
}
if (yearCache.has(year)) {
return yearCache.get(year)
}
try {
const holidays = holidaysInstance.getHolidays(year)
yearCache.set(year, holidays)
return holidays
} catch (error) {
console.warn('Failed to get holidays for year', year, error)
return []
}
}
/**
* Get holiday for a specific date
* @param {string|Date} date - Date in YYYY-MM-DD format or Date object
* @returns {Object|null} Holiday object or null if no holiday
*/
export function getHolidayForDate(date) {
if (!holidaysInstance) {
return null
}
const cacheKey = typeof date === 'string' ? date : date.toISOString().split('T')[0]
if (holidayCache.has(cacheKey)) {
return holidayCache.get(cacheKey)
}
try {
let dateObj
if (typeof date === 'string') {
const [year, month, day] = date.split('-').map(Number)
dateObj = new Date(year, month - 1, day)
} else {
dateObj = date
}
const year = dateObj.getFullYear()
const holidays = getHolidaysForYear(year)
const holiday = holidays.find((h) => {
const holidayDate = new Date(h.date)
return (
holidayDate.getFullYear() === dateObj.getFullYear() &&
holidayDate.getMonth() === dateObj.getMonth() &&
holidayDate.getDate() === dateObj.getDate()
)
})
const result = holiday || null
holidayCache.set(cacheKey, result)
return result
} catch (error) {
console.warn('Failed to get holiday for date', date, error)
return null
}
}
/**
* Check if a date is a holiday
* @param {string|Date} date - Date in YYYY-MM-DD format or Date object
* @returns {boolean} True if the date is a holiday
*/
export function isHoliday(date) {
return getHolidayForDate(date) !== null
}
/**
* Get available countries for holidays
* @returns {Array} Array of country codes
*/
export function getAvailableCountries() {
try {
const holidays = new Holidays()
const countries = holidays.getCountries()
// The getCountries method might return an object, convert to array of keys
if (countries && typeof countries === 'object') {
return Array.isArray(countries) ? countries : Object.keys(countries)
}
return ['US', 'GB', 'DE', 'FR', 'CA', 'AU'] // Fallback
} catch (error) {
console.warn('Failed to get available countries', error)
return ['US', 'GB', 'DE', 'FR', 'CA', 'AU'] // Fallback to common countries
}
}
/**
* Get available states/regions for a country
* @param {string} country - Country code
* @returns {Array} Array of state/region codes
*/
export function getAvailableStates(country) {
try {
if (!country) return []
const holidays = new Holidays()
const states = holidays.getStates(country)
// The getStates method might return an object, convert to array of keys
if (states && typeof states === 'object') {
return Array.isArray(states) ? states : Object.keys(states)
}
return []
} catch (error) {
console.warn('Failed to get available states for', country, error)
return []
}
}
/**
* Get holiday configuration info
* @returns {Object} Current holiday configuration
*/
export function getHolidayConfig() {
return {
country: currentCountry,
state: currentState,
region: currentRegion,
initialized: !!holidaysInstance,
}
}
// Initialize with US holidays by default
initializeHolidays('US')