Date cleanup / refactoring

This commit is contained in:
Leo Vasanko
2025-08-23 21:26:22 -06:00
parent cb0ac1eaf0
commit e78ced2383
8 changed files with 244 additions and 497 deletions

View File

@@ -25,7 +25,8 @@ import {
getLocalizedWeekdayNames,
getLocaleWeekendDays,
getLocaleFirstDay,
isoWeekInfo,
getISOWeek,
getISOWeekYear,
fromLocalString,
toLocalString,
mondayIndex,
@@ -85,7 +86,7 @@ const updateVisibleWeeks = () => {
const topDisplayIndex = Math.floor(scrollTop / rowHeight.value)
const topVW = topDisplayIndex + minVirtualWeek.value
const monday = getMondayForVirtualWeek(topVW)
const { year } = isoWeekInfo(monday)
const year = getISOWeekYear(monday)
if (calendarStore.viewYear !== year) {
calendarStore.setViewYear(year)
}
@@ -102,7 +103,7 @@ const updateVisibleWeeks = () => {
const newVisibleWeeks = []
for (let vw = startVW; vw <= endVW; vw++) {
newVisibleWeeks.push({ virtualWeek: vw, monday: getMondayForVirtualWeek(vw) })
newVisibleWeeks.push({ virtualWeek: vw, monday: getMondayForVirtualWeek(vw) })
}
visibleWeeks.value = newVisibleWeeks
}
@@ -126,7 +127,7 @@ const handleWheel = (e) => {
const navigateToYear = (targetYear, weekIndex) => {
const monday = getMondayForVirtualWeek(weekIndex)
const { week } = isoWeekInfo(monday)
const week = getISOWeek(monday)
const jan4 = new Date(targetYear, 0, 4)
const jan4Monday = addDays(jan4, -mondayIndex(jan4))
const targetMonday = addDays(jan4Monday, (week - 1) * 7)

View File

@@ -1,7 +1,12 @@
<script setup>
import { computed } from 'vue'
import { useCalendarStore } from '@/stores/CalendarStore'
import { getLocalizedWeekdayNames, reorderByFirstDay, isoWeekInfo } from '@/utils/date'
import {
getLocalizedWeekdayNames,
reorderByFirstDay,
getISOWeek,
getISOWeekYear,
} from '@/utils/date'
import Numeric from '@/components/Numeric.vue'
import { addDays } from 'date-fns'
@@ -27,7 +32,7 @@ const topVirtualWeek = computed(() => {
const currentYear = computed(() => {
const weekStart = addDays(baseDate.value, topVirtualWeek.value * 7)
const anchor = addDays(weekStart, (4 - weekStart.getDay() + 7) % 7)
return isoWeekInfo(anchor).year
return getISOWeekYear(anchor)
})
function virtualWeekOf(d) {
@@ -53,10 +58,10 @@ function changeYear(y) {
// Anchor Thursday of current calendar week
const curCalWeekStart = addDays(baseDate.value, vw * 7)
const curAnchorThu = addDays(curCalWeekStart, (4 - curCalWeekStart.getDay() + 7) % 7)
let { week: isoW } = isoWeekInfo(curAnchorThu)
let isoW = getISOWeek(curAnchorThu)
// Build Monday of ISO week
let weekMon = isoWeekMonday(y, isoW)
if (isoWeekInfo(weekMon).year !== y) {
if (getISOWeekYear(weekMon) !== y) {
isoW--
weekMon = isoWeekMonday(y, isoW)
}

View File

@@ -6,7 +6,6 @@ import CalendarWeek from '@/components/CalendarWeek.vue'
import Jogwheel from '@/components/Jogwheel.vue'
import SettingsDialog from '@/components/SettingsDialog.vue'
import {
isoWeekInfo,
getLocalizedMonthName,
monthAbbr,
lunarPhaseSymbol,
@@ -17,6 +16,7 @@ import {
getMondayOfISOWeek,
getOccurrenceIndex,
getVirtualOccurrenceEndDate,
getISOWeek,
} from '@/utils/date'
import { toLocalString, fromLocalString, DEFAULT_TZ } from '@/utils/date'
import { addDays, differenceInCalendarDays } from 'date-fns'
@@ -134,7 +134,7 @@ function getFirstDayForVirtualWeek(virtualWeek) {
function createWeek(virtualWeek) {
const firstDay = getFirstDayForVirtualWeek(virtualWeek)
const isoAnchor = addDays(firstDay, (4 - firstDay.getDay() + 7) % 7)
const weekNumber = isoWeekInfo(isoAnchor).week
const weekNumber = getISOWeek(isoAnchor)
const days = []
let cur = new Date(firstDay)
let hasFirst = false
@@ -149,7 +149,7 @@ function createWeek(virtualWeek) {
}
for (let i = 0; i < 7; i++) {
const dateStr = toLocalString(cur, DEFAULT_TZ)
const dateStr = toLocalString(cur, DEFAULT_TZ)
const storedEvents = []
// Find all non-repeating events that occur on this date
@@ -175,19 +175,19 @@ function createWeek(virtualWeek) {
}
// Check if any virtual occurrence spans this date
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
const baseEnd = fromLocalString(base.endDate, DEFAULT_TZ)
const spanDays = Math.max(0, differenceInCalendarDays(baseEnd, baseStart))
const currentDate = fromLocalString(dateStr, DEFAULT_TZ)
const baseStart = fromLocalString(base.startDate, DEFAULT_TZ)
const baseEnd = fromLocalString(base.endDate, DEFAULT_TZ)
const spanDays = Math.max(0, differenceInCalendarDays(baseEnd, baseStart))
const currentDate = fromLocalString(dateStr, DEFAULT_TZ)
let occurrenceFound = false
// Walk backwards within span to find occurrence start
for (let offset = 0; offset <= spanDays && !occurrenceFound; offset++) {
const candidateStart = addDays(currentDate, -offset)
const candidateStartStr = toLocalString(candidateStart, DEFAULT_TZ)
const candidateStart = addDays(currentDate, -offset)
const candidateStartStr = toLocalString(candidateStart, DEFAULT_TZ)
const occurrenceIndex = getOccurrenceIndex(base, candidateStartStr, DEFAULT_TZ)
const occurrenceIndex = getOccurrenceIndex(base, candidateStartStr, DEFAULT_TZ)
if (occurrenceIndex !== null) {
// Calculate the end date of this occurrence
const virtualEndDate = getVirtualOccurrenceEndDate(base, candidateStartStr, DEFAULT_TZ)
@@ -252,18 +252,18 @@ function createWeek(virtualWeek) {
dateStr <= addDaysStr(selection.value.startDate, selection.value.dayCount - 1),
events: dayEvents,
})
cur = addDays(cur, 1)
cur = addDays(cur, 1)
}
let monthLabel = null
if (hasFirst && monthToLabel !== null) {
if (labelYear && labelYear <= calendarStore.config.max_year) {
let weeksSpan = 0
const d = addDays(cur, -1)
const d = addDays(cur, -1)
for (let i = 0; i < 6; i++) {
const probe = addDays(cur, -1 + i * 7)
d.setTime(probe.getTime())
const probe = addDays(cur, -1 + i * 7)
d.setTime(probe.getTime())
if (d.getMonth() === monthToLabel) weeksSpan++
}
@@ -337,7 +337,7 @@ function calculateSelection(anchorStr, otherStr) {
if (forward) {
return { startDate: anchorStr, dayCount: limit }
} else {
const startDate = addDaysStr(anchorStr, -(limit - 1), DEFAULT_TZ)
const startDate = addDaysStr(anchorStr, -(limit - 1), DEFAULT_TZ)
return { startDate, dayCount: limit }
}
}
@@ -441,7 +441,7 @@ watch(
() => calendarStore.config.first_day,
() => {
const currentTopVW = Math.floor(scrollTop.value / rowHeight.value) + minVirtualWeek.value
const currentTopDate = getFirstDayForVirtualWeek(currentTopVW)
const currentTopDate = getFirstDayForVirtualWeek(currentTopVW)
requestAnimationFrame(() => {
const newTopWeekIndex = getWeekIndex(currentTopDate)
const newScroll = (newTopWeekIndex - minVirtualWeek.value) * rowHeight.value

View File

@@ -4,7 +4,13 @@ import { ref, computed, watch, onMounted, onUnmounted, nextTick } from 'vue'
import BaseDialog from './BaseDialog.vue'
import WeekdaySelector from './WeekdaySelector.vue'
import Numeric from './Numeric.vue'
import { addDaysStr, getMondayOfISOWeek, fromLocalString, toLocalString, DEFAULT_TZ } from '@/utils/date'
import {
addDaysStr,
getMondayOfISOWeek,
fromLocalString,
toLocalString,
DEFAULT_TZ,
} from '@/utils/date'
import { addDays, addMonths } from 'date-fns'
const props = defineProps({
@@ -318,9 +324,9 @@ function openEditDialog(payload) {
// Compute occurrenceDate based on occurrenceIndex rather than parsing instanceId
if (event.isRepeating) {
if (event.repeat === 'weeks' && occurrenceIndex >= 0) {
const pattern = event.repeatWeekdays || []
const baseStart = fromLocalString(event.startDate, DEFAULT_TZ)
const baseEnd = fromLocalString(event.endDate, DEFAULT_TZ)
const pattern = event.repeatWeekdays || []
const baseStart = fromLocalString(event.startDate, DEFAULT_TZ)
const baseEnd = fromLocalString(event.endDate, DEFAULT_TZ)
if (occurrenceIndex === 0) {
occurrenceDate = baseStart
weekday = baseStart.getDay()
@@ -334,7 +340,7 @@ function openEditDialog(payload) {
const diff = Math.floor((blk - baseBlockStart) / WEEK_MS)
return diff % interval === 0
}
let cur = addDays(baseEnd, 1)
let cur = addDays(baseEnd, 1)
let found = 0 // number of repeat occurrences found so far
let safety = 0
while (found < occurrenceIndex && safety < 20000) {
@@ -349,8 +355,8 @@ function openEditDialog(payload) {
weekday = cur.getDay()
}
} else if (event.repeat === 'months' && occurrenceIndex >= 0) {
const baseDate = fromLocalString(event.startDate, DEFAULT_TZ)
occurrenceDate = addMonths(baseDate, occurrenceIndex)
const baseDate = fromLocalString(event.startDate, DEFAULT_TZ)
occurrenceDate = addMonths(baseDate, occurrenceIndex)
}
}
dialogMode.value = 'edit'
@@ -556,7 +562,7 @@ const formattedOccurrenceShort = computed(() => {
const ev = calendarStore.getEventById(editingEventId.value)
if (ev?.startDate) {
try {
return fromLocalString(ev.startDate, DEFAULT_TZ)
return fromLocalString(ev.startDate, DEFAULT_TZ)
.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
.replace(/, /, ' ')
} catch {
@@ -584,7 +590,7 @@ const headerDateShort = computed(() => {
const ev = calendarStore.getEventById(editingEventId.value)
if (ev?.startDate) {
try {
return fromLocalString(ev.startDate, DEFAULT_TZ)
return fromLocalString(ev.startDate, DEFAULT_TZ)
.toLocaleDateString(undefined, { month: 'short', day: 'numeric' })
.replace(/, /, ' ')
} catch {
@@ -616,16 +622,16 @@ const finalOccurrenceDate = computed(() => {
// Convert to Monday-first index
// We'll just check store pattern
if (pattern[startWeekdaySun]) occs = 1
let cursor = new Date(start)
let cursor = new Date(start)
while (occs < count && occs < 10000) {
cursor = addDays(cursor, 1)
cursor = addDays(cursor, 1)
if (pattern[cursor.getDay()]) occs++
}
if (occs === count) return cursor
return null
} else if (uiDisplayFrequency.value === 'months') {
const monthsToAdd = displayInterval.value * (count - 1)
return addMonths(start, monthsToAdd)
const monthsToAdd = displayInterval.value * (count - 1)
return addMonths(start, monthsToAdd)
} else if (uiDisplayFrequency.value === 'years') {
const yearsToAdd = displayInterval.value * (count - 1)
const d = new Date(start)

View File

@@ -141,9 +141,9 @@ function resetAll() {
if (typeof calendarStore.$reset === 'function') {
calendarStore.$reset()
} else {
const now = new Date()
calendarStore.today = now.toISOString().slice(0, 10)
calendarStore.now = now.toISOString()
const now = new Date()
calendarStore.today = now.toISOString().slice(0, 10)
calendarStore.now = now.toISOString()
calendarStore.events = new Map()
calendarStore.weekend = [6, 0]
calendarStore.config.first_day = 1

View File

@@ -7,7 +7,11 @@
<!-- Event spans will be rendered here -->
</div>
</div>
<div v-if="monthLabel" class="month-name-label" :style="{ height: `${monthLabel.weeksSpan * 64}px` }">
<div
v-if="monthLabel"
class="month-name-label"
:style="{ height: `${monthLabel.weeksSpan * 64}px` }"
>
<span>{{ monthLabel.name }} '{{ monthLabel.year }}</span>
</div>
</div>
@@ -16,19 +20,23 @@
<script setup>
import { computed } from 'vue'
import DayCell from './DayCell.vue'
import { isoWeekInfo, toLocalString, getLocalizedMonthName, monthAbbr, DEFAULT_TZ } from '@/utils/date'
import {
toLocalString,
getLocalizedMonthName,
monthAbbr,
DEFAULT_TZ,
getISOWeek,
} from '@/utils/date'
import { addDays } from 'date-fns'
const props = defineProps({
week: {
type: Object,
required: true
}
required: true,
},
})
const weekNumber = computed(() => {
return isoWeekInfo(props.week.monday).week
})
const weekNumber = computed(() => getISOWeek(props.week.monday))
const days = computed(() => {
const d = new Date(props.week.monday)
@@ -41,7 +49,7 @@ const days = computed(() => {
dayOfMonth: d.getDate(),
month: d.getMonth(),
isFirstDayOfMonth: d.getDate() === 1,
monthClass: monthAbbr[d.getMonth()]
monthClass: monthAbbr[d.getMonth()],
})
d.setTime(addDays(d, 1).getTime())
}
@@ -49,19 +57,19 @@ const days = computed(() => {
})
const monthLabel = computed(() => {
const firstDayOfMonth = days.value.find(d => d.isFirstDayOfMonth)
const firstDayOfMonth = days.value.find((d) => d.isFirstDayOfMonth)
if (!firstDayOfMonth) return null
const month = firstDayOfMonth.month
const year = firstDayOfMonth.date.getFullYear()
// This is a simplified calculation for weeksSpan
const weeksSpan = 4
const weeksSpan = 4
return {
name: getLocalizedMonthName(month),
name: getLocalizedMonthName(month),
year: String(year).slice(-2),
weeksSpan
weeksSpan,
}
})
</script>