mirror of
https://github.com/callumalpass/tasknotes.git
synced 2026-07-22 12:50:26 +00:00
Improve date handling, error reporting, and consistency across services and helpers
• Update date formatting:
- In FilterService.ts and CacheManager.ts, replace native Date ISO string extraction (using toISOString().split('T')[0]) with date-fns’ format(date, 'yyyy-MM-dd') to ensure local timezone handling and consistency.
• Enhance safe date parsing:
- Modify FilterService.ts methods (due date and scheduled date checks, as well as getDueDateGroupFromDate and getScheduledDateGroupFromDate) to use parseISO from date-fns wrapped in try…catch, providing error logging and graceful fallback (e.g., returning 'Invalid Date') when parsing fails.
- Similarly, in helpers.ts, use parseISO and other date-fns utilities (startOfDay, isBefore) to safely parse and compare dates in isTaskOverdue and isRecurringTaskDueOn, with error handling to prevent unexpected failures.
• Improve note date extraction:
- In CacheManager.ts and AgendaView.ts, adjust parsing for note createdDate values to handle both full ISO timestamps and simple YYYY-MM-DD formats, ensuring accurate date grouping and consistent comparisons in the agenda view.
• General consistency and robustness:
- Replace multiple usages of "new Date(...)" with date-fns equivalents to standardize date operations across the codebase, reduce timezone-related issues, and enhance error handling.
This commit ensures more reliable date computation and formatting throughout the project while providing clear error notifications when parsing issues occur.
This commit is contained in:
parent
5830d9343f
commit
ef8614e5c3
4 changed files with 97 additions and 56 deletions
|
|
@ -102,7 +102,7 @@ export class FilterService extends EventEmitter {
|
|||
|
||||
// Get tasks with due dates in the range (existing logic)
|
||||
for (let date = new Date(start); date <= end; date.setDate(date.getDate() + 1)) {
|
||||
const dateStr = date.toISOString().split('T')[0];
|
||||
const dateStr = format(date, 'yyyy-MM-dd'); // CORRECT: Uses local timezone
|
||||
const pathsForDate = this.cacheManager.getTaskPathsByDate(dateStr);
|
||||
pathsForDate.forEach(path => pathsInRange.add(path));
|
||||
}
|
||||
|
|
@ -254,20 +254,28 @@ export class FilterService extends EventEmitter {
|
|||
|
||||
// Check due date
|
||||
if (task.due) {
|
||||
const dueDate = new Date(task.due);
|
||||
if (query.includeOverdue && dueDate < new Date()) {
|
||||
// This is an overdue task and we want to include overdue tasks
|
||||
inRange = true;
|
||||
} else if (dueDate >= startDate && dueDate <= endDate) {
|
||||
inRange = true;
|
||||
try {
|
||||
const dueDate = parseISO(task.due); // Safe parsing
|
||||
if (query.includeOverdue && isBefore(dueDate, new Date())) {
|
||||
// This is an overdue task and we want to include overdue tasks
|
||||
inRange = true;
|
||||
} else if (dueDate >= startDate && dueDate <= endDate) {
|
||||
inRange = true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error parsing due date ${task.due}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
// Check scheduled date if due date doesn't qualify
|
||||
if (!inRange && task.scheduled) {
|
||||
const scheduledDate = new Date(task.scheduled);
|
||||
if (scheduledDate >= startDate && scheduledDate <= endDate) {
|
||||
inRange = true;
|
||||
try {
|
||||
const scheduledDate = parseISO(task.scheduled); // Safe parsing
|
||||
if (scheduledDate >= startDate && scheduledDate <= endDate) {
|
||||
inRange = true;
|
||||
}
|
||||
} catch (error) {
|
||||
console.error(`Error parsing scheduled date ${task.scheduled}:`, error);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -420,25 +428,30 @@ export class FilterService extends EventEmitter {
|
|||
* Helper method to get due date group from a specific date string
|
||||
*/
|
||||
private getDueDateGroupFromDate(dueDate: string): string {
|
||||
const due = new Date(dueDate);
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
try {
|
||||
const due = parseISO(dueDate); // Safe parsing
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
const thisWeek = new Date(today);
|
||||
thisWeek.setDate(thisWeek.getDate() + 7);
|
||||
const thisWeek = new Date(today);
|
||||
thisWeek.setDate(thisWeek.getDate() + 7);
|
||||
|
||||
const dueDateOnly = new Date(due);
|
||||
dueDateOnly.setHours(0, 0, 0, 0);
|
||||
const dueDateOnly = new Date(due);
|
||||
dueDateOnly.setHours(0, 0, 0, 0);
|
||||
|
||||
if (dueDateOnly < today) return 'Overdue';
|
||||
if (dueDateOnly.getTime() === today.getTime()) return 'Today';
|
||||
if (dueDateOnly.getTime() === tomorrow.getTime()) return 'Tomorrow';
|
||||
if (dueDateOnly <= thisWeek) return 'This week';
|
||||
|
||||
return 'Later';
|
||||
if (dueDateOnly < today) return 'Overdue';
|
||||
if (dueDateOnly.getTime() === today.getTime()) return 'Today';
|
||||
if (dueDateOnly.getTime() === tomorrow.getTime()) return 'Tomorrow';
|
||||
if (dueDateOnly <= thisWeek) return 'This week';
|
||||
|
||||
return 'Later';
|
||||
} catch (error) {
|
||||
console.error(`Error parsing due date ${dueDate}:`, error);
|
||||
return 'Invalid Date';
|
||||
}
|
||||
}
|
||||
|
||||
private getScheduledDateGroup(task: TaskInfo, targetDate?: Date): string {
|
||||
|
|
@ -450,25 +463,30 @@ export class FilterService extends EventEmitter {
|
|||
* Helper method to get scheduled date group from a specific date string
|
||||
*/
|
||||
private getScheduledDateGroupFromDate(scheduledDate: string): string {
|
||||
const scheduled = new Date(scheduledDate);
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
try {
|
||||
const scheduled = parseISO(scheduledDate); // Safe parsing
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
const tomorrow = new Date(today);
|
||||
tomorrow.setDate(tomorrow.getDate() + 1);
|
||||
|
||||
const thisWeek = new Date(today);
|
||||
thisWeek.setDate(thisWeek.getDate() + 7);
|
||||
const thisWeek = new Date(today);
|
||||
thisWeek.setDate(thisWeek.getDate() + 7);
|
||||
|
||||
const scheduledDateOnly = new Date(scheduled);
|
||||
scheduledDateOnly.setHours(0, 0, 0, 0);
|
||||
const scheduledDateOnly = new Date(scheduled);
|
||||
scheduledDateOnly.setHours(0, 0, 0, 0);
|
||||
|
||||
if (scheduledDateOnly < today) return 'Past scheduled';
|
||||
if (scheduledDateOnly.getTime() === today.getTime()) return 'Today';
|
||||
if (scheduledDateOnly.getTime() === tomorrow.getTime()) return 'Tomorrow';
|
||||
if (scheduledDateOnly <= thisWeek) return 'This week';
|
||||
|
||||
return 'Later';
|
||||
if (scheduledDateOnly < today) return 'Past scheduled';
|
||||
if (scheduledDateOnly.getTime() === today.getTime()) return 'Today';
|
||||
if (scheduledDateOnly.getTime() === tomorrow.getTime()) return 'Tomorrow';
|
||||
if (scheduledDateOnly <= thisWeek) return 'This week';
|
||||
|
||||
return 'Later';
|
||||
} catch (error) {
|
||||
console.error(`Error parsing scheduled date ${scheduledDate}:`, error);
|
||||
return 'Invalid Date';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -3,6 +3,7 @@ import { TaskInfo, NoteInfo, IndexedFile, FileEventHandlers } from '../types';
|
|||
import { extractNoteInfo, extractTaskInfo, debounce } from './helpers';
|
||||
import { FieldMapper } from '../services/FieldMapper';
|
||||
import * as YAML from 'yaml';
|
||||
import { format } from 'date-fns';
|
||||
|
||||
/**
|
||||
* Unified cache manager that provides centralized data access and caching
|
||||
|
|
@ -362,7 +363,7 @@ export class CacheManager {
|
|||
async getTasksDueOnDate(date: Date): Promise<TaskInfo[]> {
|
||||
// Ensure cache is initialized first
|
||||
await this.ensureInitialized();
|
||||
const dateStr = date.toISOString().split('T')[0];
|
||||
const dateStr = format(date, 'yyyy-MM-dd'); // CORRECT: Uses local timezone
|
||||
const taskPaths = this.tasksByDate.get(dateStr) || new Set();
|
||||
const results: TaskInfo[] = [];
|
||||
|
||||
|
|
@ -389,7 +390,7 @@ export class CacheManager {
|
|||
async getNotesForDate(date: Date, forceRefresh = false): Promise<NoteInfo[]> {
|
||||
// Ensure cache is initialized first
|
||||
await this.ensureInitialized();
|
||||
const dateStr = date.toISOString().split('T')[0];
|
||||
const dateStr = format(date, 'yyyy-MM-dd'); // CORRECT: Uses local timezone
|
||||
const notePaths = this.notesByDate.get(dateStr) || new Set();
|
||||
const results: NoteInfo[] = [];
|
||||
|
||||
|
|
@ -702,7 +703,10 @@ export class CacheManager {
|
|||
|
||||
// Add to new indexes
|
||||
if (noteInfo.createdDate) {
|
||||
const dateStr = noteInfo.createdDate.split('T')[0];
|
||||
// Extract just the date part - handle both YYYY-MM-DD and full ISO timestamps
|
||||
const dateStr = noteInfo.createdDate.includes('T')
|
||||
? noteInfo.createdDate.split('T')[0]
|
||||
: noteInfo.createdDate;
|
||||
if (!this.notesByDate.has(dateStr)) {
|
||||
this.notesByDate.set(dateStr, new Set());
|
||||
}
|
||||
|
|
@ -804,7 +808,10 @@ export class CacheManager {
|
|||
const oldNote = oldData as NoteInfo;
|
||||
|
||||
if (oldNote.createdDate) {
|
||||
const dateStr = oldNote.createdDate.split('T')[0];
|
||||
// Extract just the date part - handle both YYYY-MM-DD and full ISO timestamps
|
||||
const dateStr = oldNote.createdDate.includes('T')
|
||||
? oldNote.createdDate.split('T')[0]
|
||||
: oldNote.createdDate;
|
||||
const pathSet = this.notesByDate.get(dateStr);
|
||||
if (pathSet) {
|
||||
pathSet.delete(path);
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
import { normalizePath, TFile, Vault } from 'obsidian';
|
||||
import { format } from 'date-fns';
|
||||
import { format, parseISO, startOfDay, isBefore, isSameDay as isSameDayFns } from 'date-fns';
|
||||
import { TimeInfo, TaskInfo, TimeEntry } from '../types';
|
||||
import * as YAML from 'yaml';
|
||||
import { YAMLCache } from './YAMLCache';
|
||||
|
|
@ -475,11 +475,16 @@ export function extractTaskInfo(
|
|||
export function isTaskOverdue(task: {due?: string}): boolean {
|
||||
if (!task.due) return false;
|
||||
|
||||
const dueDate = new Date(task.due);
|
||||
const today = new Date();
|
||||
today.setHours(0, 0, 0, 0);
|
||||
|
||||
return dueDate < today;
|
||||
try {
|
||||
// Safely parse the date string using date-fns
|
||||
const dueDate = startOfDay(parseISO(task.due));
|
||||
const today = startOfDay(new Date());
|
||||
|
||||
return isBefore(dueDate, today); // Use date-fns for comparison
|
||||
} catch (error) {
|
||||
console.error(`Error parsing due date ${task.due}:`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -516,9 +521,14 @@ export function isRecurringTaskDueOn(task: any, date: Date): boolean {
|
|||
}
|
||||
// Fall back to using the original due date
|
||||
else if (task.due) {
|
||||
const originalDueDate = new Date(task.due);
|
||||
return originalDueDate.getDate() === dayOfMonth &&
|
||||
originalDueDate.getMonth() === targetDate.getMonth();
|
||||
try {
|
||||
const originalDueDate = parseISO(task.due); // Safe parsing
|
||||
return originalDueDate.getDate() === dayOfMonth &&
|
||||
originalDueDate.getMonth() === targetDate.getMonth();
|
||||
} catch (error) {
|
||||
console.error(`Error parsing due date ${task.due}:`, error);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
default:
|
||||
|
|
@ -623,7 +633,7 @@ export function extractNoteInfo(content: string, path: string, file?: TFile): {t
|
|||
// If it's a full ISO timestamp or similar, extract just the date part
|
||||
else {
|
||||
try {
|
||||
const date = new Date(createdDate);
|
||||
const date = parseISO(createdDate); // Use parseISO for safe parsing
|
||||
if (!isNaN(date.getTime())) {
|
||||
// Format to YYYY-MM-DD to ensure consistency
|
||||
createdDate = format(date, "yyyy-MM-dd");
|
||||
|
|
|
|||
|
|
@ -334,8 +334,14 @@ export class AgendaView extends ItemView {
|
|||
|
||||
const notesForDate = allNotes.filter(note => {
|
||||
if (note.createdDate) {
|
||||
const noteCreatedDate = note.createdDate.split('T')[0];
|
||||
return noteCreatedDate === dateStr;
|
||||
try {
|
||||
// Safely parse the note's date and compare it to the agenda's date
|
||||
const noteDate = parseISO(note.createdDate);
|
||||
return isSameDay(noteDate, dayData.date);
|
||||
} catch (e) {
|
||||
// Handle cases where the date string might be invalid
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
});
|
||||
|
|
|
|||
Loading…
Reference in a new issue