From 7a3be56bd48e13efdc52fcf4835738fe8ab974eb Mon Sep 17 00:00:00 2001 From: dralkh Date: Sun, 15 Feb 2026 08:13:48 +0300 Subject: [PATCH] Fix date consistency bug between calendar and list views --- ui/sidebar/list-view-renderer.ts | 26 +++++++++++++------------- ui/sidebar/note-item-renderer.ts | 10 +++++----- utils/dates.ts | 8 ++++---- 3 files changed, 22 insertions(+), 22 deletions(-) diff --git a/ui/sidebar/list-view-renderer.ts b/ui/sidebar/list-view-renderer.ts index 9b1b8b5..4665a00 100644 --- a/ui/sidebar/list-view-renderer.ts +++ b/ui/sidebar/list-view-renderer.ts @@ -117,7 +117,7 @@ export class ListViewRenderer { // statsCountEl = statsEl.createEl("div", { cls: "review-stats-count" }); // } - // const overdueNotes = dueNotesForStats.filter(note => note.nextReviewDate < DateUtils.startOfDay()); + // const overdueNotes = dueNotesForStats.filter(note => note.nextReviewDate < DateUtils.startOfUTCDay()); // let totalTime = 0; // for (const note of dueNotesForStats) { // totalTime += await this.plugin.reviewScheduleService.estimateReviewTime(note.path); @@ -313,9 +313,9 @@ export class ListViewRenderer { headerContainer.createEl("h3"); // Placeholder for heading // "Advance All" button for future sections - const todayStart = DateUtils.startOfDay(new Date()); // Returns timestamp + const todayStart = DateUtils.startOfUTCDay(new Date()); // Returns timestamp const sectionDateKeyIsFuture = !["Due notes", "Today"].includes(dateStr) && - (notesForSection[0] && DateUtils.startOfDay(new Date(notesForSection[0].nextReviewDate)) > todayStart); + (notesForSection[0] && DateUtils.startOfUTCDay(new Date(notesForSection[0].nextReviewDate)) > todayStart); if (sectionDateKeyIsFuture) { const advanceAllBtn = headerContainer.createEl("button", { text: "Advance all", cls: "review-date-action-button review-date-advance-all" }); @@ -393,7 +393,7 @@ export class ListViewRenderer { dateHeading.setText(displayHeader); let overdueBadge = dateHeading.querySelector(".review-overdue-badge"); - const todayActualStart = DateUtils.startOfDay(); // Timestamp for actual today's midnight + const todayActualStart = DateUtils.startOfUTCDay(); // Timestamp for actual today's midnight // Condition for showing overdue badge: // The overdue badge should only show for the "Due notes" section. @@ -411,7 +411,7 @@ export class ListViewRenderer { // "Due notes" might be the category for notes *on* that day. // In this case, overdue days relative to that selected past date will be 0. const baseDate = this.getActiveListBaseDate(); - const referenceDateForDiff = baseDate ? DateUtils.startOfDay(baseDate) : todayActualStart; + const referenceDateForDiff = baseDate ? DateUtils.startOfUTCDay(baseDate) : todayActualStart; return Math.floor((referenceDateForDiff - new Date(note.nextReviewDate).getTime()) / (24 * 60 * 60 * 1000)); }); const maxDays = Math.max(0, ...daysDiff.filter(d => d >= 0 && !isNaN(d))); // Ensure positive days and filter NaN @@ -497,7 +497,7 @@ export class ListViewRenderer { const upcomingKeys = this.getSortedDateKeys(upcomingGroupedNotes) .filter(key => { if (key === 'Due notes') return false; - const actualTodayStart = DateUtils.startOfDay(new Date()); + const actualTodayStart = DateUtils.startOfUTCDay(new Date()); if (key === DateUtils.formatDate(actualTodayStart, 'relative', null)) return false; // Exclude "Today" if it's empty or handled by main list if (key === DateUtils.formatDate(DateUtils.addDays(actualTodayStart, 1), 'relative', null)) return false; // Exclude "Tomorrow" return true; @@ -694,10 +694,10 @@ export class ListViewRenderer { const advanceSelectedBtn = bulkActionsContainer.querySelector('.review-bulk-advance'); if (advanceSelectedBtn) { if (selectedNotesPaths.length > 1) { - const todayStart = DateUtils.startOfDay(new Date()); // Returns timestamp + const todayStart = DateUtils.startOfUTCDay(new Date()); // Returns timestamp const hasEligibleFutureNote = selectedNotesPaths.some(path => { const schedule = this.plugin.reviewScheduleService.schedules[path]; - return schedule && DateUtils.startOfDay(new Date(schedule.nextReviewDate)) > todayStart; + return schedule && DateUtils.startOfUTCDay(new Date(schedule.nextReviewDate)) > todayStart; }); advanceSelectedBtn.disabled = !hasEligibleFutureNote; advanceSelectedBtn.toggleClass('sf-hidden', false); // Always show if bulk actions are visible, rely on disabled state @@ -717,7 +717,7 @@ export class ListViewRenderer { if (!headerStatsEl) return; const dueNotesForStats = this.plugin.reviewScheduleService.getDueNotesWithCustomOrder(Date.now(), true); - const overdueNotes = dueNotesForStats.filter(note => note.nextReviewDate < DateUtils.startOfDay()); + const overdueNotes = dueNotesForStats.filter(note => note.nextReviewDate < DateUtils.startOfUTCDay()); let totalTime = 0; for (const note of dueNotesForStats) { totalTime += await this.plugin.reviewScheduleService.estimateReviewTime(note.path); @@ -806,7 +806,7 @@ export class ListViewRenderer { const daysDiff = notesInSection.map(noteEl => { const path = (noteEl as HTMLElement).dataset.notePath; const noteSchedule = path ? this.plugin.reviewScheduleService.schedules[path] : null; - return noteSchedule ? Math.abs(Math.floor((noteSchedule.nextReviewDate - DateUtils.startOfDay()) / (24 * 60 * 60 * 1000))) : 0; + return noteSchedule ? Math.abs(Math.floor((noteSchedule.nextReviewDate - DateUtils.startOfUTCDay()) / (24 * 60 * 60 * 1000))) : 0; }); const maxDays = Math.max(0, ...daysDiff.filter(d => !isNaN(d))); if (maxDays > 0) { @@ -842,13 +842,13 @@ export class ListViewRenderer { */ groupNotesByDate(notes: ReviewSchedule[], _includeFuture = false): Record { const grouped: Record = {}; - const actualTodayStart = DateUtils.startOfDay(new Date()); + const actualTodayStart = DateUtils.startOfUTCDay(new Date()); const activeListBaseDate = this.getActiveListBaseDate(); - const refDateForFilteringStart = activeListBaseDate ? DateUtils.startOfDay(new Date(activeListBaseDate)) : actualTodayStart; + const refDateForFilteringStart = activeListBaseDate ? DateUtils.startOfUTCDay(new Date(activeListBaseDate)) : actualTodayStart; for (const note of notes) { const noteDate = new Date(note.nextReviewDate); - const noteDateStart = DateUtils.startOfDay(noteDate); // Timestamp for note's due day midnight + const noteDateStart = DateUtils.startOfUTCDay(noteDate); // Timestamp for note's due day midnight (UTC) if (activeListBaseDate) { // When a specific date is selected from the calendar (activeListBaseDate is set), diff --git a/ui/sidebar/note-item-renderer.ts b/ui/sidebar/note-item-renderer.ts index 8dd4488..a086764 100644 --- a/ui/sidebar/note-item-renderer.ts +++ b/ui/sidebar/note-item-renderer.ts @@ -25,7 +25,7 @@ export class NoteItemRenderer { noteEl.removeAttribute("title"); if (dateStr === "Due notes") { noteEl.addClass("overdue-note"); - const daysOverdue = Math.abs(Math.floor((note.nextReviewDate - DateUtils.startOfDay()) / (24 * 60 * 60 * 1000))); + const daysOverdue = Math.abs(Math.floor((note.nextReviewDate - DateUtils.startOfUTCDay()) / (24 * 60 * 60 * 1000))); const originalDueDate = new Date(note.nextReviewDate).toLocaleDateString(); noteEl.setAttribute("title", `Originally due: ${originalDueDate} (${daysOverdue} ${daysOverdue === 1 ? 'day' : 'days'} overdue)`); } @@ -90,8 +90,8 @@ export class NoteItemRenderer { // Advance button state const advanceBtn = noteEl.querySelector(".review-note-advance"); if (advanceBtn) { - const todayStartTs = DateUtils.startOfDay(new Date()); // Returns timestamp - const noteReviewDayStartTs = DateUtils.startOfDay(new Date(note.nextReviewDate)); // Returns timestamp + const todayStartTs = DateUtils.startOfUTCDay(new Date()); // Returns timestamp + const noteReviewDayStartTs = DateUtils.startOfUTCDay(new Date(note.nextReviewDate)); // Returns timestamp const isEligibleForAdvance = noteReviewDayStartTs > todayStartTs; advanceBtn.disabled = !isEligibleForAdvance; if (isEligibleForAdvance) { @@ -335,8 +335,8 @@ export class NoteItemRenderer { // Conditional "Advance note" context menu item const schedule = this.plugin.reviewScheduleService.schedules[path]; if (schedule) { - const todayStart = DateUtils.startOfDay(new Date()); // Returns timestamp - const noteReviewDayStart = DateUtils.startOfDay(new Date(schedule.nextReviewDate)); // Returns timestamp + const todayStart = DateUtils.startOfUTCDay(new Date()); // Returns timestamp + const noteReviewDayStart = DateUtils.startOfUTCDay(new Date(schedule.nextReviewDate)); // Returns timestamp if (noteReviewDayStart > todayStart) { menu.addItem((item) => item .setTitle("Advance note") diff --git a/utils/dates.ts b/utils/dates.ts index 8914367..d3f1828 100644 --- a/utils/dates.ts +++ b/utils/dates.ts @@ -40,8 +40,8 @@ export class DateUtils { // Determine the reference date for relative calculations // Normalize dates to their start of day for accurate day-based comparison - const normalizedNoteEventDate = this.startOfDay(noteEventDate); - const normalizedActualCurrentDate = this.startOfDay(new Date()); // Actual current day, for "Due notes" + const normalizedNoteEventDate = this.startOfUTCDay(noteEventDate); + const normalizedActualCurrentDate = this.startOfUTCDay(new Date()); // Actual current day, for "Due notes" // "Due notes" are always relative to the *actual* current day, regardless of baseDateParam if (normalizedNoteEventDate < normalizedActualCurrentDate) { @@ -52,11 +52,11 @@ export class DateUtils { // The label for the group of notes (which are all for baseDateParam's day) // should reflect how baseDateParam's day relates to the actual current day. if (baseDateParam) { - const normalizedBaseDate = this.startOfDay(new Date(baseDateParam)); // The day being viewed in the calendar + const normalizedBaseDate = this.startOfUTCDay(new Date(baseDateParam)); // The day being viewed in the calendar if (normalizedBaseDate === normalizedActualCurrentDate) { return 'Today'; // e.g., Calendar view is set to actual today - } else if (normalizedBaseDate === this.startOfDay(new Date(this.addDays(normalizedActualCurrentDate, 1)))) { + } else if (normalizedBaseDate === this.startOfUTCDay(new Date(this.addDays(normalizedActualCurrentDate, 1)))) { return 'Tomorrow'; // e.g., Calendar view is set to actual tomorrow } else { // Calendar view is set to a specific day that is not actual today or actual tomorrow.