UNPKG

@tongziyang/uni-calendar-plugin

Version:

A comprehensive calendar plugin for uniapp with support for Gregorian and Lunar calendars, date selection, hotel booking, check-in functionality, and more.

597 lines (532 loc) 19 kB
import { DateInfo } from '../types'; import { solarToLunar, getHolidayInfo } from './lunar'; // 日期选项接口 export interface DateOptions { firstDayOfWeek?: number; selectedDate?: Date | null; rangeStart?: Date | null; rangeEnd?: Date | null; priceData?: Record<string, number>; checkInData?: Record<string, boolean>; disabledDate?: ((date: Date) => boolean) | null; } /** * 格式化日期为字符串 * @param date 日期对象 * @param format 格式字符串 * @returns 格式化后的日期字符串 */ export function formatDate(date: Date, format: string = 'YYYY-MM-DD'): string { if (!date || !(date instanceof Date) || isNaN(date.getTime())) { console.warn('Invalid date provided to formatDate'); return ''; } const year = date.getFullYear(); const month = date.getMonth() + 1; const day = date.getDate(); const hours = date.getHours(); const minutes = date.getMinutes(); const seconds = date.getSeconds(); return format .replace(/YYYY/g, year.toString()) .replace(/YY/g, (year % 100).toString().padStart(2, '0')) .replace(/MM/g, month.toString().padStart(2, '0')) .replace(/M/g, month.toString()) .replace(/DD/g, day.toString().padStart(2, '0')) .replace(/D/g, day.toString()) .replace(/HH/g, hours.toString().padStart(2, '0')) .replace(/H/g, hours.toString()) .replace(/mm/g, minutes.toString().padStart(2, '0')) .replace(/m/g, minutes.toString()) .replace(/ss/g, seconds.toString().padStart(2, '0')) .replace(/s/g, seconds.toString()); } /** * 解析日期字符串为Date对象 * @param dateStr 日期字符串 * @returns Date对象 */ export function parseDate(dateStr: string): Date { if (!dateStr) { console.warn('Empty date string provided to parseDate'); return new Date(); } // 尝试解析多种格式的日期字符串 const date = new Date(dateStr); if (!isNaN(date.getTime())) { return date; } // 尝试解析 YYYY-MM-DD 格式 const parts = dateStr.split(/[-\/]/); if (parts.length === 3) { const year = parseInt(parts[0], 10); const month = parseInt(parts[1], 10) - 1; const day = parseInt(parts[2], 10); const newDate = new Date(year, month, day); if (!isNaN(newDate.getTime())) { return newDate; } } // 无法解析,返回当前日期 console.warn(`Unable to parse date string: ${dateStr}, returning current date`); return new Date(); } /** * 获取两个日期之间的天数 * @param start 开始日期 * @param end 结束日期 * @returns 天数 */ export function getDaysBetween(start: Date, end: Date): number { if (!start || !end || !(start instanceof Date) || !(end instanceof Date) || isNaN(start.getTime()) || isNaN(end.getTime())) { console.warn('Invalid date(s) provided to getDaysBetween'); return 0; } const startDate = new Date(start.getFullYear(), start.getMonth(), start.getDate()); const endDate = new Date(end.getFullYear(), end.getMonth(), end.getDate()); const diff = endDate.getTime() - startDate.getTime(); return Math.round(diff / (1000 * 60 * 60 * 24)); } /** * 判断两个日期是否是同一天 * @param date1 日期1 * @param date2 日期2 * @returns 是否同一天 */ export function isSameDay(date1: Date | null, date2: Date | null): boolean { if (!date1 || !date2 || !(date1 instanceof Date) || !(date2 instanceof Date) || isNaN(date1.getTime()) || isNaN(date2.getTime())) { return false; } return ( date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() && date1.getDate() === date2.getDate() ); } /** * 判断两个日期是否是同一个月 * @param date1 日期1 * @param date2 日期2 * @returns 是否同一个月 */ export function isSameMonth(date1: Date | null, date2: Date | null): boolean { if (!date1 || !date2 || !(date1 instanceof Date) || !(date2 instanceof Date) || isNaN(date1.getTime()) || isNaN(date2.getTime())) { return false; } return ( date1.getFullYear() === date2.getFullYear() && date1.getMonth() === date2.getMonth() ); } /** * 判断日期是否在日期范围内 * @param date 日期 * @param start 开始日期 * @param end 结束日期 * @returns 是否在范围内 */ export function isDateInRange(date: Date, start: Date, end: Date): boolean { if (!date || !start || !end || !(date instanceof Date) || !(start instanceof Date) || !(end instanceof Date) || isNaN(date.getTime()) || isNaN(start.getTime()) || isNaN(end.getTime())) { return false; } const targetDate = new Date(date.getFullYear(), date.getMonth(), date.getDate()).getTime(); const startDate = new Date(start.getFullYear(), start.getMonth(), start.getDate()).getTime(); const endDate = new Date(end.getFullYear(), end.getMonth(), end.getDate()).getTime(); return targetDate >= startDate && targetDate <= endDate; } /** * 获取某月的第一天 * @param year 年 * @param month 月(0-11) * @returns 第一天的Date对象 */ export function getFirstDayOfMonth(year: number, month: number): Date { return new Date(year, month, 1); } /** * 获取某月的最后一天 * @param year 年 * @param month 月(0-11) * @returns 最后一天的Date对象 */ export function getLastDayOfMonth(year: number, month: number): Date { return new Date(year, month + 1, 0); } /** * 获取某月的天数 * @param year 年 * @param month 月(0-11) * @returns 天数 */ export function getDaysInMonth(year: number, month: number): number { return new Date(year, month + 1, 0).getDate(); } /** * 获取星期几的名称数组 * @param firstDayOfWeek 一周的第一天(0表示周日,1表示周一) * @param abbreviated 是否使用缩写 * @returns 星期几的名称数组 */ export function getWeekDayNames(firstDayOfWeek: number = 0, abbreviated: boolean = true): string[] { const weekDays = abbreviated ? ['日', '一', '二', '三', '四', '五', '六'] : ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']; const result = [...weekDays]; for (let i = 0; i < firstDayOfWeek; i++) { result.push(result.shift()!); } return result; } /** * 获取某个日期的详细信息 * @param date 日期 * @param options 选项 * @returns 日期信息 */ export function getDayInfo(date: Date, options: DateOptions = {}): DateInfo { if (!date || !(date instanceof Date) || isNaN(date.getTime())) { console.warn('Invalid date provided to getDayInfo'); return {} as DateInfo; } const { selectedDate = null, priceData = {}, checkInData = {}, disabledDate = null } = options; const today = new Date(); const dateStr = formatDate(date, 'YYYY-MM-DD'); const lunarInfo = solarToLunar(date); const holidayInfo = getHolidayInfo(date); return { date, day: date.getDate(), month: date.getMonth(), year: date.getFullYear(), isCurrentMonth: true, // 默认为当前月 isToday: isSameDay(date, today), isSelected: selectedDate ? isSameDay(date, selectedDate) : false, isInRange: false, // 默认不在范围内 isRangeStart: false, isRangeEnd: false, isWeekend: date.getDay() === 0 || date.getDay() === 6, isHoliday: holidayInfo.isHoliday !== undefined ? holidayInfo.isHoliday : !!holidayInfo.name, isRestDay: !!holidayInfo.isRestDay, isHolidayPeriod: !!holidayInfo.isHolidayPeriod, isCheckedIn: !!checkInData[dateStr], isDisabled: disabledDate ? disabledDate(date) : false, price: priceData[dateStr], lunarDay: lunarInfo?.lunarDay, lunarMonth: lunarInfo?.lunarMonth, lunarYear: lunarInfo?.lunarYear, lunarFestival: lunarInfo?.lunarFestival, solarFestival: lunarInfo?.solarFestival, solarTerm: lunarInfo?.solarTerm, }; } /** * 获取某月的所有日期信息 * @param year 年 * @param month 月(0-11) * @param options 选项 * @returns 日期信息数组 */ export function getMonthDays(year: number, month: number, options: DateOptions = {}): DateInfo[] { const { firstDayOfWeek = 0, selectedDate = null, rangeStart = null, rangeEnd = null, priceData = {}, checkInData = {}, disabledDate = null } = options; const today = new Date(); const firstDay = getFirstDayOfMonth(year, month); const lastDay = getLastDayOfMonth(year, month); const daysInMonth = getDaysInMonth(year, month); // 计算上个月需要显示的天数 let prevMonthDays = firstDay.getDay() - firstDayOfWeek; if (prevMonthDays < 0) prevMonthDays += 7; // 计算下个月需要显示的天数 const totalDaysToShow = Math.ceil((daysInMonth + prevMonthDays) / 7) * 7; const nextMonthDays = totalDaysToShow - daysInMonth - prevMonthDays; // 获取上个月的最后几天 const prevMonth = month === 0 ? 11 : month - 1; const prevMonthYear = month === 0 ? year - 1 : year; const daysInPrevMonth = getDaysInMonth(prevMonthYear, prevMonth); // 获取下个月的前几天 const nextMonth = month === 11 ? 0 : month + 1; const nextMonthYear = month === 11 ? year + 1 : year; const days: DateInfo[] = []; // 添加上个月的日期 for (let i = 0; i < prevMonthDays; i++) { const day = daysInPrevMonth - prevMonthDays + i + 1; const date = new Date(prevMonthYear, prevMonth, day); const dateStr = formatDate(date, 'YYYY-MM-DD'); const lunarInfo = solarToLunar(date); const holidayInfo = getHolidayInfo(date); days.push({ date, day, month: prevMonth, year: prevMonthYear, isCurrentMonth: false, isToday: isSameDay(date, today), isSelected: selectedDate ? isSameDay(date, selectedDate) : false, isInRange: rangeStart && rangeEnd ? isDateInRange(date, rangeStart, rangeEnd) : false, isRangeStart: rangeStart ? isSameDay(date, rangeStart) : false, isRangeEnd: rangeEnd ? isSameDay(date, rangeEnd) : false, isWeekend: date.getDay() === 0 || date.getDay() === 6, isHoliday: holidayInfo.isHoliday !== undefined ? holidayInfo.isHoliday : !!holidayInfo.name, isRestDay: !!holidayInfo.isRestDay, isHolidayPeriod: !!holidayInfo.isHolidayPeriod, isCheckedIn: !!checkInData[dateStr], isDisabled: disabledDate ? disabledDate(date) : false, price: priceData[dateStr], lunarDay: lunarInfo?.lunarDay, lunarMonth: lunarInfo?.lunarMonth, lunarYear: lunarInfo?.lunarYear, lunarFestival: lunarInfo?.lunarFestival, solarFestival: lunarInfo?.solarFestival, solarTerm: lunarInfo?.solarTerm, }); } // 添加当前月的日期 for (let i = 1; i <= daysInMonth; i++) { const date = new Date(year, month, i); const dateStr = formatDate(date, 'YYYY-MM-DD'); const lunarInfo = solarToLunar(date); const holidayInfo = getHolidayInfo(date); days.push({ date, day: i, month, year, isCurrentMonth: true, isToday: isSameDay(date, today), isSelected: selectedDate ? isSameDay(date, selectedDate) : false, isInRange: rangeStart && rangeEnd ? isDateInRange(date, rangeStart, rangeEnd) : false, isRangeStart: rangeStart ? isSameDay(date, rangeStart) : false, isRangeEnd: rangeEnd ? isSameDay(date, rangeEnd) : false, isWeekend: date.getDay() === 0 || date.getDay() === 6, isHoliday: holidayInfo.isHoliday !== undefined ? holidayInfo.isHoliday : !!holidayInfo.name, isRestDay: !!holidayInfo.isRestDay, isHolidayPeriod: !!holidayInfo.isHolidayPeriod, isCheckedIn: !!checkInData[dateStr], isDisabled: disabledDate ? disabledDate(date) : false, price: priceData[dateStr], lunarDay: lunarInfo?.lunarDay, lunarMonth: lunarInfo?.lunarMonth, lunarYear: lunarInfo?.lunarYear, lunarFestival: lunarInfo?.lunarFestival, solarFestival: lunarInfo?.solarFestival, solarTerm: lunarInfo?.solarTerm, }); } // 添加下个月的日期 for (let i = 1; i <= nextMonthDays; i++) { const date = new Date(nextMonthYear, nextMonth, i); const dateStr = formatDate(date, 'YYYY-MM-DD'); const lunarInfo = solarToLunar(date); const holidayInfo = getHolidayInfo(date); days.push({ date, day: i, month: nextMonth, year: nextMonthYear, isCurrentMonth: false, isToday: isSameDay(date, today), isSelected: selectedDate ? isSameDay(date, selectedDate) : false, isInRange: rangeStart && rangeEnd ? isDateInRange(date, rangeStart, rangeEnd) : false, isRangeStart: rangeStart ? isSameDay(date, rangeStart) : false, isRangeEnd: rangeEnd ? isSameDay(date, rangeEnd) : false, isWeekend: date.getDay() === 0 || date.getDay() === 6, isHoliday: holidayInfo.isHoliday !== undefined ? holidayInfo.isHoliday : !!holidayInfo.name, isRestDay: !!holidayInfo.isRestDay, isHolidayPeriod: !!holidayInfo.isHolidayPeriod, isCheckedIn: !!checkInData[dateStr], isDisabled: disabledDate ? disabledDate(date) : false, price: priceData[dateStr], lunarDay: lunarInfo?.lunarDay, lunarMonth: lunarInfo?.lunarMonth, lunarYear: lunarInfo?.lunarYear, lunarFestival: lunarInfo?.lunarFestival, solarFestival: lunarInfo?.solarFestival, solarTerm: lunarInfo?.solarTerm, }); } return days; } /** * 获取某周的所有日期信息 * @param date 周内的某一天 * @param options 选项 * @returns 日期信息数组 */ export function getWeekDays(date: Date, options: DateOptions = {}): DateInfo[] { if (!date || !(date instanceof Date) || isNaN(date.getTime())) { console.warn('Invalid date provided to getWeekDays'); return []; } const { firstDayOfWeek = 0, selectedDate = null, rangeStart = null, rangeEnd = null, priceData = {}, checkInData = {}, disabledDate = null } = options; const today = new Date(); const currentDay = date.getDay(); const diff = currentDay - firstDayOfWeek; const adjustedDiff = diff < 0 ? diff + 7 : diff; const firstDayOfWeek_ = new Date(date); firstDayOfWeek_.setDate(date.getDate() - adjustedDiff); const days: DateInfo[] = []; for (let i = 0; i < 7; i++) { const currentDate = new Date(firstDayOfWeek_); currentDate.setDate(firstDayOfWeek_.getDate() + i); const year = currentDate.getFullYear(); const month = currentDate.getMonth(); const day = currentDate.getDate(); const dateStr = formatDate(currentDate, 'YYYY-MM-DD'); const lunarInfo = solarToLunar(currentDate); const holidayInfo = getHolidayInfo(currentDate); days.push({ date: currentDate, day, month, year, isCurrentMonth: currentDate.getMonth() === date.getMonth(), isToday: isSameDay(currentDate, today), isSelected: selectedDate ? isSameDay(currentDate, selectedDate) : false, isInRange: rangeStart && rangeEnd ? isDateInRange(currentDate, rangeStart, rangeEnd) : false, isRangeStart: rangeStart ? isSameDay(currentDate, rangeStart) : false, isRangeEnd: rangeEnd ? isSameDay(currentDate, rangeEnd) : false, isWeekend: currentDate.getDay() === 0 || currentDate.getDay() === 6, isHoliday: holidayInfo.isHoliday !== undefined ? holidayInfo.isHoliday : !!holidayInfo.name, isRestDay: !!holidayInfo.isRestDay, isHolidayPeriod: !!holidayInfo.isHolidayPeriod, isCheckedIn: !!checkInData[dateStr], isDisabled: disabledDate ? disabledDate(currentDate) : false, price: priceData[dateStr], lunarDay: lunarInfo?.lunarDay, lunarMonth: lunarInfo?.lunarMonth, lunarYear: lunarInfo?.lunarYear, lunarFestival: lunarInfo?.lunarFestival, solarFestival: lunarInfo?.solarFestival, solarTerm: lunarInfo?.solarTerm, }); } return days; } /** * 获取某年的所有月份信息 * @param year 年份 * @returns 月份信息数组 */ export function getYearMonths(year: number): { year: number; month: number; name: string }[] { const months = []; for (let i = 0; i < 12; i++) { months.push({ year, month: i, name: `${i + 1}月` }); } return months; } /** * 获取日期范围内的所有日期 * @param start 开始日期 * @param end 结束日期 * @returns 日期数组 */ export function getDateRange(start: Date, end: Date): Date[] { if (!start || !end || !(start instanceof Date) || !(end instanceof Date) || isNaN(start.getTime()) || isNaN(end.getTime())) { console.warn('Invalid date(s) provided to getDateRange'); return []; } const dates: Date[] = []; const current = new Date(start); while (current <= end) { dates.push(new Date(current)); current.setDate(current.getDate() + 1); } return dates; } /** * 添加天数到日期 * @param date 日期 * @param days 天数 * @returns 新日期 */ export function addDays(date: Date, days: number): Date { if (!date || !(date instanceof Date) || isNaN(date.getTime())) { console.warn('Invalid date provided to addDays'); return new Date(); } const result = new Date(date); result.setDate(result.getDate() + days); return result; } /** * 添加月数到日期 * @param date 日期 * @param months 月数 * @returns 新日期 */ export function addMonths(date: Date, months: number): Date { if (!date || !(date instanceof Date) || isNaN(date.getTime())) { console.warn('Invalid date provided to addMonths'); return new Date(); } const result = new Date(date); result.setMonth(result.getMonth() + months); return result; } /** * 添加年数到日期 * @param date 日期 * @param years 年数 * @returns 新日期 */ export function addYears(date: Date, years: number): Date { if (!date || !(date instanceof Date) || isNaN(date.getTime())) { console.warn('Invalid date provided to addYears'); return new Date(); } const result = new Date(date); result.setFullYear(result.getFullYear() + years); return result; } /** * 获取日期是一年中的第几周 * @param date 日期 * @param firstDayOfWeek 一周的第一天(0表示周日,1表示周一) * @returns 周数 */ export function getWeekNumber(date: Date, firstDayOfWeek: number = 0): number { if (!date || !(date instanceof Date) || isNaN(date.getTime())) { console.warn('Invalid date provided to getWeekNumber'); return 0; } // 复制日期,避免修改原始日期 const target = new Date(date.valueOf()); const dayNr = (date.getDay() + 7 - firstDayOfWeek) % 7; // 设置为一周的第一天 target.setDate(target.getDate() - dayNr); // 获取一月四日所在的周,这是ISO 8601标准定义的第一周 const jan4 = new Date(target.getFullYear(), 0, 4); const dayDiff = (target.getTime() - jan4.getTime()) / 86400000; // 计算周数 return Math.ceil((dayDiff + jan4.getDay() + 1) / 7); }