Fix date parsing parsing words with weekdays in them (#607)
Co-authored-by: kolaente <k@knt.li> Reviewed-on: https://kolaente.dev/vikunja/frontend/pulls/607 Co-authored-by: konrad <konrad@kola-entertainments.de> Co-committed-by: konrad <konrad@kola-entertainments.de>
This commit is contained in:
@ -1,103 +0,0 @@
|
||||
import {parseDate} from './time/parseDate'
|
||||
import priorities from '../models/priorities.json'
|
||||
|
||||
const LABEL_PREFIX = '@'
|
||||
const LIST_PREFIX = '#'
|
||||
const PRIORITY_PREFIX = '!'
|
||||
const ASSIGNEE_PREFIX = '+'
|
||||
|
||||
/**
|
||||
* Parses task text for dates, assignees, labels, lists, priorities and returns an object with all found intents.
|
||||
*
|
||||
* @param text
|
||||
*/
|
||||
export const parseTaskText = text => {
|
||||
const result = {
|
||||
text: text,
|
||||
date: null,
|
||||
labels: [],
|
||||
list: null,
|
||||
priority: null,
|
||||
assignees: [],
|
||||
}
|
||||
|
||||
result.labels = getItemsFromPrefix(text, LABEL_PREFIX)
|
||||
|
||||
const lists = getItemsFromPrefix(text, LIST_PREFIX)
|
||||
result.list = lists.length > 0 ? lists[0] : null
|
||||
|
||||
result.priority = getPriority(text)
|
||||
|
||||
result.assignees = getItemsFromPrefix(text, ASSIGNEE_PREFIX)
|
||||
|
||||
const {newText, date} = parseDate(text)
|
||||
result.text = newText
|
||||
result.date = date
|
||||
|
||||
return cleanupResult(result)
|
||||
}
|
||||
|
||||
const getItemsFromPrefix = (text, prefix) => {
|
||||
const items = []
|
||||
|
||||
const itemParts = text.split(prefix)
|
||||
itemParts.forEach((p, index) => {
|
||||
// First part contains the rest
|
||||
if (index < 1) {
|
||||
return
|
||||
}
|
||||
|
||||
let labelText
|
||||
if (p.charAt(0) === '\'') {
|
||||
labelText = p.split('\'')[1]
|
||||
} else if (p.charAt(0) === '"') {
|
||||
labelText = p.split('"')[1]
|
||||
} else {
|
||||
// Only until the next space
|
||||
labelText = p.split(' ')[0]
|
||||
}
|
||||
items.push(labelText)
|
||||
})
|
||||
|
||||
return Array.from(new Set(items))
|
||||
}
|
||||
|
||||
const getPriority = text => {
|
||||
const ps = getItemsFromPrefix(text, PRIORITY_PREFIX)
|
||||
if (ps.length === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
for (const p of ps) {
|
||||
for (const pi in priorities) {
|
||||
if (priorities[pi] === parseInt(p)) {
|
||||
return parseInt(p)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
const cleanupItemText = (text, items, prefix) => {
|
||||
items.forEach(l => {
|
||||
text = text
|
||||
.replace(`${prefix}'${l}' `, '')
|
||||
.replace(`${prefix}'${l}'`, '')
|
||||
.replace(`${prefix}"${l}" `, '')
|
||||
.replace(`${prefix}"${l}"`, '')
|
||||
.replace(`${prefix}${l} `, '')
|
||||
.replace(`${prefix}${l}`, '')
|
||||
})
|
||||
return text
|
||||
}
|
||||
|
||||
const cleanupResult = result => {
|
||||
result.text = cleanupItemText(result.text, result.labels, LABEL_PREFIX)
|
||||
result.text = cleanupItemText(result.text, [result.list], LIST_PREFIX)
|
||||
result.text = cleanupItemText(result.text, [result.priority], PRIORITY_PREFIX)
|
||||
result.text = cleanupItemText(result.text, result.assignees, ASSIGNEE_PREFIX)
|
||||
result.text = result.text.trim()
|
||||
|
||||
return result
|
||||
}
|
@ -1,407 +0,0 @@
|
||||
import {parseTaskText} from './parseTaskText'
|
||||
import {getDateFromText, getDateFromTextIn} from './time/parseDate'
|
||||
import {calculateDayInterval} from './time/calculateDayInterval'
|
||||
import priorities from '../models/priorities.json'
|
||||
|
||||
describe('Parse Task Text', () => {
|
||||
it('should return text with no intents as is', () => {
|
||||
expect(parseTaskText('Lorem Ipsum').text).toBe('Lorem Ipsum')
|
||||
})
|
||||
|
||||
describe('Date Parsing', () => {
|
||||
it('should not return any date if none was provided', () => {
|
||||
const result = parseTaskText('Lorem Ipsum')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.date).toBeNull()
|
||||
})
|
||||
it('should ignore casing', () => {
|
||||
const result = parseTaskText('Lorem Ipsum ToDay')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const now = new Date()
|
||||
expect(result.date.getFullYear()).toBe(now.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(now.getMonth())
|
||||
expect(result.date.getDate()).toBe(now.getDate())
|
||||
})
|
||||
it('should recognize today', () => {
|
||||
const result = parseTaskText('Lorem Ipsum today')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const now = new Date()
|
||||
expect(result.date.getFullYear()).toBe(now.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(now.getMonth())
|
||||
expect(result.date.getDate()).toBe(now.getDate())
|
||||
})
|
||||
describe('should recognize today with a time', () => {
|
||||
const cases = {
|
||||
'at 15:00': '15:0',
|
||||
'@ 15:00': '15:0',
|
||||
'at 15:30': '15:30',
|
||||
'@ 3pm': '15:0',
|
||||
'at 3pm': '15:0',
|
||||
'at 3 pm': '15:0',
|
||||
'at 3am': '3:0',
|
||||
'at 3:12 am': '3:12',
|
||||
'at 3:12 pm': '15:12',
|
||||
}
|
||||
|
||||
for (const c in cases) {
|
||||
it('should recognize today with a time ' + c, () => {
|
||||
const result = parseTaskText('Lorem Ipsum today ' + c)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const now = new Date()
|
||||
expect(result.date.getFullYear()).toBe(now.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(now.getMonth())
|
||||
expect(result.date.getDate()).toBe(now.getDate())
|
||||
expect(`${result.date.getHours()}:${result.date.getMinutes()}`).toBe(cases[c])
|
||||
expect(result.date.getSeconds()).toBe(0)
|
||||
})
|
||||
}
|
||||
})
|
||||
it('should recognize tomorrow', () => {
|
||||
const result = parseTaskText('Lorem Ipsum tomorrow')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const tomorrow = new Date()
|
||||
tomorrow.setDate(tomorrow.getDate() + 1)
|
||||
expect(result.date.getFullYear()).toBe(tomorrow.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(tomorrow.getMonth())
|
||||
expect(result.date.getDate()).toBe(tomorrow.getDate())
|
||||
})
|
||||
it('should recognize next monday', () => {
|
||||
const result = parseTaskText('Lorem Ipsum next monday')
|
||||
|
||||
const untilNextMonday = calculateDayInterval('nextMonday')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const nextMonday = new Date()
|
||||
nextMonday.setDate(nextMonday.getDate() + untilNextMonday)
|
||||
expect(result.date.getFullYear()).toBe(nextMonday.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(nextMonday.getMonth())
|
||||
expect(result.date.getDate()).toBe(nextMonday.getDate())
|
||||
})
|
||||
it('should recognize this weekend', () => {
|
||||
const result = parseTaskText('Lorem Ipsum this weekend')
|
||||
|
||||
const untilThisWeekend = calculateDayInterval('thisWeekend')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const thisWeekend = new Date()
|
||||
thisWeekend.setDate(thisWeekend.getDate() + untilThisWeekend)
|
||||
expect(result.date.getFullYear()).toBe(thisWeekend.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(thisWeekend.getMonth())
|
||||
expect(result.date.getDate()).toBe(thisWeekend.getDate())
|
||||
})
|
||||
it('should recognize later this week', () => {
|
||||
const result = parseTaskText('Lorem Ipsum later this week')
|
||||
|
||||
const untilLaterThisWeek = calculateDayInterval('laterThisWeek')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const laterThisWeek = new Date()
|
||||
laterThisWeek.setDate(laterThisWeek.getDate() + untilLaterThisWeek)
|
||||
expect(result.date.getFullYear()).toBe(laterThisWeek.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(laterThisWeek.getMonth())
|
||||
expect(result.date.getDate()).toBe(laterThisWeek.getDate())
|
||||
})
|
||||
it('should recognize later next week', () => {
|
||||
const result = parseTaskText('Lorem Ipsum later next week')
|
||||
|
||||
const untilLaterNextWeek = calculateDayInterval('laterNextWeek')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const laterNextWeek = new Date()
|
||||
laterNextWeek.setDate(laterNextWeek.getDate() + untilLaterNextWeek)
|
||||
expect(result.date.getFullYear()).toBe(laterNextWeek.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(laterNextWeek.getMonth())
|
||||
expect(result.date.getDate()).toBe(laterNextWeek.getDate())
|
||||
})
|
||||
it('should recognize next week', () => {
|
||||
const result = parseTaskText('Lorem Ipsum next week')
|
||||
|
||||
const untilNextWeek = calculateDayInterval('nextWeek')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const nextWeek = new Date()
|
||||
nextWeek.setDate(nextWeek.getDate() + untilNextWeek)
|
||||
expect(result.date.getFullYear()).toBe(nextWeek.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(nextWeek.getMonth())
|
||||
expect(result.date.getDate()).toBe(nextWeek.getDate())
|
||||
})
|
||||
it('should recognize next month', () => {
|
||||
const result = parseTaskText('Lorem Ipsum next month')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const nextMonth = new Date()
|
||||
nextMonth.setDate(1)
|
||||
nextMonth.setMonth(nextMonth.getMonth() + 1)
|
||||
expect(result.date.getFullYear()).toBe(nextMonth.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(nextMonth.getMonth())
|
||||
expect(result.date.getDate()).toBe(nextMonth.getDate())
|
||||
})
|
||||
it('should recognize a date', () => {
|
||||
const result = parseTaskText('Lorem Ipsum 06/26/2021')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const date = new Date()
|
||||
date.setFullYear(2021, 5, 26)
|
||||
expect(result.date.getFullYear()).toBe(date.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(date.getMonth())
|
||||
expect(result.date.getDate()).toBe(date.getDate())
|
||||
})
|
||||
it('should recognize end of month', () => {
|
||||
const result = parseTaskText('Lorem Ipsum end of month')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const curDate = new Date()
|
||||
const date = new Date(curDate.getFullYear(), curDate.getMonth() + 1, 0)
|
||||
expect(result.date.getFullYear()).toBe(date.getFullYear())
|
||||
expect(result.date.getMonth()).toBe(date.getMonth())
|
||||
expect(result.date.getDate()).toBe(date.getDate())
|
||||
})
|
||||
it('should recognize weekdays', () => {
|
||||
const result = parseTaskText('Lorem Ipsum thu')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const nextThursday = new Date()
|
||||
nextThursday.setDate(nextThursday.getDate() + ((4 + 7 - nextThursday.getDay()) % 7))
|
||||
expect(`${result.date.getFullYear()}-${result.date.getMonth()}-${result.date.getDate()}`).toBe(`${nextThursday.getFullYear()}-${nextThursday.getMonth()}-${nextThursday.getDate()}`)
|
||||
})
|
||||
it('should recognize weekdays with time', () => {
|
||||
const result = parseTaskText('Lorem Ipsum thu at 14:00')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
const nextThursday = new Date()
|
||||
nextThursday.setDate(nextThursday.getDate() + ((4 + 7 - nextThursday.getDay()) % 7))
|
||||
expect(`${result.date.getFullYear()}-${result.date.getMonth()}-${result.date.getDate()}`).toBe(`${nextThursday.getFullYear()}-${nextThursday.getMonth()}-${nextThursday.getDate()}`)
|
||||
expect(`${result.date.getHours()}:${result.date.getMinutes()}`).toBe('14:0')
|
||||
})
|
||||
it('should recognize dates of the month in the past but next month', () => {
|
||||
const date = new Date()
|
||||
date.setDate(date.getDate() - 1)
|
||||
const result = parseTaskText(`Lorem Ipsum ${date.getDate()}nd`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.date.getDate()).toBe(date.getDate())
|
||||
expect(result.date.getMonth()).toBe(date.getMonth() + 1)
|
||||
})
|
||||
it('should recognize dates of the month in the future', () => {
|
||||
const date = new Date()
|
||||
const result = parseTaskText(`Lorem Ipsum ${date.getDate() + 1}nd`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.date.getDate()).toBe(date.getDate() + 1)
|
||||
})
|
||||
|
||||
describe('Parse date from text', () => {
|
||||
const now = new Date()
|
||||
now.setFullYear(2021, 5, 24)
|
||||
|
||||
const cases = {
|
||||
'Lorem Ipsum 06/08/2021 ad': '2021-6-8',
|
||||
'Lorem Ipsum 6/7/21 ad': '2021-6-7',
|
||||
'27/07/2021,': null,
|
||||
'2021/07/06,': '2021-7-6',
|
||||
'2021-07-06': '2021-7-6',
|
||||
'27 jan': '2022-1-27',
|
||||
'27/1': '2022-1-27',
|
||||
'27/01': '2022-1-27',
|
||||
'16/12': '2021-12-16',
|
||||
'01/27': '2022-1-27',
|
||||
'1/27': '2022-1-27',
|
||||
'Jan 27': '2022-1-27',
|
||||
'jan 27': '2022-1-27',
|
||||
'feb 21': '2022-2-21',
|
||||
'mar 21': '2022-3-21',
|
||||
'apr 21': '2022-4-21',
|
||||
'may 21': '2022-5-21',
|
||||
'jun 21': '2022-6-21',
|
||||
'jul 21': '2021-7-21',
|
||||
'aug 21': '2021-8-21',
|
||||
'sep 21': '2021-9-21',
|
||||
'oct 21': '2021-10-21',
|
||||
'nov 21': '2021-11-21',
|
||||
'dec 21': '2021-12-21',
|
||||
}
|
||||
|
||||
for (const c in cases) {
|
||||
it(`should parse '${c}' as '${cases[c]}'`, () => {
|
||||
const {date} = getDateFromText(c, now)
|
||||
if (date === null && cases[c] === null) {
|
||||
expect(date).toBeNull()
|
||||
return
|
||||
}
|
||||
|
||||
expect(`${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()}`).toBe(cases[c])
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
describe('Parse date from text in', () => {
|
||||
const now = new Date()
|
||||
now.setFullYear(2021, 5, 24)
|
||||
now.setHours(12)
|
||||
now.setMinutes(0)
|
||||
now.setSeconds(0)
|
||||
|
||||
const cases = {
|
||||
'Lorem Ipsum in 1 hour': '2021-6-24 13:0',
|
||||
'in 2 hours': '2021-6-24 14:0',
|
||||
'in 1 day': '2021-6-25 12:0',
|
||||
'in 2 days': '2021-6-26 12:0',
|
||||
'in 1 week': '2021-7-1 12:0',
|
||||
'in 2 weeks': '2021-7-8 12:0',
|
||||
'in 4 weeks': '2021-7-22 12:0',
|
||||
'in 1 month': '2021-7-24 12:0',
|
||||
'in 3 months': '2021-9-24 12:0',
|
||||
}
|
||||
|
||||
for (const c in cases) {
|
||||
it(`should parse '${c}' as '${cases[c]}'`, () => {
|
||||
const {date} = getDateFromTextIn(c, now)
|
||||
if (date === null && cases[c] === null) {
|
||||
expect(date).toBeNull()
|
||||
return
|
||||
}
|
||||
|
||||
expect(`${date.getFullYear()}-${date.getMonth() + 1}-${date.getDate()} ${date.getHours()}:${date.getMinutes()}`).toBe(cases[c])
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
})
|
||||
|
||||
describe('Labels', () => {
|
||||
it('should parse labels', () => {
|
||||
const result = parseTaskText('Lorem Ipsum @label1 @label2')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.labels).toHaveLength(2)
|
||||
expect(result.labels[0]).toBe('label1')
|
||||
expect(result.labels[1]).toBe('label2')
|
||||
})
|
||||
it('should parse labels from the start', () => {
|
||||
const result = parseTaskText('@label1 Lorem Ipsum @label2')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.labels).toHaveLength(2)
|
||||
expect(result.labels[0]).toBe('label1')
|
||||
expect(result.labels[1]).toBe('label2')
|
||||
})
|
||||
it('should resolve duplicate labels', () => {
|
||||
const result = parseTaskText('Lorem Ipsum @label1 @label1 @label2')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.labels).toHaveLength(2)
|
||||
expect(result.labels[0]).toBe('label1')
|
||||
expect(result.labels[1]).toBe('label2')
|
||||
})
|
||||
it('should correctly parse labels with spaces in them', () => {
|
||||
const result = parseTaskText(`Lorem @'label with space' Ipsum`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.labels).toHaveLength(1)
|
||||
expect(result.labels[0]).toBe('label with space')
|
||||
})
|
||||
it('should correctly parse labels with spaces in them and "', () => {
|
||||
const result = parseTaskText('Lorem @"label with space" Ipsum')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.labels).toHaveLength(1)
|
||||
expect(result.labels[0]).toBe('label with space')
|
||||
})
|
||||
})
|
||||
|
||||
describe('List', () => {
|
||||
it('should parse a list', () => {
|
||||
const result = parseTaskText('Lorem Ipsum #list')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.list).toBe('list')
|
||||
})
|
||||
it('should parse a list with a space in it', () => {
|
||||
const result = parseTaskText(`Lorem Ipsum #'list with long name'`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.list).toBe('list with long name')
|
||||
})
|
||||
it('should parse a list with a space in it and "', () => {
|
||||
const result = parseTaskText(`Lorem Ipsum #"list with long name"`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.list).toBe('list with long name')
|
||||
})
|
||||
it('should parse only the first list', () => {
|
||||
const result = parseTaskText(`Lorem Ipsum #list1 #list2 #list3`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum #list2 #list3')
|
||||
expect(result.list).toBe('list1')
|
||||
})
|
||||
})
|
||||
|
||||
describe('Priority', () => {
|
||||
for (const p in priorities) {
|
||||
it(`should parse priority ${p}`, () => {
|
||||
const result = parseTaskText(`Lorem Ipsum !${priorities[p]}`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.priority).toBe(priorities[p])
|
||||
})
|
||||
}
|
||||
it(`should not parse an invalid priority`, () => {
|
||||
const result = parseTaskText(`Lorem Ipsum !9999`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum !9999')
|
||||
expect(result.priority).toBe(null)
|
||||
})
|
||||
it(`should not parse an invalid priority but use the first valid one it finds`, () => {
|
||||
const result = parseTaskText(`Lorem Ipsum !9999 !1`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum !9999')
|
||||
expect(result.priority).toBe(1)
|
||||
})
|
||||
})
|
||||
|
||||
describe('Assignee', () => {
|
||||
it('should parse an assignee', () => {
|
||||
const result = parseTaskText('Lorem Ipsum +user')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.assignees).toHaveLength(1)
|
||||
expect(result.assignees[0]).toBe('user')
|
||||
})
|
||||
it('should parse multiple assignees', () => {
|
||||
const result = parseTaskText('Lorem Ipsum +user1 +user2 +user3')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.assignees).toHaveLength(3)
|
||||
expect(result.assignees[0]).toBe('user1')
|
||||
expect(result.assignees[1]).toBe('user2')
|
||||
expect(result.assignees[2]).toBe('user3')
|
||||
})
|
||||
it('should parse avoid duplicate assignees', () => {
|
||||
const result = parseTaskText('Lorem Ipsum +user1 +user1 +user2')
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.assignees).toHaveLength(2)
|
||||
expect(result.assignees[0]).toBe('user1')
|
||||
expect(result.assignees[1]).toBe('user2')
|
||||
})
|
||||
it('should parse an assignee with a space in it', () => {
|
||||
const result = parseTaskText(`Lorem Ipsum +'user with long name'`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.assignees).toHaveLength(1)
|
||||
expect(result.assignees[0]).toBe('user with long name')
|
||||
})
|
||||
it('should parse an assignee with a space in it and "', () => {
|
||||
const result = parseTaskText(`Lorem Ipsum +"user with long name"`)
|
||||
|
||||
expect(result.text).toBe('Lorem Ipsum')
|
||||
expect(result.assignees).toHaveLength(1)
|
||||
expect(result.assignees[0]).toBe('user with long name')
|
||||
})
|
||||
})
|
||||
})
|
@ -1,4 +1,4 @@
|
||||
export function calculateNearestHours(currentDate = new Date()) {
|
||||
export function calculateNearestHours(currentDate: Date = new Date()): number {
|
||||
if (currentDate.getHours() <= 9 || currentDate.getHours() > 21) {
|
||||
return 9
|
||||
}
|
||||
@ -18,4 +18,7 @@ export function calculateNearestHours(currentDate = new Date()) {
|
||||
if (currentDate.getHours() <= 21) {
|
||||
return 21
|
||||
}
|
||||
|
||||
// Same case as in the first if, will never be called
|
||||
return 9
|
||||
}
|
@ -2,8 +2,18 @@ import {calculateDayInterval} from './calculateDayInterval'
|
||||
import {calculateNearestHours} from './calculateNearestHours'
|
||||
import {replaceAll} from '../replaceAll'
|
||||
|
||||
export const parseDate = text => {
|
||||
const lowerText = text.toLowerCase()
|
||||
interface dateParseResult {
|
||||
newText: string,
|
||||
date: Date | null,
|
||||
}
|
||||
|
||||
interface dateFoundResult {
|
||||
foundText: string | null,
|
||||
date: Date | null,
|
||||
}
|
||||
|
||||
export const parseDate = (text: string): dateParseResult => {
|
||||
const lowerText: string = text.toLowerCase()
|
||||
|
||||
if (lowerText.includes('today')) {
|
||||
return addTimeToDate(text, getDateFromInterval(calculateDayInterval('today')), 'today')
|
||||
@ -27,7 +37,7 @@ export const parseDate = text => {
|
||||
return addTimeToDate(text, getDateFromInterval(calculateDayInterval('nextWeek')), 'next week')
|
||||
}
|
||||
if (lowerText.includes('next month')) {
|
||||
const date = new Date()
|
||||
const date: Date = new Date()
|
||||
date.setDate(1)
|
||||
date.setMonth(date.getMonth() + 1)
|
||||
date.setHours(calculateNearestHours(date))
|
||||
@ -37,8 +47,8 @@ export const parseDate = text => {
|
||||
return addTimeToDate(text, date, 'next month')
|
||||
}
|
||||
if (lowerText.includes('end of month')) {
|
||||
const curDate = new Date()
|
||||
const date = new Date(curDate.getFullYear(), curDate.getMonth() + 1, 0)
|
||||
const curDate: Date = new Date()
|
||||
const date: Date = new Date(curDate.getFullYear(), curDate.getMonth() + 1, 0)
|
||||
date.setHours(calculateNearestHours(date))
|
||||
date.setMinutes(0)
|
||||
date.setSeconds(0)
|
||||
@ -72,7 +82,14 @@ export const parseDate = text => {
|
||||
}
|
||||
}
|
||||
|
||||
const addTimeToDate = (text, date, match) => {
|
||||
const addTimeToDate = (text: string, date: Date, match: string | null): dateParseResult => {
|
||||
if (match === 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 results = matcher.exec(text)
|
||||
|
||||
@ -100,17 +117,17 @@ const addTimeToDate = (text, date, match) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const getDateFromText = (text, now = new Date()) => {
|
||||
const fullDateRegex = /([0-9][0-9]?\/[0-9][0-9]?\/[0-9][0-9]([0-9][0-9])?|[0-9][0-9][0-9][0-9]\/[0-9][0-9]?\/[0-9][0-9]?|[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?)/ig
|
||||
export const getDateFromText = (text: string, now: Date = new Date()) => {
|
||||
const fullDateRegex: RegExp = /([0-9][0-9]?\/[0-9][0-9]?\/[0-9][0-9]([0-9][0-9])?|[0-9][0-9][0-9][0-9]\/[0-9][0-9]?\/[0-9][0-9]?|[0-9][0-9][0-9][0-9]-[0-9][0-9]?-[0-9][0-9]?)/ig
|
||||
|
||||
// 1. Try parsing the text as a "usual" date, like 2021-06-24 or 06/24/2021
|
||||
let results = fullDateRegex.exec(text)
|
||||
let result = results === null ? null : results[0]
|
||||
let foundText = result
|
||||
let containsYear = true
|
||||
let results: string[] | null = fullDateRegex.exec(text)
|
||||
let result: string | null = results === null ? null : results[0]
|
||||
let foundText: string | null = result
|
||||
let containsYear: boolean = true
|
||||
if (result === null) {
|
||||
// 2. Try parsing the date as something like "jan 21" or "21 jan"
|
||||
const monthRegex = /((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 = /((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
|
||||
results = monthRegex.exec(text)
|
||||
result = results === null ? null : `${results[0]} ${now.getFullYear()}`
|
||||
foundText = results === null ? '' : results[0]
|
||||
@ -118,17 +135,24 @@ export const getDateFromText = (text, now = new Date()) => {
|
||||
|
||||
if (result === null) {
|
||||
// 3. Try parsing the date as "27/01" or "01/27"
|
||||
const monthNumericRegex = /([0-9][0-9]?\/[0-9][0-9]?)/ig
|
||||
const monthNumericRegex:RegExp = /([0-9][0-9]?\/[0-9][0-9]?)/ig
|
||||
results = monthNumericRegex.exec(text)
|
||||
|
||||
// Put the year before or after the date, depending on what works
|
||||
result = results === null ? null : `${now.getFullYear()}/${results[0]}`
|
||||
if(result === null) {
|
||||
return {
|
||||
foundText,
|
||||
date: null,
|
||||
}
|
||||
}
|
||||
|
||||
foundText = results === null ? '' : results[0]
|
||||
if (isNaN(new Date(result))) {
|
||||
if (result === null || isNaN(new Date(result).getTime())) {
|
||||
result = results === null ? null : `${results[0]}/${now.getFullYear()}`
|
||||
}
|
||||
if (isNaN(new Date(result)) && results[0] !== null) {
|
||||
const parts = results[0].split('/')
|
||||
if (result === null || (isNaN(new Date(result).getTime()) && foundText !== '')) {
|
||||
const parts = foundText.split('/')
|
||||
result = `${parts[1]}/${parts[0]}/${now.getFullYear()}`
|
||||
}
|
||||
}
|
||||
@ -142,7 +166,7 @@ export const getDateFromText = (text, now = new Date()) => {
|
||||
}
|
||||
|
||||
const date = new Date(result)
|
||||
if (isNaN(date)) {
|
||||
if (isNaN(date.getTime())) {
|
||||
return {
|
||||
foundText,
|
||||
date: null,
|
||||
@ -159,7 +183,7 @@ export const getDateFromText = (text, now = new Date()) => {
|
||||
}
|
||||
}
|
||||
|
||||
export const getDateFromTextIn = (text, now = new Date()) => {
|
||||
export const getDateFromTextIn = (text: string, now: Date = new Date()) => {
|
||||
const regex = /(in [0-9]+ (hours?|days?|weeks?|months?))/ig
|
||||
const results = regex.exec(text)
|
||||
if (results === null) {
|
||||
@ -169,7 +193,7 @@ export const getDateFromTextIn = (text, now = new Date()) => {
|
||||
}
|
||||
}
|
||||
|
||||
let foundText = results[0]
|
||||
const foundText: string = results[0]
|
||||
const date = new Date(now)
|
||||
const parts = foundText.split(' ')
|
||||
switch (parts[2]) {
|
||||
@ -197,9 +221,9 @@ export const getDateFromTextIn = (text, now = new Date()) => {
|
||||
}
|
||||
}
|
||||
|
||||
const getDateFromWeekday = text => {
|
||||
const matcher = /(mon|monday|tue|tuesday|wed|wednesday|thu|thursday|fri|friday|sat|saturday|sun|sunday)/ig
|
||||
const results = matcher.exec(text)
|
||||
const getDateFromWeekday = (text: string): dateFoundResult => {
|
||||
const matcher: RegExp = / (mon|monday|tue|tuesday|wed|wednesday|thu|thursday|fri|friday|sat|saturday|sun|sunday)/ig
|
||||
const results: string[] | null = matcher.exec(text)
|
||||
if (results === null) {
|
||||
return {
|
||||
foundText: null,
|
||||
@ -207,11 +231,11 @@ const getDateFromWeekday = text => {
|
||||
}
|
||||
}
|
||||
|
||||
const date = new Date()
|
||||
const currentDay = date.getDay()
|
||||
let day = 0
|
||||
const date: Date = new Date()
|
||||
const currentDay: number = date.getDay()
|
||||
let day: number = 0
|
||||
|
||||
switch (results[0]) {
|
||||
switch (results[1]) {
|
||||
case 'mon':
|
||||
case 'monday':
|
||||
day = 1
|
||||
@ -247,16 +271,16 @@ const getDateFromWeekday = text => {
|
||||
}
|
||||
}
|
||||
|
||||
const distance = (day + 7 - currentDay) % 7
|
||||
const distance: number = (day + 7 - currentDay) % 7
|
||||
date.setDate(date.getDate() + distance)
|
||||
|
||||
return {
|
||||
foundText: results[0],
|
||||
foundText: results[1],
|
||||
date: date,
|
||||
}
|
||||
}
|
||||
|
||||
const getDayFromText = text => {
|
||||
const getDayFromText = (text: string) => {
|
||||
const matcher = /(([1-2][0-9])|(3[01])|(0?[1-9]))(st|nd|rd|th|\.)/ig
|
||||
const results = matcher.exec(text)
|
||||
if (results === null) {
|
||||
@ -279,7 +303,7 @@ const getDayFromText = text => {
|
||||
}
|
||||
}
|
||||
|
||||
const getDateFromInterval = interval => {
|
||||
const getDateFromInterval = (interval: number): Date => {
|
||||
const newDate = new Date()
|
||||
newDate.setDate(newDate.getDate() + interval)
|
||||
newDate.setHours(calculateNearestHours(newDate))
|
Reference in New Issue
Block a user