From b0db3ce34c7808709d96c27136e509aea309ea4d Mon Sep 17 00:00:00 2001 From: kolaente Date: Sun, 5 May 2024 13:51:22 +0200 Subject: [PATCH] fix(quick add magic): parse full month name as month, do not replace only the abbreviation Resolves https://kolaente.dev/vikunja/vikunja/issues/2320 --- frontend/src/helpers/time/parseDate.ts | 16 ++++---- frontend/src/modules/parseTaskText.test.ts | 47 +++++++++++++++------- frontend/src/modules/parseTaskText.ts | 4 +- 3 files changed, 42 insertions(+), 25 deletions(-) diff --git a/frontend/src/helpers/time/parseDate.ts b/frontend/src/helpers/time/parseDate.ts index 124acd5c2..d1ca0a743 100644 --- a/frontend/src/helpers/time/parseDate.ts +++ b/frontend/src/helpers/time/parseDate.ts @@ -2,7 +2,7 @@ import {calculateDayInterval} from './calculateDayInterval' import {calculateNearestHours} from './calculateNearestHours' import {replaceAll} from '../replaceAll' -interface dateParseResult { +export interface dateParseResult { newText: string, date: Date | null, } @@ -12,7 +12,7 @@ interface dateFoundResult { date: Date | null, } -const monthsRegexGroup = '(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)' +const monthsRegexGroup = '(january|february|march|april|june|july|august|september|october|november|december|jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)' function matchesDateExpr(text: string, dateExpr: string): boolean { return text.match(new RegExp('(^| )' + dateExpr, 'gi')) !== null @@ -60,12 +60,12 @@ export const parseDate = (text: string, now: Date = new Date()): dateParseResult return addTimeToDate(text, date, 'end of month') } - let parsed = getDateFromWeekday(text) + let parsed = getDateFromWeekday(text, now) if (parsed.date !== null) { return addTimeToDate(text, parsed.date, parsed.foundText) } - parsed = getDayFromText(text) + parsed = getDayFromText(text, now) if (parsed.date !== null) { const month = getMonthFromText(text, parsed.date) return addTimeToDate(month.newText, month.date, parsed.foundText) @@ -76,7 +76,7 @@ export const parseDate = (text: string, now: Date = new Date()): dateParseResult return addTimeToDate(text, parsed.date, parsed.foundText) } - parsed = getDateFromText(text) + parsed = getDateFromText(text, now) if (parsed.date === null) { return { @@ -230,7 +230,7 @@ export const getDateFromTextIn = (text: string, now: Date = new Date()) => { } } -const getDateFromWeekday = (text: string): dateFoundResult => { +const getDateFromWeekday = (text: string, date: Date = new Date()): dateFoundResult => { const matcher = /(^| )(next )?(monday|mon|tuesday|tue|wednesday|wed|thursday|thu|friday|fri|saturday|sat|sunday|sun)($| )/g const results: string[] | null = matcher.exec(text.toLowerCase()) // The i modifier does not seem to work. if (results === null) { @@ -240,7 +240,6 @@ const getDateFromWeekday = (text: string): dateFoundResult => { } } - const date: Date = new Date() const currentDay: number = date.getDay() let day = 0 @@ -296,7 +295,7 @@ const getDateFromWeekday = (text: string): dateFoundResult => { } } -const getDayFromText = (text: string) => { +const getDayFromText = (text: string, now: Date = new Date()) => { const matcher = /(^| )(([1-2][0-9])|(3[01])|(0?[1-9]))(st|nd|rd|th|\.)($| )/ig const results = matcher.exec(text) if (results === null) { @@ -306,7 +305,6 @@ const getDayFromText = (text: string) => { } } - const now = new Date() const date = new Date(now) const day = parseInt(results[0]) date.setDate(day) diff --git a/frontend/src/modules/parseTaskText.test.ts b/frontend/src/modules/parseTaskText.test.ts index 7213e6cc5..d3d49a196 100644 --- a/frontend/src/modules/parseTaskText.test.ts +++ b/frontend/src/modules/parseTaskText.test.ts @@ -1,7 +1,7 @@ -import {beforeEach, afterEach, describe, it, expect, vi} from 'vitest' +import {afterEach, beforeEach, describe, expect, it, vi} from 'vitest' -import {parseTaskText, PrefixMode} from './parseTaskText' -import {getDateFromText, parseDate} from '../helpers/time/parseDate' +import {ParsedTaskText, parseTaskText, PrefixMode} from './parseTaskText' +import {parseDate} from '../helpers/time/parseDate' import {calculateDayInterval} from '../helpers/time/calculateDayInterval' import {PRIORITIES} from '@/constants/priorities' import {MILLISECONDS_A_DAY} from '@/constants/date' @@ -461,50 +461,69 @@ describe('Parse Task Text', () => { '1/27': '2022-1-27', 'jan 27': '2022-1-27', 'Jan 27': '2022-1-27', + 'january 27': '2022-1-27', + 'January 27': '2022-1-27', 'feb 21': '2022-2-21', 'Feb 21': '2022-2-21', + 'february 21': '2022-2-21', + 'February 21': '2022-2-21', 'mar 21': '2022-3-21', 'Mar 21': '2022-3-21', + 'march 21': '2022-3-21', + 'March 21': '2022-3-21', 'apr 21': '2022-4-21', 'Apr 21': '2022-4-21', + 'april 21': '2022-4-21', + 'April 21': '2022-4-21', 'may 21': '2022-5-21', 'May 21': '2022-5-21', 'jun 21': '2022-6-21', 'Jun 21': '2022-6-21', + 'june 21': '2022-6-21', + 'June 21': '2022-6-21', + '21st June': '2021-6-21', 'jul 21': '2021-7-21', 'Jul 21': '2021-7-21', + 'july 21': '2021-7-21', + 'July 21': '2021-7-21', 'aug 21': '2021-8-21', 'Aug 21': '2021-8-21', + 'august 21': '2021-8-21', + 'August 21': '2021-8-21', 'sep 21': '2021-9-21', 'Sep 21': '2021-9-21', + 'september 21': '2021-9-21', + 'September 21': '2021-9-21', 'oct 21': '2021-10-21', 'Oct 21': '2021-10-21', + 'october 21': '2021-10-21', + 'October 21': '2021-10-21', 'nov 21': '2021-11-21', 'Nov 21': '2021-11-21', + 'november 21': '2021-11-21', + 'November 21': '2021-11-21', 'dec 21': '2021-12-21', 'Dec 21': '2021-12-21', + 'december 21': '2021-12-21', + 'December 21': '2021-12-21', } as Record for (const c in cases) { - it(`should parse '${c}' as '${cases[c]}' with the date at the end`, () => { - const {date, foundText} = getDateFromText(`Lorem Ipsum ${c}`, now) + const assertResult = ({date, text}: ParsedTaskText) => { if (date === null && cases[c] === null) { expect(date).toBeNull() return } expect(`${date?.getFullYear()}-${date?.getMonth() + 1}-${date?.getDate()}`).toBe(cases[c]) - expect(foundText.trim()).toBe(c) + expect(text.trim()).toBe('Lorem Ipsum') + } + + it(`should parse '${c}' as '${cases[c]}' with the date at the end`, () => { + assertResult(parseTaskText(`Lorem Ipsum ${c}`, PrefixMode.Default, now)) }) it(`should parse '${c}' as '${cases[c]}' with the date at the beginning`, () => { - const {date, foundText} = getDateFromText(`${c} Lorem Ipsum`, now) - if (date === null && cases[c] === null) { - expect(date).toBeNull() - return - } - - expect(`${date?.getFullYear()}-${date?.getMonth() + 1}-${date?.getDate()}`).toBe(cases[c]) - expect(foundText.trim()).toBe(c) + assertResult(parseTaskText(`${c} Lorem Ipsum`, PrefixMode.Default, now)) }) } }) diff --git a/frontend/src/modules/parseTaskText.ts b/frontend/src/modules/parseTaskText.ts index 5b1b1063c..e5abd5e9f 100644 --- a/frontend/src/modules/parseTaskText.ts +++ b/frontend/src/modules/parseTaskText.ts @@ -55,7 +55,7 @@ interface Prefixes { * * @param text */ -export const parseTaskText = (text: string, prefixesMode: PrefixMode = PrefixMode.Default): ParsedTaskText => { +export const parseTaskText = (text: string, prefixesMode: PrefixMode = PrefixMode.Default, now: Date = new Date()): ParsedTaskText => { const result: ParsedTaskText = { text: text, date: null, @@ -86,7 +86,7 @@ export const parseTaskText = (text: string, prefixesMode: PrefixMode = PrefixMod result.text = textWithoutMatched result.repeats = repeats - const {newText, date} = parseDate(result.text) + const {newText, date} = parseDate(result.text, now) result.text = newText result.date = date