Cleanup date and recurrence calculations.
This commit is contained in:
parent
e78ced2383
commit
8e926c0a21
@ -5,9 +5,10 @@ import {
|
||||
getLocaleWeekendDays,
|
||||
getMondayOfISOWeek,
|
||||
getOccurrenceIndex,
|
||||
getOccurrenceDate,
|
||||
DEFAULT_TZ,
|
||||
} from '@/utils/date'
|
||||
import { differenceInCalendarDays, addDays, addMonths } from 'date-fns'
|
||||
import { differenceInCalendarDays, addDays } from 'date-fns'
|
||||
import {
|
||||
initializeHolidays,
|
||||
getHolidayForDate,
|
||||
@ -235,249 +236,100 @@ export const useCalendarStore = defineStore('calendar', {
|
||||
},
|
||||
|
||||
deleteEvent(eventId) {
|
||||
console.log('Deleting event', eventId)
|
||||
this.events.delete(eventId)
|
||||
},
|
||||
|
||||
deleteSingleOccurrence(ctx) {
|
||||
const { baseId, occurrenceIndex } = ctx
|
||||
// Remove the first (base) occurrence of a repeating event by shifting anchor forward
|
||||
deleteFirstOccurrence(baseId) {
|
||||
console.log('Deleting first occurrence', baseId)
|
||||
const base = this.getEventById(baseId)
|
||||
if (!base || !base.isRepeating) return
|
||||
// WEEKLY SERIES ------------------------------------------------------
|
||||
if (base.repeat === 'weeks') {
|
||||
// Special case: deleting the first occurrence (index 0) should shift the series forward
|
||||
if (occurrenceIndex === 0) {
|
||||
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
|
||||
const baseEnd = fromLocalString(base.endDate, DEFAULT_TZ)
|
||||
const spanDays = Math.max(0, differenceInCalendarDays(baseEnd, baseStart))
|
||||
const pattern = base.repeatWeekdays || []
|
||||
if (!pattern.some(Boolean)) {
|
||||
// No pattern to continue -> delete whole series
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
const interval = base.repeatInterval || 1
|
||||
const WEEK_MS = 7 * 86400000
|
||||
const baseBlockStart = getMondayOfISOWeek(baseStart)
|
||||
const isAligned = (d) => {
|
||||
const blk = getMondayOfISOWeek(d)
|
||||
const diff = Math.floor((blk - baseBlockStart) / WEEK_MS)
|
||||
return diff % interval === 0
|
||||
}
|
||||
let probe = new Date(baseStart)
|
||||
let safety = 0
|
||||
let found = null
|
||||
while (safety < 5000) {
|
||||
probe = addDays(probe, 1)
|
||||
if (pattern[probe.getDay()] && isAligned(probe)) {
|
||||
found = new Date(probe)
|
||||
break
|
||||
}
|
||||
safety++
|
||||
}
|
||||
if (!found) {
|
||||
// Nothing after first -> delete series
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
// Adjust repeat count
|
||||
if (base.repeatCount !== 'unlimited') {
|
||||
const rc = parseInt(base.repeatCount, 10)
|
||||
if (!isNaN(rc)) {
|
||||
const newRc = rc - 1
|
||||
if (newRc <= 0) {
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
base.repeatCount = String(newRc)
|
||||
}
|
||||
}
|
||||
const newEnd = addDays(found, spanDays)
|
||||
base.startDate = toLocalString(found, DEFAULT_TZ)
|
||||
base.endDate = toLocalString(newEnd, DEFAULT_TZ)
|
||||
base.isSpanning = base.startDate < base.endDate
|
||||
this.events.set(base.id, base)
|
||||
return
|
||||
}
|
||||
const interval = base.repeatInterval || 1
|
||||
const pattern = base.repeatWeekdays || []
|
||||
if (!pattern.some(Boolean)) return
|
||||
// Preserve original count before any truncation
|
||||
const originalCountRaw = base.repeatCount
|
||||
|
||||
// Determine target occurrence date
|
||||
let targetDate = null
|
||||
if (ctx.occurrenceDate instanceof Date) {
|
||||
targetDate = new Date(
|
||||
ctx.occurrenceDate.getFullYear(),
|
||||
ctx.occurrenceDate.getMonth(),
|
||||
ctx.occurrenceDate.getDate(),
|
||||
)
|
||||
} else {
|
||||
// Fallback: derive from sequential occurrenceIndex (base=0, first repeat=1)
|
||||
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
|
||||
const baseEnd = fromLocalString(base.endDate, DEFAULT_TZ)
|
||||
if (occurrenceIndex === 0) {
|
||||
targetDate = baseStart
|
||||
} else {
|
||||
let cur = new Date(baseEnd)
|
||||
cur = addDays(cur, 1)
|
||||
let found = 0
|
||||
let safety = 0
|
||||
const WEEK_MS = 7 * 86400000
|
||||
const baseBlockStart = getMondayOfISOWeek(baseStart)
|
||||
function isAligned(d) {
|
||||
const blk = getMondayOfISOWeek(d)
|
||||
const diff = Math.floor((blk - baseBlockStart) / WEEK_MS)
|
||||
return diff % interval === 0
|
||||
}
|
||||
while (found < occurrenceIndex && safety < 50000) {
|
||||
if (pattern[cur.getDay()] && isAligned(cur)) {
|
||||
found++
|
||||
if (found === occurrenceIndex) break
|
||||
}
|
||||
cur = addDays(cur, 1)
|
||||
safety++
|
||||
}
|
||||
targetDate = cur
|
||||
}
|
||||
}
|
||||
if (!targetDate) return
|
||||
|
||||
// Count occurrences BEFORE target (always include the base occurrence as first)
|
||||
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
|
||||
const baseBlockStart = getMondayOfISOWeek(baseStart)
|
||||
const WEEK_MS = 7 * 86400000
|
||||
function isAligned(d) {
|
||||
const block = getMondayOfISOWeek(d)
|
||||
const diff = Math.floor((block - baseBlockStart) / WEEK_MS)
|
||||
return diff % interval === 0
|
||||
}
|
||||
// Start with 1 (base occurrence) if target is after base; if target IS base (should not happen here) leave 0
|
||||
let countBefore = targetDate > baseStart ? 1 : 0
|
||||
let probe = new Date(baseStart)
|
||||
probe = addDays(probe, 1) // start counting AFTER base
|
||||
let safety2 = 0
|
||||
while (probe < targetDate && safety2 < 50000) {
|
||||
if (pattern[probe.getDay()] && isAligned(probe)) countBefore++
|
||||
probe = addDays(probe, 1)
|
||||
safety2++
|
||||
}
|
||||
// Terminate original series to keep only occurrences before target
|
||||
this._terminateRepeatSeriesAtIndex(baseId, countBefore)
|
||||
|
||||
// Calculate remaining occurrences for new series using ORIGINAL total
|
||||
let remainingCount = 'unlimited'
|
||||
if (originalCountRaw !== 'unlimited') {
|
||||
const originalTotal = parseInt(originalCountRaw, 10)
|
||||
if (!isNaN(originalTotal)) {
|
||||
const rem = originalTotal - countBefore - 1 // kept + deleted
|
||||
if (rem <= 0) return // nothing left to continue
|
||||
remainingCount = String(rem)
|
||||
}
|
||||
}
|
||||
|
||||
// Continuation starts at NEXT valid occurrence (matching weekday & aligned block)
|
||||
let continuationStart = new Date(targetDate)
|
||||
let searchSafety = 0
|
||||
let foundNext = false
|
||||
while (searchSafety < 50000) {
|
||||
continuationStart = addDays(continuationStart, 1)
|
||||
if (pattern[continuationStart.getDay()] && isAligned(continuationStart)) {
|
||||
foundNext = true
|
||||
break
|
||||
}
|
||||
searchSafety++
|
||||
}
|
||||
if (!foundNext) return // no remaining occurrences
|
||||
|
||||
const spanDays = differenceInCalendarDays(
|
||||
fromLocalString(base.endDate, DEFAULT_TZ),
|
||||
fromLocalString(base.startDate, DEFAULT_TZ),
|
||||
)
|
||||
const nextStartStr = toLocalString(continuationStart, DEFAULT_TZ)
|
||||
const nextEnd = addDays(continuationStart, spanDays)
|
||||
const nextEndStr = toLocalString(nextEnd, DEFAULT_TZ)
|
||||
this.createEvent({
|
||||
title: base.title,
|
||||
startDate: nextStartStr,
|
||||
endDate: nextEndStr,
|
||||
colorId: base.colorId,
|
||||
repeat: 'weeks',
|
||||
repeatInterval: interval,
|
||||
repeatCount: remainingCount,
|
||||
repeatWeekdays: base.repeatWeekdays,
|
||||
})
|
||||
if (!base) return
|
||||
if (!base.isRepeating) {
|
||||
// Simple (non-repeating) event: delete entirely
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
// MONTHLY SERIES -----------------------------------------------------
|
||||
if (base.repeat === 'months') {
|
||||
if (occurrenceIndex === 0) {
|
||||
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
|
||||
const baseEnd = fromLocalString(base.endDate, DEFAULT_TZ)
|
||||
const spanDays = Math.max(0, differenceInCalendarDays(baseEnd, baseStart))
|
||||
const interval = base.repeatInterval || 1
|
||||
const targetMonthIndex = baseStart.getMonth() + interval
|
||||
const targetYear = baseStart.getFullYear() + Math.floor(targetMonthIndex / 12)
|
||||
const targetMonth = targetMonthIndex % 12
|
||||
const daysInTarget = new Date(targetYear, targetMonth + 1, 0).getDate()
|
||||
const dom = Math.min(baseStart.getDate(), daysInTarget)
|
||||
const newStart = new Date(targetYear, targetMonth, dom)
|
||||
if (base.repeatCount !== 'unlimited') {
|
||||
const rc = parseInt(base.repeatCount, 10)
|
||||
if (!isNaN(rc)) {
|
||||
const newRc = rc - 1
|
||||
if (newRc <= 0) {
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
base.repeatCount = String(newRc)
|
||||
}
|
||||
}
|
||||
const newEnd = addDays(newStart, spanDays)
|
||||
base.startDate = toLocalString(newStart, DEFAULT_TZ)
|
||||
base.endDate = toLocalString(newEnd, DEFAULT_TZ)
|
||||
base.isSpanning = base.startDate < base.endDate
|
||||
this.events.set(base.id, base)
|
||||
return
|
||||
}
|
||||
const interval = base.repeatInterval || 1
|
||||
// Sequential index: base=0, first repeat=1
|
||||
if (occurrenceIndex <= 0) return // base deletion handled elsewhere
|
||||
// Count prior occurrences to KEEP (indices 0 .. occurrenceIndex-1) => occurrenceIndex total
|
||||
const originalCountRaw = base.repeatCount
|
||||
const priorOccurrences = occurrenceIndex
|
||||
this._terminateRepeatSeriesAtIndex(baseId, priorOccurrences)
|
||||
// Compute span days for multi‑day events
|
||||
const spanDays = differenceInCalendarDays(
|
||||
fromLocalString(base.endDate, DEFAULT_TZ),
|
||||
fromLocalString(base.startDate, DEFAULT_TZ),
|
||||
)
|
||||
// Remaining occurrences after deletion
|
||||
let remainingCount = 'unlimited'
|
||||
if (originalCountRaw !== 'unlimited') {
|
||||
const total = parseInt(originalCountRaw, 10)
|
||||
if (!isNaN(total)) {
|
||||
const rem = total - priorOccurrences - 1 // subtract kept + deleted
|
||||
if (rem <= 0) return // nothing left
|
||||
remainingCount = String(rem)
|
||||
}
|
||||
}
|
||||
// Next occurrence after deleted one is at (occurrenceIndex + 1)*interval months from base
|
||||
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
|
||||
const nextStart = addMonths(baseStart, (occurrenceIndex + 1) * interval)
|
||||
const nextEnd = addDays(nextStart, spanDays)
|
||||
const nextStartStr = toLocalString(nextStart, DEFAULT_TZ)
|
||||
const nextEndStr = toLocalString(nextEnd, DEFAULT_TZ)
|
||||
this.createEvent({
|
||||
title: base.title,
|
||||
startDate: nextStartStr,
|
||||
endDate: nextEndStr,
|
||||
colorId: base.colorId,
|
||||
repeat: 'months',
|
||||
repeatInterval: interval,
|
||||
repeatCount: remainingCount,
|
||||
})
|
||||
const numericCount =
|
||||
base.repeatCount === 'unlimited' ? Infinity : parseInt(base.repeatCount, 10)
|
||||
if (numericCount <= 1) {
|
||||
// Only one occurrence (or invalid count) -> delete event
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
// Get the next occurrence start date (index 1)
|
||||
const nextStartStr = getOccurrenceDate(base, 1, DEFAULT_TZ)
|
||||
if (!nextStartStr) {
|
||||
// No next occurrence; remove event
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
const oldStart = fromLocalString(base.startDate, DEFAULT_TZ)
|
||||
const oldEnd = fromLocalString(base.endDate, DEFAULT_TZ)
|
||||
const durationDays = Math.max(0, differenceInCalendarDays(oldEnd, oldStart))
|
||||
const newEndStr = toLocalString(
|
||||
addDays(fromLocalString(nextStartStr, DEFAULT_TZ), durationDays),
|
||||
DEFAULT_TZ,
|
||||
)
|
||||
// Mutate existing event instead of delete+recreate so references remain stable
|
||||
base.startDate = nextStartStr
|
||||
base.endDate = newEndStr
|
||||
if (numericCount !== Infinity) {
|
||||
base.repeatCount = String(Math.max(1, numericCount - 1))
|
||||
}
|
||||
this.events.set(baseId, { ...base, isSpanning: base.startDate < base.endDate })
|
||||
},
|
||||
|
||||
// Delete a specific occurrence (not the first) from a repeating series, splitting into two
|
||||
deleteSingleOccurrence(ctx) {
|
||||
console.log('DeletesingleOccurrence')
|
||||
const { baseId, occurrenceIndex } = ctx || {}
|
||||
if (occurrenceIndex === undefined || occurrenceIndex === null) return
|
||||
const base = this.getEventById(baseId)
|
||||
if (!base) return
|
||||
if (!base.isRepeating) {
|
||||
// Single non-repeating event deletion
|
||||
if (occurrenceIndex === 0) this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
if (occurrenceIndex === 0) {
|
||||
// Delegate to specialized first-occurrence deletion
|
||||
this.deleteFirstOccurrence(baseId)
|
||||
return
|
||||
}
|
||||
// Save copy before truncation for computing next occurrence date
|
||||
const snapshot = { ...base }
|
||||
// Cap original series to occurrences before the deleted one
|
||||
base.repeatCount = occurrenceIndex
|
||||
const nextStartStr = getOccurrenceDate(snapshot, occurrenceIndex + 1, DEFAULT_TZ)
|
||||
console.log('Deleting single', occurrenceIndex, nextStartStr)
|
||||
if (!nextStartStr) return // no continuation
|
||||
const durationDays = Math.max(
|
||||
0,
|
||||
differenceInCalendarDays(
|
||||
fromLocalString(snapshot.endDate),
|
||||
fromLocalString(snapshot.startDate),
|
||||
),
|
||||
)
|
||||
const newEndStr = toLocalString(addDays(fromLocalString(nextStartStr), durationDays))
|
||||
const originalNumeric =
|
||||
snapshot.repeatCount === 'unlimited' ? Infinity : parseInt(snapshot.repeatCount, 10)
|
||||
let remainingCount = 'unlimited'
|
||||
if (originalNumeric !== Infinity) {
|
||||
const rem = originalNumeric - (occurrenceIndex + 1)
|
||||
if (rem <= 0) return
|
||||
remainingCount = String(rem)
|
||||
}
|
||||
this.createEvent({
|
||||
title: snapshot.title,
|
||||
startDate: nextStartStr,
|
||||
endDate: newEndStr,
|
||||
colorId: snapshot.colorId,
|
||||
repeat: snapshot.repeat,
|
||||
repeatInterval: snapshot.repeatInterval,
|
||||
repeatCount: remainingCount,
|
||||
repeatWeekdays: snapshot.repeatWeekdays,
|
||||
})
|
||||
},
|
||||
|
||||
deleteFromOccurrence(ctx) {
|
||||
@ -494,86 +346,6 @@ export const useCalendarStore = defineStore('calendar', {
|
||||
this._terminateRepeatSeriesAtIndex(baseId, keptTotal)
|
||||
},
|
||||
|
||||
deleteFirstOccurrence(baseId) {
|
||||
const base = this.getEventById(baseId)
|
||||
if (!base || !base.isRepeating) return
|
||||
const oldStart = fromLocalString(base.startDate, DEFAULT_TZ)
|
||||
const oldEnd = fromLocalString(base.endDate, DEFAULT_TZ)
|
||||
const spanDays = Math.max(0, differenceInCalendarDays(oldEnd, oldStart))
|
||||
|
||||
let newStartDate = null
|
||||
|
||||
if (base.repeat === 'weeks') {
|
||||
const pattern = base.repeatWeekdays || []
|
||||
if (!pattern.some(Boolean)) {
|
||||
// No valid pattern -> delete series
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
const interval = base.repeatInterval || 1
|
||||
const baseBlockStart = getMondayOfISOWeek(oldStart)
|
||||
const WEEK_MS = 7 * 86400000
|
||||
const isAligned = (d) => {
|
||||
const block = getMondayOfISOWeek(d)
|
||||
const diff = Math.floor((block - baseBlockStart) / WEEK_MS)
|
||||
return diff % interval === 0
|
||||
}
|
||||
// search forward for next valid weekday respecting interval alignment
|
||||
let probe = new Date(oldStart)
|
||||
let safety = 0
|
||||
while (safety < 5000) {
|
||||
probe = addDays(probe, 1)
|
||||
if (pattern[probe.getDay()] && isAligned(probe)) {
|
||||
newStartDate = new Date(probe)
|
||||
break
|
||||
}
|
||||
safety++
|
||||
}
|
||||
} else if (base.repeat === 'months') {
|
||||
const interval = base.repeatInterval || 1
|
||||
const y = oldStart.getFullYear()
|
||||
const m = oldStart.getMonth()
|
||||
const targetMonthIndex = m + interval
|
||||
const targetYear = y + Math.floor(targetMonthIndex / 12)
|
||||
const targetMonth = targetMonthIndex % 12
|
||||
const daysInTargetMonth = new Date(targetYear, targetMonth + 1, 0).getDate()
|
||||
const dom = Math.min(oldStart.getDate(), daysInTargetMonth)
|
||||
newStartDate = new Date(targetYear, targetMonth, dom)
|
||||
} else {
|
||||
// Unsupported repeat type
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
|
||||
if (!newStartDate) {
|
||||
// No continuation; deleting first removes series
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
|
||||
// Decrement repeatCount if limited
|
||||
if (base.repeatCount !== 'unlimited') {
|
||||
const rc = parseInt(base.repeatCount, 10)
|
||||
if (!isNaN(rc)) {
|
||||
const newRc = rc - 1
|
||||
if (newRc <= 0) {
|
||||
// After removing first occurrence there are none left
|
||||
this.deleteEvent(baseId)
|
||||
return
|
||||
}
|
||||
base.repeatCount = String(newRc)
|
||||
}
|
||||
}
|
||||
|
||||
const newEndDate = addDays(newStartDate, spanDays)
|
||||
base.startDate = toLocalString(newStartDate, DEFAULT_TZ)
|
||||
base.endDate = toLocalString(newEndDate, DEFAULT_TZ)
|
||||
base.isSpanning = base.startDate < base.endDate
|
||||
// Persist updated base event
|
||||
this.events.set(base.id, base)
|
||||
return base.id
|
||||
},
|
||||
|
||||
// Adjust start/end range of a base event (non-generated) and reindex occurrences
|
||||
setEventRange(eventId, newStartStr, newEndStr, { mode = 'auto' } = {}) {
|
||||
const snapshot = this.events.get(eventId)
|
||||
|
@ -139,6 +139,68 @@ function getOccurrenceIndex(event, dateStr, timeZone = DEFAULT_TZ) {
|
||||
return null
|
||||
}
|
||||
|
||||
// Reverse lookup: given a recurrence index (0-based) return the occurrence start date string.
|
||||
// Returns null if the index is out of range or the event is not repeating.
|
||||
function getWeeklyOccurrenceDate(event, occurrenceIndex, timeZone = DEFAULT_TZ) {
|
||||
if (!event?.isRepeating || event.repeat !== 'weeks') return null
|
||||
if (occurrenceIndex < 0 || !Number.isInteger(occurrenceIndex)) return null
|
||||
if (event.repeatCount !== 'unlimited' && occurrenceIndex >= event.repeatCount) return null
|
||||
const pattern = event.repeatWeekdays || []
|
||||
if (!pattern.some(Boolean)) return null
|
||||
const interval = event.repeatInterval || 1
|
||||
const baseStart = fromLocalString(event.startDate, timeZone)
|
||||
if (occurrenceIndex === 0) return toLocalString(baseStart, timeZone)
|
||||
const baseWeekMonday = getMondayOfISOWeek(baseStart, timeZone)
|
||||
const baseDow = dateFns.getDay(baseStart)
|
||||
// Sorted list of active weekday indices
|
||||
const patternDays = []
|
||||
for (let d = 0; d < 7; d++) if (pattern[d]) patternDays.push(d)
|
||||
// First (possibly partial) week: only pattern days >= baseDow and >= baseStart date
|
||||
const firstWeekDates = []
|
||||
for (const d of patternDays) {
|
||||
if (d < baseDow) continue
|
||||
const date = dateFns.addDays(baseWeekMonday, d)
|
||||
if (date < baseStart) continue
|
||||
firstWeekDates.push(date)
|
||||
}
|
||||
const F = firstWeekDates.length
|
||||
if (occurrenceIndex < F) {
|
||||
return toLocalString(firstWeekDates[occurrenceIndex], timeZone)
|
||||
}
|
||||
const remaining = occurrenceIndex - F
|
||||
const P = patternDays.length
|
||||
if (P === 0) return null
|
||||
// Determine aligned week group (k >= 1) in which the remaining-th occurrence lies
|
||||
const k = Math.floor(remaining / P) + 1 // 1-based aligned week count after base week
|
||||
const indexInWeek = remaining % P
|
||||
const dow = patternDays[indexInWeek]
|
||||
const occurrenceDate = dateFns.addDays(baseWeekMonday, k * interval * 7 + dow)
|
||||
return toLocalString(occurrenceDate, timeZone)
|
||||
}
|
||||
|
||||
function getMonthlyOccurrenceDate(event, occurrenceIndex, timeZone = DEFAULT_TZ) {
|
||||
if (!event?.isRepeating || event.repeat !== 'months') return null
|
||||
if (occurrenceIndex < 0 || !Number.isInteger(occurrenceIndex)) return null
|
||||
if (event.repeatCount !== 'unlimited' && occurrenceIndex >= event.repeatCount) return null
|
||||
const interval = event.repeatInterval || 1
|
||||
const baseStart = fromLocalString(event.startDate, timeZone)
|
||||
const targetMonthOffset = occurrenceIndex * interval
|
||||
const monthDate = dateFns.addMonths(baseStart, targetMonthOffset)
|
||||
// Adjust day for shorter months (clamp like forward logic)
|
||||
const baseDay = dateFns.getDate(baseStart)
|
||||
const daysInTargetMonth = dateFns.getDaysInMonth(monthDate)
|
||||
const day = Math.min(baseDay, daysInTargetMonth)
|
||||
const actual = makeTZDate(dateFns.getYear(monthDate), dateFns.getMonth(monthDate), day, timeZone)
|
||||
return toLocalString(actual, timeZone)
|
||||
}
|
||||
|
||||
function getOccurrenceDate(event, occurrenceIndex, timeZone = DEFAULT_TZ) {
|
||||
if (!event?.isRepeating || event.repeat === 'none') return null
|
||||
if (event.repeat === 'weeks') return getWeeklyOccurrenceDate(event, occurrenceIndex, timeZone)
|
||||
if (event.repeat === 'months') return getMonthlyOccurrenceDate(event, occurrenceIndex, timeZone)
|
||||
return null
|
||||
}
|
||||
|
||||
function getVirtualOccurrenceEndDate(event, occurrenceStartDate, timeZone = DEFAULT_TZ) {
|
||||
const baseStart = fromLocalString(event.startDate, timeZone)
|
||||
const baseEnd = fromLocalString(event.endDate, timeZone)
|
||||
@ -237,6 +299,7 @@ export {
|
||||
getMondayOfISOWeek,
|
||||
mondayIndex,
|
||||
getOccurrenceIndex,
|
||||
getOccurrenceDate,
|
||||
getVirtualOccurrenceEndDate,
|
||||
// formatting & localization
|
||||
pad,
|
||||
|
Loading…
x
Reference in New Issue
Block a user