diff --git a/PLUG.md b/PLUG.md index 4e55725..8e47d15 100644 --- a/PLUG.md +++ b/PLUG.md @@ -1,6 +1,6 @@ --- name: Library/sstent/icalendar/PLUG -version: 0.2.12 +version: 0.2.13 tags: meta/library files: - icalendar.plug.js diff --git a/icalendar.ts b/icalendar.ts index 588f2b6..51fad90 100644 --- a/icalendar.ts +++ b/icalendar.ts @@ -1,7 +1,7 @@ import { clientStore, config, datastore, editor, index } from "@silverbulletmd/silverbullet/syscalls"; import { convertIcsCalendar, type IcsCalendar, type IcsEvent, type IcsDateObjects } from "ts-ics"; -const VERSION = "0.2.12"; +const VERSION = "0.2.13"; const CACHE_KEY = "icalendar:lastSync"; const DEFAULT_CACHE_DURATION_SECONDS = 21600; // 6 hours @@ -53,38 +53,10 @@ interface CalendarEvent extends DateToString { // ============================================================================ /** - * Robustly converts an ICS date object to a localized PST string + * Standard SilverBullet local date string formatter */ -function processIcsDate(obj: any, manualShift = 0): string { - // 1. Get the "Wall Time" (e.g., 16:00) - // If ts-ics provides a 'local' date string, it's usually the wall time but incorrectly marked as UTC - const wallTimeStr = (obj.local && typeof obj.local.date === "string") - ? obj.local.date - : (typeof obj.date === "string" ? obj.date : ""); - - if (!wallTimeStr) return ""; - - // 2. Parse it as if it were UTC to get a base date object - // We remove the 'Z' if it exists to ensure we control the parsing - const baseDate = new Date(wallTimeStr.replace("Z", "") + "Z"); - - // 3. Determine the Source Offset (e.g., W. Europe = +1) - const tzName = obj.local?.timezone || obj.timezone || "UTC"; - const sourceOffset = TIMEZONE_OFFSETS[tzName] || 0; - - // 4. Calculate the TRUE UTC time - // UTC = WallTime - SourceOffset - let utcTime = baseDate.getTime() - (sourceOffset * 3600000); - - // 5. Apply User's Manual Shift (if any) - if (manualShift !== 0) { - utcTime += (manualShift * 3600000); - } - - // 6. Localize to the browser's current timezone (PST) - const d = new Date(utcTime); +function toLocalISO(d: Date): string { const pad = (n: number) => String(n).padStart(2, "0"); - return d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) + @@ -94,6 +66,33 @@ function processIcsDate(obj: any, manualShift = 0): string { "." + String(d.getMilliseconds()).padStart(3, "0"); } +/** + * Robustly converts an ICS date object to a localized PST string + */ +function processIcsDate(obj: any, manualShift = 0): string { + if (!obj) return ""; + + // Handle case where it's already a Date object + if (obj instanceof Date) { + return toLocalISO(new Date(obj.getTime() + (manualShift * 3600000))); + } + + // Extract the underlying Date object from the ts-ics structure + const baseDate = obj.date instanceof Date ? obj.date : null; + if (!baseDate) return ""; + + // If ts-ics gave us a 'local' field, it has already tried to parse the TZID + const tzName = obj.local?.timezone || obj.timezone || "UTC"; + const sourceOffset = TIMEZONE_OFFSETS[tzName] || 0; + + // Calculate the TRUE UTC time if it wasn't already in UTC + // WallTime (baseDate) - SourceOffset = UTC + const utcTime = baseDate.getTime() - (sourceOffset * 3600000); + + // Create final localized date + return toLocalISO(new Date(utcTime + (manualShift * 3600000))); +} + function isIcsDateObjects(obj: any): obj is IcsDateObjects { return obj && typeof obj === 'object' && ('date' in obj && 'type' in obj); } @@ -114,10 +113,7 @@ function convertDatesToStrings(obj: T, hourShift = 0): DateToString { } if (obj instanceof Date) { - // Fallback for raw Date objects - const d = new Date(obj.getTime() + (hourShift * 3600000)); - const pad = (n: number) => String(n).padStart(2, "0"); - return d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) + "T" + pad(d.getHours()) + ":" + pad(d.getMinutes()) + ":" + pad(d.getSeconds()) + ".000" as DateToString; + return toLocalISO(new Date(obj.getTime() + (hourShift * 3600000))) as DateToString; } if (Array.isArray(obj)) { @@ -217,9 +213,10 @@ export async function clearCache() { allKeys.push(["idx", ...key.slice(2), "$icalendar"]); } if (allKeys.length > 0) await datastore.batchDel(allKeys); + await clientStore.del(CACHE_KEY); await editor.flashNotification("Calendar index cleared", "info"); } export async function showVersion() { await editor.flashNotification(`iCalendar Plug ${VERSION}`, "info"); -} +} \ No newline at end of file