fix(quick add magic): time parsing for certain conditions (#2367)
continuous-integration/drone/push Build is passing Details

Co-authored-by: kolaente <k@knt.li>
Reviewed-on: #2367
Reviewed-by: Dominik Pschenitschni <dpschen@noreply.kolaente.de>
This commit is contained in:
konrad 2022-09-15 11:59:29 +00:00
parent 2df2bd38e2
commit b24d5f2dce
3 changed files with 64 additions and 22 deletions

View File

@ -11,7 +11,7 @@
"build:dev": "vite build -m development --outDir dist-dev/",
"lint": "eslint --ignore-pattern '*.test.*' ./src --ext .vue,.js,.ts",
"cypress:open": "cypress open",
"test:unit": "vitest",
"test:unit": "vitest --run",
"test:unit-watch": "vitest watch",
"test:frontend": "cypress run",
"typecheck": "vue-tsc --noEmit && vue-tsc --noEmit -p tsconfig.vitest.json --composite false",

View File

@ -12,7 +12,9 @@ interface dateFoundResult {
date: Date | null,
}
export const parseDate = (text: string): dateParseResult => {
const monthsRegexGroup = '(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)'
export const parseDate = (text: string, now: Date = new Date()): dateParseResult => {
const lowerText: string = text.toLowerCase()
if (lowerText.includes('today')) {
@ -62,39 +64,44 @@ export const parseDate = (text: string): dateParseResult => {
}
parsed = getDayFromText(text)
if (parsed.date !== null) {
const month = getMonthFromText(text, parsed.date)
return addTimeToDate(text, month.date, parsed.foundText)
}
parsed = getDateFromTextIn(text, now)
if (parsed.date !== null) {
return addTimeToDate(text, parsed.date, parsed.foundText)
}
parsed = getDateFromTextIn(text)
if (parsed.date !== null) {
parsed = getDateFromText(text)
if (parsed.date === null) {
return {
newText: replaceAll(text, parsed.foundText, ''),
date: parsed.date,
}
}
parsed = getDateFromText(text)
return {
newText: replaceAll(text, parsed.foundText, ''),
date: parsed.date,
}
return addTimeToDate(text, parsed.date, parsed.foundText)
}
const addTimeToDate = (text: string, date: Date, match: string | null): dateParseResult => {
if (match === null) {
const addTimeToDate = (text: string, date: Date, previousMatch: string | null): dateParseResult => {
previousMatch = previousMatch?.trim() || ''
text = replaceAll(text, previousMatch, '')
if (previousMatch === null) {
return {
newText: text,
date: null,
}
}
const matcher = new RegExp(`(${match} (at|@) )([0-9][0-9]?(:[0-9][0-9]?)?( ?(a|p)m)?)`, 'ig')
const timeRegex = ' (at|@) ([0-9][0-9]?(:[0-9][0-9]?)?( ?(a|p)m)?)'
const matcher = new RegExp(timeRegex, 'ig')
const results = matcher.exec(text)
if (results !== null) {
const time = results[3]
const time = results[2]
const parts = time.split(':')
let hours = parseInt(parts[0])
let minutes = 0
@ -110,7 +117,7 @@ const addTimeToDate = (text: string, date: Date, match: string | null): datePars
date.setSeconds(0)
}
const replace = results !== null ? results[0] : match
const replace = results !== null ? results[0] : previousMatch
return {
newText: replaceAll(text, replace, ''),
date: date,
@ -127,10 +134,10 @@ export const getDateFromText = (text: string, now: Date = new Date()) => {
let containsYear: boolean = true
if (result === null) {
// 2. Try parsing the date as something like "jan 21" or "21 jan"
const monthRegex: RegExp = / ((jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec) [0-9][0-9]?|[0-9][0-9]? (jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec))/ig
const monthRegex: RegExp = new RegExp(` (${monthsRegexGroup} [0-9][0-9]?|[0-9][0-9]? ${monthsRegexGroup})`, 'ig')
results = monthRegex.exec(text)
result = results === null ? null : `${results[0]} ${now.getFullYear()}`
foundText = results === null ? '' : results[0]
result = results === null ? null : `${results[0]} ${now.getFullYear()}`.trim()
foundText = results === null ? '' : results[0].trim()
containsYear = false
if (result === null) {
@ -309,7 +316,7 @@ const getDayFromText = (text: string) => {
while (date < now) {
date.setMonth(date.getMonth() + 1)
}
if (date.getDate() !== day) {
date.setDate(day)
}
@ -320,6 +327,25 @@ const getDayFromText = (text: string) => {
}
}
const getMonthFromText = (text: string, date: Date) => {
const matcher = new RegExp(monthsRegexGroup, 'ig')
const results = matcher.exec(text)
if (results === null) {
return {
newText: text,
date,
}
}
const fullDate = new Date(`${results[0]} 1 ${(new Date()).getFullYear()}`)
date.setMonth(fullDate.getMonth())
return {
newText: replaceAll(text, results[0], ''),
date,
}
}
const getDateFromInterval = (interval: number): Date => {
const newDate = new Date()
newDate.setDate(newDate.getDate() + interval)

View File

@ -1,7 +1,7 @@
import {beforeEach, afterEach, describe, it, expect, vi} from 'vitest'
import {parseTaskText, PrefixMode} from './parseTaskText'
import {getDateFromText, getDateFromTextIn} from '../helpers/time/parseDate'
import {getDateFromText, getDateFromTextIn, parseDate} from '../helpers/time/parseDate'
import {calculateDayInterval} from '../helpers/time/calculateDayInterval'
import {PRIORITIES} from '@/constants/priorities'
@ -359,7 +359,7 @@ describe('Parse Task Text', () => {
it('should not recognize dates in urls', () => {
const text = 'https://some-url.org/blog/2019/1/233526-some-more-text'
const result = parseTaskText(text)
expect(result.text).toBe(text)
expect(result.date).toBeNull()
})
@ -483,6 +483,15 @@ describe('Parse Task Text', () => {
now.setMinutes(0)
now.setSeconds(0)
beforeEach(() => {
vi.useFakeTimers()
vi.setSystemTime(now)
})
afterEach(() => {
vi.useRealTimers()
})
const cases = {
'Lorem Ipsum in 1 hour': '2021-6-24 13:0',
'in 2 hours': '2021-6-24 14:0',
@ -493,11 +502,18 @@ describe('Parse Task Text', () => {
'in 4 weeks': '2021-7-22 12:0',
'in 1 month': '2021-7-24 12:0',
'in 3 months': '2021-9-24 12:0',
'Something in 5 days at 10:00': '2021-6-29 10:0',
'Something 17th at 10:00': '2021-7-17 10:0',
'Something sep 17 at 10:00': '2021-9-17 10:0',
'Something sep 17th at 10:00': '2021-9-17 10:0',
'Something at 10:00 in 5 days': '2021-6-29 10:0',
'Something at 10:00 17th': '2021-7-17 10:0',
'Something at 10:00 sep 17th': '2021-9-17 10:0',
}
for (const c in cases) {
it(`should parse '${c}' as '${cases[c]}'`, () => {
const {date} = getDateFromTextIn(c, now)
const {date} = parseDate(c, now)
if (date === null && cases[c] === null) {
expect(date).toBeNull()
return