46 lines
1.7 KiB
TypeScript
46 lines
1.7 KiB
TypeScript
export type CalendarMode = 'month' | 'week'
|
|||
|
|
|
||
|
|
/** Calendar dates are warehouse-local dates, never UTC conversions. */
|
||
|
|
export function dateKey(date: Date): string {
|
||
|
|
return `${date.getFullYear()}-${String(date.getMonth() + 1).padStart(2, '0')}-${String(date.getDate()).padStart(2, '0')}`
|
||
|
|
}
|
||
|
|
|
||
|
|
function parseDate(value: string): Date {
|
||
|
|
const date = new Date(`${value}T12:00:00`)
|
||
|
|
if (!/^\d{4}-\d{2}-\d{2}$/.test(value) || Number.isNaN(date.getTime()) || dateKey(date) !== value) {
|
||
|
|
throw new Error('无效的日历日期')
|
||
|
|
}
|
||
|
|
return date
|
||
|
|
}
|
||
|
|
|
||
|
|
export function addDays(value: string, days: number): string {
|
||
|
|
const date = parseDate(value)
|
||
|
|
date.setDate(date.getDate() + days)
|
||
|
|
return dateKey(date)
|
||
|
|
}
|
||
|
|
|
||
|
|
export function calendarRange(anchor: string, mode: CalendarMode): { from: string; to: string } {
|
||
|
|
const date = parseDate(anchor)
|
||
|
|
if (mode === 'month') date.setDate(1)
|
||
|
|
date.setDate(date.getDate() - (date.getDay() + 6) % 7)
|
||
|
|
const from = dateKey(date)
|
||
|
|
// A half-open interval also works directly with database timestamps.
|
||
|
|
return { from, to: addDays(from, mode === 'month' ? 42 : 7) }
|
||
|
|
}
|
||
|
|
|
||
|
|
export function calendarDates(anchor: string, mode: CalendarMode): string[] {
|
||
|
|
const { from } = calendarRange(anchor, mode)
|
||
|
|
return Array.from({ length: mode === 'month' ? 42 : 7 }, (_, index) => addDays(from, index))
|
||
|
|
}
|
||
|
|
|
||
|
|
export function moveCalendar(anchor: string, mode: CalendarMode, offset: number): string {
|
||
|
|
if (mode === 'week') return addDays(anchor, offset * 7)
|
||
|
|
const date = parseDate(anchor)
|
||
|
|
const day = date.getDate()
|
||
|
|
date.setDate(1)
|
||
|
|
date.setMonth(date.getMonth() + offset)
|
||
|
|
const lastDay = new Date(date.getFullYear(), date.getMonth() + 1, 0).getDate()
|
||
|
|
date.setDate(Math.min(day, lastDay))
|
||
|
|
return dateKey(date)
|
||
|
|
}
|