fix recurring nlp scheduled times

This commit is contained in:
callumalpass 2026-06-14 15:32:38 +10:00
parent ef04893eba
commit bcf1ac3091
3 changed files with 95 additions and 0 deletions

View file

@ -35,3 +35,4 @@ When a change has user-facing documentation, include a canonical tasknotes.dev l
## Fixed
- (#2007) Fixed a remaining task creation autocomplete case where a status value containing another status label could leave status fragments in the task title. Thanks to @prepare4robots for the follow-up report.
- (#2031) Fixed quick task creation failing when natural language input created a recurring task with a scheduled time. Thanks to @rdefaccio for reporting this.

View file

@ -2,15 +2,37 @@ export * from "@tasknotes/model/recurrence";
export type { TaskInfo } from "../types";
import {
addDTSTARTToRecurrenceRule as addDTSTARTToRecurrenceRuleModel,
addDTSTARTToRecurrenceRuleWithDraggedTime as addDTSTARTToRecurrenceRuleWithDraggedTimeModel,
getNextUncompletedOccurrence as getNextUncompletedOccurrenceModel,
updateToNextScheduledOccurrence as updateToNextScheduledOccurrenceModel,
} from "@tasknotes/model/recurrence";
import { getTodayString } from "../utils/dateUtils";
type AddDTSTARTTaskInput = Parameters<typeof addDTSTARTToRecurrenceRuleModel>[0];
type AddDTSTARTWithDraggedTimeTaskInput = Parameters<
typeof addDTSTARTToRecurrenceRuleWithDraggedTimeModel
>[0];
type RecurringTaskInput = Parameters<typeof getNextUncompletedOccurrenceModel>[0];
type RecurrenceUpdateTaskInput = Parameters<typeof updateToNextScheduledOccurrenceModel>[0];
type RecurrenceUpdateResult = ReturnType<typeof updateToNextScheduledOccurrenceModel>;
export function addDTSTARTToRecurrenceRule(task: AddDTSTARTTaskInput): string | null {
return addDTSTARTToRecurrenceRuleModel(normalizeMinutePrecisionDateInputs(task));
}
export function addDTSTARTToRecurrenceRuleWithDraggedTime(
task: AddDTSTARTWithDraggedTimeTaskInput,
draggedStart: Date,
allDay: boolean
): string | null {
return addDTSTARTToRecurrenceRuleWithDraggedTimeModel(
normalizeMinutePrecisionDateInputs(task),
draggedStart,
allDay
);
}
export function getNextUncompletedOccurrence(task: RecurringTaskInput): Date | null {
return getNextUncompletedOccurrenceModel(task, { today: getTodayString() });
}
@ -23,3 +45,35 @@ export function updateToNextScheduledOccurrence(
today: getTodayString(),
});
}
function normalizeMinutePrecisionDateInputs<T extends { scheduled?: string; dateCreated?: string }>(
task: T
): T {
const scheduled = normalizeMinutePrecisionDateTime(task.scheduled);
const dateCreated = normalizeMinutePrecisionDateTime(task.dateCreated);
if (scheduled === task.scheduled && dateCreated === task.dateCreated) {
return task;
}
return {
...task,
scheduled,
dateCreated,
};
}
function normalizeMinutePrecisionDateTime(value: string | undefined): string | undefined {
if (!value) return value;
const match = value
.trim()
.match(/^(\d{4}-\d{2}-\d{2})[T ](\d{2}:\d{2})(Z|[+-]\d{2}:\d{2})?$/);
if (!match) {
return value;
}
const [, date, time, zone = ""] = match;
return `${date}T${time}:00${zone}`;
}

View file

@ -0,0 +1,40 @@
import type TaskNotesPlugin from "../../../src/main";
import { addDTSTARTToRecurrenceRule } from "../../../src/core/recurrence";
import { buildTaskCreationDataFromParsed } from "../../../src/services/buildTaskCreationDataFromParsed";
import { DEFAULT_SETTINGS } from "../../../src/settings/defaults";
import type { ParsedTaskData } from "../../../src/services/NaturalLanguageParser";
function createPlugin(): TaskNotesPlugin {
return {
settings: {
...DEFAULT_SETTINGS,
},
} as unknown as TaskNotesPlugin;
}
describe("Issue #2031: NLP recurring task with time", () => {
it("adds DTSTART for NLP-created recurring tasks with minute-precision scheduled times", () => {
const parsed: ParsedTaskData = {
title: "this is a task",
scheduledDate: "2026-06-13",
scheduledTime: "15:00",
recurrence: "FREQ=DAILY",
};
const taskData = buildTaskCreationDataFromParsed(createPlugin(), parsed);
expect(taskData.scheduled).toBe("2026-06-13T15:00");
expect(taskData.recurrence).toBe("FREQ=DAILY");
expect(
addDTSTARTToRecurrenceRule({
title: taskData.title,
status: taskData.status,
priority: taskData.priority,
path: "",
archived: false,
scheduled: taskData.scheduled,
recurrence: taskData.recurrence,
})
).toBe("DTSTART:20260613T150000Z;FREQ=DAILY");
});
});