diff --git a/lib/utils/common-utils.ts b/lib/utils/common-utils.ts index e89ee3fa1..532760672 100644 --- a/lib/utils/common-utils.ts +++ b/lib/utils/common-utils.ts @@ -1,4 +1,4 @@ -import parseDate from '@/utils/parse-date'; +import { parseDate } from '@/utils/parse-date'; const title = require('title'); // convert a string into title case @@ -21,7 +21,7 @@ const convertDateToISO8601 = (date?: string | Date | number | null) => { } if (typeof date !== 'object') { // some routes may call `.toUTCString()` before passing the date to ctx... - date = parseDate.parse(date); + date = parseDate(date); } return date.toISOString(); }; diff --git a/test/utils/parse-date.js b/lib/utils/parse-date.test.ts similarity index 97% rename from test/utils/parse-date.js rename to lib/utils/parse-date.test.ts index d1dc703f8..d5774f73e 100644 --- a/test/utils/parse-date.js +++ b/lib/utils/parse-date.test.ts @@ -1,5 +1,6 @@ -const { parseRelativeDate } = require('@/utils/parse-date'); -const MockDate = require('mockdate'); +import { describe, expect, it } from '@jest/globals'; +import { parseRelativeDate } from '@/utils/parse-date'; +import MockDate from 'mockdate'; describe('parseRelativeDate', () => { const second = 1000; diff --git a/lib/utils/parse-date.ts b/lib/utils/parse-date.ts index 7f43f0944..98b28143f 100644 --- a/lib/utils/parse-date.ts +++ b/lib/utils/parse-date.ts @@ -125,84 +125,83 @@ const toDurations = (matches: string[]) => { return durations; }; -export default { - parse: (date: string | number, ...options: any) => dayjs(date, ...options).toDate(), - parseRelativeDate: (date: string) => { - // 预处理日期字符串 date +export const parseDate = (date: string | number, ...options: any) => dayjs(date, ...options).toDate(); - const theDate = toDate(date); +export const parseRelativeDate = (date: string) => { + // 预处理日期字符串 date - // 将 `\d+年\d+月...\d+秒前` 分割成 `['\d+年', ..., '\d+秒前']` + const theDate = toDate(date); - const matches = theDate.match(/(?:\D+)?\d+(?!:|-|\/|(a|p)m)\D+/g); + // 将 `\d+年\d+月...\d+秒前` 分割成 `['\d+年', ..., '\d+秒前']` - if (matches) { - // 获得最后的时间单元,如 `\d+秒前` + const matches = theDate.match(/(?:\D+)?\d+(?!:|-|\/|(a|p)m)\D+/g); - const lastMatch = matches.pop(); + if (matches) { + // 获得最后的时间单元,如 `\d+秒前` - if (lastMatch) { - // 若最后的时间单元含有 `前`、`以前`、`之前` 等标识字段,减去相应的时间长度 - // 如 `1分10秒前` + const lastMatch = matches.pop(); - const beforeMatches = /(.*)(?:[之以]?前|ago)$/.exec(lastMatch); - if (beforeMatches) { - matches.push(beforeMatches[1]); - return dayjs() - .subtract(dayjs.duration(toDurations(matches))) - .toDate(); - } + if (lastMatch) { + // 若最后的时间单元含有 `前`、`以前`、`之前` 等标识字段,减去相应的时间长度 + // 如 `1分10秒前` - // 若最后的时间单元含有 `后`、`以后`、`之后` 等标识字段,加上相应的时间长度 - // 如 `1分10秒后` + const beforeMatches = /(.*)(?:[之以]?前|ago)$/.exec(lastMatch); + if (beforeMatches) { + matches.push(beforeMatches[1]); + return dayjs() + .subtract(dayjs.duration(toDurations(matches))) + .toDate(); + } - const afterMatches = /(?:^in(.*)|(.*)[之以]?[后後])$/.exec(lastMatch); - if (afterMatches) { - matches.push(afterMatches[1] ?? afterMatches[2]); - return dayjs() + // 若最后的时间单元含有 `后`、`以后`、`之后` 等标识字段,加上相应的时间长度 + // 如 `1分10秒后` + + const afterMatches = /(?:^in(.*)|(.*)[之以]?[后後])$/.exec(lastMatch); + if (afterMatches) { + matches.push(afterMatches[1] ?? afterMatches[2]); + return dayjs() + .add(dayjs.duration(toDurations(matches))) + .toDate(); + } + + // 以下处理日期字符串 date 含有特殊词的情形 + // 如 `今天1点10分` + + matches.push(lastMatch); + } + const firstMatch = matches.shift(); + + if (firstMatch) { + for (const w of words) { + const wordMatches = w.regExp.exec(firstMatch); + if (wordMatches) { + matches.unshift(wordMatches[1]); + + // 取特殊词对应日零时为起点,加上相应的时间长度 + + return w.startAt + .set('hour', 0) + .set('minute', 0) + .set('second', 0) + .set('millisecond', 0) .add(dayjs.duration(toDurations(matches))) .toDate(); } - - // 以下处理日期字符串 date 含有特殊词的情形 - // 如 `今天1点10分` - - matches.push(lastMatch); - } - const firstMatch = matches.shift(); - - if (firstMatch) { - for (const w of words) { - const wordMatches = w.regExp.exec(firstMatch); - if (wordMatches) { - matches.unshift(wordMatches[1]); - - // 取特殊词对应日零时为起点,加上相应的时间长度 - - return w.startAt - .set('hour', 0) - .set('minute', 0) - .set('second', 0) - .set('millisecond', 0) - .add(dayjs.duration(toDurations(matches))) - .toDate(); - } - } - } - } else { - // 若日期字符串 date 不匹配 patterns 中所有模式,则默认为 `特殊词 + 标准时间格式` 的情形,此时直接将特殊词替换为对应日期 - // 如今天为 `2022-03-22`,则 `今天 20:00` => `2022-03-22 20:00` - - for (const w of words) { - const wordMatches = w.regExp.exec(theDate); - if (wordMatches) { - // The default parser of dayjs() can parse '8:00 pm' but not '8:00pm' - // so we need to insert a space in between - return dayjs(`${w.startAt.format('YYYY-MM-DD')} ${/a|pm$/.test(wordMatches[1]) ? wordMatches[1].replace(/a|pm/, ' $&') : wordMatches[1]}`).toDate(); - } } } + } else { + // 若日期字符串 date 不匹配 patterns 中所有模式,则默认为 `特殊词 + 标准时间格式` 的情形,此时直接将特殊词替换为对应日期 + // 如今天为 `2022-03-22`,则 `今天 20:00` => `2022-03-22 20:00` - return date; - }, + for (const w of words) { + const wordMatches = w.regExp.exec(theDate); + if (wordMatches) { + // The default parser of dayjs() can parse '8:00 pm' but not '8:00pm' + // so we need to insert a space in between + return dayjs(`${w.startAt.format('YYYY-MM-DD')} ${/a|pm$/.test(wordMatches[1]) ? wordMatches[1].replace(/a|pm/, ' $&') : wordMatches[1]}`).toDate(); + } + } + } + + return date; };