From 4b2a2d0b36a4f6c7f43c0e2b00582ddc3aa73dec Mon Sep 17 00:00:00 2001 From: Leo Vasanko Date: Sun, 24 Aug 2025 10:03:08 -0600 Subject: [PATCH] Date formatting cleanup, added utility functions to avoid repetition. --- src/components/CalendarView.vue | 7 ++--- src/components/EventDialog.vue | 46 +++++---------------------------- src/utils/date.js | 34 ++++++++++++++++++++++++ 3 files changed, 42 insertions(+), 45 deletions(-) diff --git a/src/components/CalendarView.vue b/src/components/CalendarView.vue index 6ec4d5e..b88f728 100644 --- a/src/components/CalendarView.vue +++ b/src/components/CalendarView.vue @@ -13,7 +13,7 @@ import { daysInclusive, addDaysStr, formatDateRange, - getMondayOfISOWeek, + formatTodayString, getOccurrenceIndex, getVirtualOccurrenceEndDate, getISOWeek, @@ -83,10 +83,7 @@ const selectedDateRange = computed(() => { const todayString = computed(() => { const d = new Date(calendarStore.now) - const t = d - .toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' }) - .replace(/,? /, '\n') - return t.charAt(0).toUpperCase() + t.slice(1) + return formatTodayString(d) }) const visibleWeeks = computed(() => { diff --git a/src/components/EventDialog.vue b/src/components/EventDialog.vue index f792f8e..f735396 100644 --- a/src/components/EventDialog.vue +++ b/src/components/EventDialog.vue @@ -4,7 +4,7 @@ import { ref, computed, watch, onUnmounted, nextTick } from 'vue' import BaseDialog from './BaseDialog.vue' import WeekdaySelector from './WeekdaySelector.vue' import Numeric from './Numeric.vue' -import { addDaysStr, getMondayOfISOWeek, fromLocalString, DEFAULT_TZ } from '@/utils/date' +import { addDaysStr, getMondayOfISOWeek, fromLocalString, formatDateShort, formatDateLong, DEFAULT_TZ } from '@/utils/date' import { addDays, addMonths } from 'date-fns' const props = defineProps({ @@ -440,24 +440,12 @@ const isLastOccurrence = computed(() => { }) const formattedOccurrenceShort = computed(() => { if (occurrenceContext.value?.occurrenceDate) { - try { - return occurrenceContext.value.occurrenceDate - .toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) - .replace(/, /, ' ') - } catch { - /* noop */ - } + return formatDateShort(occurrenceContext.value.occurrenceDate) } if (isRepeatingBaseEdit.value && editingEventId.value) { const ev = calendarStore.getEventById(editingEventId.value) if (ev?.startDate) { - try { - return fromLocalString(ev.startDate, DEFAULT_TZ) - .toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) - .replace(/, /, ' ') - } catch { - /* noop */ - } + return formatDateShort(fromLocalString(ev.startDate, DEFAULT_TZ)) } } return '' @@ -465,24 +453,12 @@ const formattedOccurrenceShort = computed(() => { const headerDateShort = computed(() => { if (occurrenceContext.value?.occurrenceDate) { - try { - return occurrenceContext.value.occurrenceDate - .toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) - .replace(/, /, ' ') - } catch { - return '' - } + return formatDateShort(occurrenceContext.value.occurrenceDate) } if (editingEventId.value) { const ev = calendarStore.getEventById(editingEventId.value) if (ev?.startDate) { - try { - return fromLocalString(ev.startDate, DEFAULT_TZ) - .toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) - .replace(/, /, ' ') - } catch { - return '' - } + return formatDateShort(fromLocalString(ev.startDate, DEFAULT_TZ)) } } return '' @@ -533,17 +509,7 @@ const formattedFinalOccurrence = computed(() => { const includeYear = d.getFullYear() !== now.getFullYear() || d.getTime() - now.getTime() >= 1000 * 60 * 60 * 24 * 365 - const opts = { - weekday: 'short', - month: 'short', - day: 'numeric', - ...(includeYear ? { year: 'numeric' } : {}), - } - try { - return d.toLocaleDateString(undefined, opts) - } catch { - return d.toDateString() - } + return formatDateLong(d, includeYear) }) const recurrenceSummary = computed(() => { diff --git a/src/utils/date.js b/src/utils/date.js index 885460d..d7312fb 100644 --- a/src/utils/date.js +++ b/src/utils/date.js @@ -287,6 +287,37 @@ function lunarPhaseSymbol(date) { } // Exports ----------------------------------------------------------------- +/** + * Format date as short localized string (e.g., "Jan 15") + */ +function formatDateShort(date) { + return date.toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) + .replace(/, /, ' ') +} + +/** + * Format date as long localized string with optional year (e.g., "Mon Jan 15" or "Mon Jan 15, 2025") + */ +function formatDateLong(date, includeYear = false) { + const opts = { + weekday: 'short', + month: 'short', + day: 'numeric', + ...(includeYear ? { year: 'numeric' } : {}), + } + return date.toLocaleDateString(undefined, opts) +} + +/** + * Format date as today string (e.g., "Monday\nJanuary 15") + */ +function formatTodayString(date) { + const formatted = date + .toLocaleDateString(undefined, { weekday: 'long', month: 'long', day: 'numeric' }) + .replace(/,? /, '\n') + return formatted.charAt(0).toUpperCase() + formatted.slice(1) +} + export { // constants monthAbbr, @@ -311,6 +342,9 @@ export { reorderByFirstDay, getLocalizedMonthName, formatDateRange, + formatDateShort, + formatDateLong, + formatTodayString, lunarPhaseSymbol, // iso helpers re-export getISOWeek,