mirror of
https://github.com/go-vikunja/vikunja.git
synced 2026-04-29 11:00:07 -05:00
fix: allow middle-of-text dates when followed by time expressions (#2195)
Reworked matchDateAtBoundary() to use a single regex pass instead of two-pass start/end anchoring. Middle-of-text matches are now accepted when followed by a time expression (at/@ prefix), so inputs like "meeting 9/11 at 10:00" still parse correctly while "The 9/11 Report" is rejected.
This commit is contained in:
@@ -15,23 +15,29 @@ interface dateFoundResult {
|
|||||||
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)'
|
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)'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Tries matching a date regex against text, but only at the start or end of the string.
|
* Matches a date regex against text, rejecting matches that appear in the middle
|
||||||
* This prevents false positives like "The 9/11 Report" where the date-like pattern
|
* of text with non-date content on both sides. This prevents false positives like
|
||||||
* appears in the middle of the text.
|
* "The 9/11 Report" while still allowing "meeting 9/11 at 10:00".
|
||||||
*
|
*
|
||||||
* The pattern is tested in two passes: first anchored to the start, then anchored to the end.
|
* Matches at the start or end of text are always accepted. Middle matches are
|
||||||
|
* only accepted when followed by a time expression (at/@ prefix).
|
||||||
*/
|
*/
|
||||||
function matchDateAtBoundary(text: string, pattern: string): RegExpExecArray | null {
|
function matchDateAtBoundary(text: string, pattern: string): RegExpExecArray | null {
|
||||||
// Pass 1: try matching at the start of the text
|
const regex = new RegExp(`(^| )${pattern}($| )`, 'gi')
|
||||||
const startRegex = new RegExp(`^${pattern}($| )`, 'gi')
|
const result = regex.exec(text)
|
||||||
const startResult = startRegex.exec(text)
|
if (result === null) return null
|
||||||
if (startResult !== null) {
|
|
||||||
return startResult
|
|
||||||
}
|
|
||||||
|
|
||||||
// Pass 2: try matching at the end of the text
|
const matchEnd = result.index + result[0].length
|
||||||
const endRegex = new RegExp(`(^| )${pattern}$`, 'gi')
|
const isAtStart = result.index === 0
|
||||||
return endRegex.exec(text)
|
const isAtEnd = matchEnd >= text.length
|
||||||
|
|
||||||
|
if (isAtStart || isAtEnd) return result
|
||||||
|
|
||||||
|
// Allow middle-of-text matches when followed by a time expression
|
||||||
|
const afterMatch = text.substring(matchEnd)
|
||||||
|
if (/^(at |@ )/i.test(afterMatch)) return result
|
||||||
|
|
||||||
|
return null
|
||||||
}
|
}
|
||||||
|
|
||||||
function matchesDateExpr(text: string, dateExpr: string): boolean {
|
function matchesDateExpr(text: string, dateExpr: string): boolean {
|
||||||
|
|||||||
@@ -392,6 +392,8 @@ describe('Parse Task Text', () => {
|
|||||||
const boundaryTests = [
|
const boundaryTests = [
|
||||||
{input: '9/11 meeting', dateStr: '2021-9-11', text: 'meeting'},
|
{input: '9/11 meeting', dateStr: '2021-9-11', text: 'meeting'},
|
||||||
{input: 'meeting 9/11', dateStr: '2021-9-11', text: 'meeting'},
|
{input: 'meeting 9/11', dateStr: '2021-9-11', text: 'meeting'},
|
||||||
|
{input: 'meeting 9/11 at 10:00', dateStr: '2021-9-11', text: 'meeting'},
|
||||||
|
{input: 'meeting 9/11 @ 15:00', dateStr: '2021-9-11', text: 'meeting'},
|
||||||
{input: '2021-06-24 Lorem Ipsum', dateStr: '2021-6-24', text: 'Lorem Ipsum'},
|
{input: '2021-06-24 Lorem Ipsum', dateStr: '2021-6-24', text: 'Lorem Ipsum'},
|
||||||
{input: 'Lorem Ipsum 06/26/2021', dateStr: '2021-6-26', text: 'Lorem Ipsum'},
|
{input: 'Lorem Ipsum 06/26/2021', dateStr: '2021-6-26', text: 'Lorem Ipsum'},
|
||||||
{input: '01.02 Lorem Ipsum', dateStr: '2022-2-1', text: 'Lorem Ipsum'},
|
{input: '01.02 Lorem Ipsum', dateStr: '2022-2-1', text: 'Lorem Ipsum'},
|
||||||
|
|||||||
Reference in New Issue
Block a user