rsshub
Version:
Make RSS Great Again!
150 lines (148 loc) • 4.69 kB
JavaScript
import dayjs from "dayjs";
import customParseFormat from "dayjs/plugin/customParseFormat.js";
import duration from "dayjs/plugin/duration.js";
import isSameOrBefore from "dayjs/plugin/isSameOrBefore.js";
import weekday from "dayjs/plugin/weekday.js";
//#region lib/utils/parse-date.ts
dayjs.extend(customParseFormat);
dayjs.extend(duration);
dayjs.extend(isSameOrBefore);
dayjs.extend(weekday);
const words = [
{
startAt: dayjs(),
regExp: /^(?:今[天日]|to?day?)(.*)/
},
{
startAt: dayjs().subtract(1, "days"),
regExp: /^(?:昨[天日]|y(?:ester)?day?)(.*)/
},
{
startAt: dayjs().subtract(2, "days"),
regExp: /^(?:前天|(?:the)?d(?:ay)?b(?:eforeyesterda)?y)(.*)/
},
{
startAt: dayjs().isSameOrBefore(dayjs().weekday(1)) ? dayjs().weekday(1).subtract(1, "week") : dayjs().weekday(1),
regExp: /^(?:周|星期)一(.*)/
},
{
startAt: dayjs().isSameOrBefore(dayjs().weekday(2)) ? dayjs().weekday(2).subtract(1, "week") : dayjs().weekday(2),
regExp: /^(?:周|星期)二(.*)/
},
{
startAt: dayjs().isSameOrBefore(dayjs().weekday(3)) ? dayjs().weekday(3).subtract(1, "week") : dayjs().weekday(3),
regExp: /^(?:周|星期)三(.*)/
},
{
startAt: dayjs().isSameOrBefore(dayjs().weekday(4)) ? dayjs().weekday(4).subtract(1, "week") : dayjs().weekday(4),
regExp: /^(?:周|星期)四(.*)/
},
{
startAt: dayjs().isSameOrBefore(dayjs().weekday(5)) ? dayjs().weekday(5).subtract(1, "week") : dayjs().weekday(5),
regExp: /^(?:周|星期)五(.*)/
},
{
startAt: dayjs().isSameOrBefore(dayjs().weekday(6)) ? dayjs().weekday(6).subtract(1, "week") : dayjs().weekday(6),
regExp: /^(?:周|星期)六(.*)/
},
{
startAt: dayjs().isSameOrBefore(dayjs().weekday(7)) ? dayjs().weekday(7).subtract(1, "week") : dayjs().weekday(7),
regExp: /^(?:周|星期)[天日](.*)/
},
{
startAt: dayjs().add(1, "days"),
regExp: /^(?:明[天日]|y(?:ester)?day?)(.*)/
},
{
startAt: dayjs().add(2, "days"),
regExp: /^(?:[后後][天日]|(?:the)?d(?:ay)?a(?:fter)?t(?:omrrow)?)(.*)/
}
];
const patterns = [
{
unit: "years",
regExp: /(\d+)(?:年|y(?:ea)?rs?)/
},
{
unit: "months",
regExp: /(\d+)(?:[个個]?月|months?)/
},
{
unit: "weeks",
regExp: /(\d+)(?:周|[个個]?星期|weeks?)/
},
{
unit: "days",
regExp: /(\d+)(?:天|日|d(?:ay)?s?)/
},
{
unit: "hours",
regExp: /(\d+)(?:[个個]?(?:小?时|時|点|點)|h(?:(?:ou)?r)?s?)/
},
{
unit: "minutes",
regExp: /(\d+)(?:分[鐘钟]?|m(?:in(?:ute)?)?s?)/
},
{
unit: "seconds",
regExp: /(\d+)(?:秒[鐘钟]?|s(?:ec(?:ond)?)?s?)/
}
];
const patternSize = Object.keys(patterns).length;
/**
* 预处理日期字符串
* @param {String} date 原始日期字符串
*/
const toDate = (date) => date.toLowerCase().replaceAll(/(^an?\s)|(\san?\s)/g, "1").replaceAll(/几|幾/g, "3").replaceAll(/[\s,]/g, "");
/**
* 将 `['\d+时', ..., '\d+秒']` 转换为 `{ hours: \d+, ..., seconds: \d+ }`
* 用于描述时间长度
* @param {Array.<String>} matches 所有匹配结果
*/
const toDurations = (matches) => {
const durations = {};
let p = 0;
for (const m of matches) for (; p <= patternSize; p++) {
const match = patterns[p].regExp.exec(m);
if (match) {
durations[patterns[p].unit] = match[1];
break;
}
}
return durations;
};
const parseDate = (date, ...options) => dayjs(date, ...options).toDate();
const parseRelativeDate = (date) => {
const theDate = toDate(date);
const matches = theDate.match(/(?:\D+)?\d+(?!:|-|\/|(a|p)m)\D+/g);
if (matches) {
const lastMatch = matches.pop();
if (lastMatch) {
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().add(dayjs.duration(toDurations(matches))).toDate();
}
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 for (const w of words) {
const wordMatches = w.regExp.exec(theDate);
if (wordMatches) return dayjs(`${w.startAt.format("YYYY-MM-DD")} ${/a|pm$/.test(wordMatches[1]) ? wordMatches[1].replace(/a|pm/, " $&") : wordMatches[1]}`).toDate();
}
return date;
};
//#endregion
export { parseRelativeDate as n, parseDate as t };