mirror of
https://github.com/ourongxing/newsnow.git
synced 2025-01-19 03:09:14 +08:00
feat: new source 36kr
This commit is contained in:
parent
81e0023c17
commit
24cf2a5a75
137
auto-imports.d.ts
vendored
137
auto-imports.d.ts
vendored
@ -1,137 +0,0 @@
|
||||
/* eslint-disable */
|
||||
/* prettier-ignore */
|
||||
// @ts-nocheck
|
||||
// noinspection JSUnusedGlobalSymbols
|
||||
// Generated by unplugin-auto-import
|
||||
// biome-ignore lint: disable
|
||||
export {}
|
||||
declare global {
|
||||
const $fetch: typeof import('ofetch')['$fetch']
|
||||
const afterAll: typeof import('vitest')['afterAll']
|
||||
const afterEach: typeof import('vitest')['afterEach']
|
||||
const appendCorsHeaders: typeof import('h3')['appendCorsHeaders']
|
||||
const appendCorsPreflightHeaders: typeof import('h3')['appendCorsPreflightHeaders']
|
||||
const appendHeader: typeof import('h3')['appendHeader']
|
||||
const appendHeaders: typeof import('h3')['appendHeaders']
|
||||
const appendResponseHeader: typeof import('h3')['appendResponseHeader']
|
||||
const appendResponseHeaders: typeof import('h3')['appendResponseHeaders']
|
||||
const assert: typeof import('vitest')['assert']
|
||||
const assertMethod: typeof import('h3')['assertMethod']
|
||||
const beforeAll: typeof import('vitest')['beforeAll']
|
||||
const beforeEach: typeof import('vitest')['beforeEach']
|
||||
const callNodeListener: typeof import('h3')['callNodeListener']
|
||||
const chai: typeof import('vitest')['chai']
|
||||
const clearResponseHeaders: typeof import('h3')['clearResponseHeaders']
|
||||
const clearSession: typeof import('h3')['clearSession']
|
||||
const createApp: typeof import('h3')['createApp']
|
||||
const createAppEventHandler: typeof import('h3')['createAppEventHandler']
|
||||
const createError: typeof import('h3')['createError']
|
||||
const createEvent: typeof import('h3')['createEvent']
|
||||
const createEventStream: typeof import('h3')['createEventStream']
|
||||
const createRouter: typeof import('h3')['createRouter']
|
||||
const day: typeof import('./server/utils/date')['day']
|
||||
const defaultContentType: typeof import('h3')['defaultContentType']
|
||||
const defineEventHandler: typeof import('h3')['defineEventHandler']
|
||||
const defineFallbackSource: typeof import('./server/utils/source')['defineFallbackSource']
|
||||
const defineLazyEventHandler: typeof import('h3')['defineLazyEventHandler']
|
||||
const defineNodeListener: typeof import('h3')['defineNodeListener']
|
||||
const defineNodeMiddleware: typeof import('h3')['defineNodeMiddleware']
|
||||
const defineRSSHubSource: typeof import('./server/utils/source')['defineRSSHubSource']
|
||||
const defineRSSSource: typeof import('./server/utils/source')['defineRSSSource']
|
||||
const defineRequestMiddleware: typeof import('h3')['defineRequestMiddleware']
|
||||
const defineResponseMiddleware: typeof import('h3')['defineResponseMiddleware']
|
||||
const defineSource: typeof import('./server/utils/source')['defineSource']
|
||||
const defineWebSocket: typeof import('h3')['defineWebSocket']
|
||||
const defineWebSocketHandler: typeof import('h3')['defineWebSocketHandler']
|
||||
const deleteCookie: typeof import('h3')['deleteCookie']
|
||||
const describe: typeof import('vitest')['describe']
|
||||
const dynamicEventHandler: typeof import('h3')['dynamicEventHandler']
|
||||
const eventHandler: typeof import('h3')['eventHandler']
|
||||
const expect: typeof import('vitest')['expect']
|
||||
const fetchWithEvent: typeof import('h3')['fetchWithEvent']
|
||||
const fromNodeMiddleware: typeof import('h3')['fromNodeMiddleware']
|
||||
const fromPlainHandler: typeof import('h3')['fromPlainHandler']
|
||||
const fromWebHandler: typeof import('h3')['fromWebHandler']
|
||||
const getCookie: typeof import('h3')['getCookie']
|
||||
const getHeader: typeof import('h3')['getHeader']
|
||||
const getHeaders: typeof import('h3')['getHeaders']
|
||||
const getMethod: typeof import('h3')['getMethod']
|
||||
const getProxyRequestHeaders: typeof import('h3')['getProxyRequestHeaders']
|
||||
const getQuery: typeof import('h3')['getQuery']
|
||||
const getRequestFingerprint: typeof import('h3')['getRequestFingerprint']
|
||||
const getRequestHeader: typeof import('h3')['getRequestHeader']
|
||||
const getRequestHeaders: typeof import('h3')['getRequestHeaders']
|
||||
const getRequestHost: typeof import('h3')['getRequestHost']
|
||||
const getRequestIP: typeof import('h3')['getRequestIP']
|
||||
const getRequestPath: typeof import('h3')['getRequestPath']
|
||||
const getRequestProtocol: typeof import('h3')['getRequestProtocol']
|
||||
const getRequestURL: typeof import('h3')['getRequestURL']
|
||||
const getRequestWebStream: typeof import('h3')['getRequestWebStream']
|
||||
const getResponseHeader: typeof import('h3')['getResponseHeader']
|
||||
const getResponseHeaders: typeof import('h3')['getResponseHeaders']
|
||||
const getResponseStatus: typeof import('h3')['getResponseStatus']
|
||||
const getResponseStatusText: typeof import('h3')['getResponseStatusText']
|
||||
const getRouterParam: typeof import('h3')['getRouterParam']
|
||||
const getRouterParams: typeof import('h3')['getRouterParams']
|
||||
const getSession: typeof import('h3')['getSession']
|
||||
const getValidatedQuery: typeof import('h3')['getValidatedQuery']
|
||||
const getValidatedRouterParams: typeof import('h3')['getValidatedRouterParams']
|
||||
const handleCacheHeaders: typeof import('h3')['handleCacheHeaders']
|
||||
const handleCors: typeof import('h3')['handleCors']
|
||||
const isCorsOriginAllowed: typeof import('h3')['isCorsOriginAllowed']
|
||||
const isError: typeof import('h3')['isError']
|
||||
const isEvent: typeof import('h3')['isEvent']
|
||||
const isEventHandler: typeof import('h3')['isEventHandler']
|
||||
const isMethod: typeof import('h3')['isMethod']
|
||||
const isPreflightRequest: typeof import('h3')['isPreflightRequest']
|
||||
const isStream: typeof import('h3')['isStream']
|
||||
const isWebResponse: typeof import('h3')['isWebResponse']
|
||||
const it: typeof import('vitest')['it']
|
||||
const lazyEventHandler: typeof import('h3')['lazyEventHandler']
|
||||
const logger: typeof import('./server/utils/logger')['logger']
|
||||
const ofetch: typeof import('ofetch')['ofetch']
|
||||
const parseCookies: typeof import('h3')['parseCookies']
|
||||
const promisifyNodeListener: typeof import('h3')['promisifyNodeListener']
|
||||
const proxyRequest: typeof import('h3')['proxyRequest']
|
||||
const readBody: typeof import('h3')['readBody']
|
||||
const readFormData: typeof import('h3')['readFormData']
|
||||
const readMultipartFormData: typeof import('h3')['readMultipartFormData']
|
||||
const readRawBody: typeof import('h3')['readRawBody']
|
||||
const readValidatedBody: typeof import('h3')['readValidatedBody']
|
||||
const removeResponseHeader: typeof import('h3')['removeResponseHeader']
|
||||
const rss2json: typeof import('./server/utils/rss2json')['rss2json']
|
||||
const sanitizeStatusCode: typeof import('h3')['sanitizeStatusCode']
|
||||
const sanitizeStatusMessage: typeof import('h3')['sanitizeStatusMessage']
|
||||
const sealSession: typeof import('h3')['sealSession']
|
||||
const send: typeof import('h3')['send']
|
||||
const sendError: typeof import('h3')['sendError']
|
||||
const sendIterable: typeof import('h3')['sendIterable']
|
||||
const sendNoContent: typeof import('h3')['sendNoContent']
|
||||
const sendProxy: typeof import('h3')['sendProxy']
|
||||
const sendRedirect: typeof import('h3')['sendRedirect']
|
||||
const sendStream: typeof import('h3')['sendStream']
|
||||
const sendWebResponse: typeof import('h3')['sendWebResponse']
|
||||
const serveStatic: typeof import('h3')['serveStatic']
|
||||
const setCookie: typeof import('h3')['setCookie']
|
||||
const setHeader: typeof import('h3')['setHeader']
|
||||
const setHeaders: typeof import('h3')['setHeaders']
|
||||
const setResponseHeader: typeof import('h3')['setResponseHeader']
|
||||
const setResponseHeaders: typeof import('h3')['setResponseHeaders']
|
||||
const setResponseStatus: typeof import('h3')['setResponseStatus']
|
||||
const splitCookiesString: typeof import('h3')['splitCookiesString']
|
||||
const suite: typeof import('vitest')['suite']
|
||||
const test: typeof import('vitest')['test']
|
||||
const toEventHandler: typeof import('h3')['toEventHandler']
|
||||
const toNodeListener: typeof import('h3')['toNodeListener']
|
||||
const toPlainHandler: typeof import('h3')['toPlainHandler']
|
||||
const toWebHandler: typeof import('h3')['toWebHandler']
|
||||
const toWebRequest: typeof import('h3')['toWebRequest']
|
||||
const tranformToUTC: typeof import('./server/utils/date')['tranformToUTC']
|
||||
const unsealSession: typeof import('h3')['unsealSession']
|
||||
const updateSession: typeof import('h3')['updateSession']
|
||||
const useBase: typeof import('h3')['useBase']
|
||||
const useSession: typeof import('h3')['useSession']
|
||||
const vi: typeof import('vitest')['vi']
|
||||
const vitest: typeof import('vitest')['vitest']
|
||||
const writeEarlyHints: typeof import('h3')['writeEarlyHints']
|
||||
}
|
30
server/sources/36kr.ts
Normal file
30
server/sources/36kr.ts
Normal file
@ -0,0 +1,30 @@
|
||||
import type { NewsItem } from "@shared/types"
|
||||
import { load } from "cheerio"
|
||||
import { $fetch } from "ofetch"
|
||||
|
||||
export default defineSource(async () => {
|
||||
const url = "https://www.36kr.com/newsflashes"
|
||||
const response = await $fetch(url)
|
||||
const $ = load(response)
|
||||
const news: NewsItem[] = []
|
||||
const $items = $(".news-list-item")
|
||||
$items.each((_, el) => {
|
||||
const $el = $(el)
|
||||
const $a = $el.find("a")
|
||||
const url = $a.attr("href")
|
||||
const title = $a.text()
|
||||
const relativeDate = $el.find(".time")
|
||||
if (url && title && relativeDate) {
|
||||
news.push({
|
||||
url,
|
||||
title,
|
||||
id: url,
|
||||
extra: {
|
||||
date: parseRelativeDate(relativeDate.text()),
|
||||
},
|
||||
})
|
||||
}
|
||||
})
|
||||
|
||||
return news.slice(0, 20)
|
||||
})
|
@ -31,7 +31,8 @@ export default defineSource(async () => {
|
||||
title: i.editor_title || load(i.message).text().split("\n")[0],
|
||||
url: i.shareUrl,
|
||||
extra: {
|
||||
date: new Date(i.dateline * 1000).getTime(),
|
||||
info: i.targetRow?.subTitle,
|
||||
// date: new Date(i.dateline * 1000).getTime(),
|
||||
},
|
||||
})).slice(0, 20)
|
||||
})
|
||||
|
@ -1,4 +1,165 @@
|
||||
import { describe, expect, it } from "vitest"
|
||||
import MockDate from "mockdate"
|
||||
|
||||
describe("parseRelativeDate", () => {
|
||||
const second = 1000
|
||||
const minute = 60 * second
|
||||
const hour = 60 * minute
|
||||
const day = 24 * hour
|
||||
const week = 7 * day
|
||||
const month = 30 * day
|
||||
const year = 365 * day
|
||||
const date = new Date()
|
||||
|
||||
const weekday = (d: number) => +new Date(date.getFullYear(), date.getMonth(), date.getDate() + d - (date.getDay() > d ? date.getDay() : date.getDay() + 7))
|
||||
|
||||
// 固定时间
|
||||
MockDate.set(date)
|
||||
|
||||
it("s秒钟前", () => {
|
||||
expect(+new Date(parseRelativeDate("10秒前"))).toBe(+date - 10 * second)
|
||||
})
|
||||
|
||||
it("m分钟前", () => {
|
||||
expect(+new Date(parseRelativeDate("10分钟前"))).toBe(+date - 10 * minute)
|
||||
})
|
||||
|
||||
it("m分鐘前", () => {
|
||||
expect(+new Date(parseRelativeDate("10分鐘前"))).toBe(+date - 10 * minute)
|
||||
})
|
||||
|
||||
it("m分钟后", () => {
|
||||
expect(+new Date(parseRelativeDate("10分钟后"))).toBe(+date + 10 * minute)
|
||||
})
|
||||
|
||||
it("a minute ago", () => {
|
||||
expect(+new Date(parseRelativeDate("a minute ago"))).toBe(+date - 1 * minute)
|
||||
})
|
||||
|
||||
it("s minutes ago", () => {
|
||||
expect(+new Date(parseRelativeDate("10 minutes ago"))).toBe(+date - 10 * minute)
|
||||
})
|
||||
|
||||
it("s mins ago", () => {
|
||||
expect(+new Date(parseRelativeDate("10 mins ago"))).toBe(+date - 10 * minute)
|
||||
})
|
||||
|
||||
it("in s minutes", () => {
|
||||
expect(+new Date(parseRelativeDate("in 10 minutes"))).toBe(+date + 10 * minute)
|
||||
})
|
||||
|
||||
it("in an hour", () => {
|
||||
expect(+new Date(parseRelativeDate("in an hour"))).toBe(+date + 1 * hour)
|
||||
})
|
||||
|
||||
it("h小时前", () => {
|
||||
expect(+new Date(parseRelativeDate("10小时前"))).toBe(+date - 10 * hour)
|
||||
})
|
||||
|
||||
it("h个小时前", () => {
|
||||
expect(+new Date(parseRelativeDate("10个小时前"))).toBe(+date - 10 * hour)
|
||||
})
|
||||
|
||||
it("d天前", () => {
|
||||
expect(+new Date(parseRelativeDate("10天前"))).toBe(+date - 10 * day)
|
||||
})
|
||||
|
||||
it("w周前", () => {
|
||||
expect(+new Date(parseRelativeDate("10周前"))).toBe(+date - 10 * week)
|
||||
})
|
||||
|
||||
it("w星期前", () => {
|
||||
expect(+new Date(parseRelativeDate("10星期前"))).toBe(+date - 10 * week)
|
||||
})
|
||||
|
||||
it("w个星期前", () => {
|
||||
expect(+new Date(parseRelativeDate("10个星期前"))).toBe(+date - 10 * week)
|
||||
})
|
||||
|
||||
it("m月前", () => {
|
||||
expect(+new Date(parseRelativeDate("1月前"))).toBe(+date - 1 * month)
|
||||
})
|
||||
|
||||
it("m个月前", () => {
|
||||
expect(+new Date(parseRelativeDate("1个月前"))).toBe(+date - 1 * month)
|
||||
})
|
||||
|
||||
it("y年前", () => {
|
||||
expect(+new Date(parseRelativeDate("1年前"))).toBe(+date - 1 * year)
|
||||
})
|
||||
|
||||
it("y年M个月前", () => {
|
||||
expect(+new Date(parseRelativeDate("1年1个月前"))).toBe(+date - 1 * year - 1 * month)
|
||||
})
|
||||
|
||||
it("d天H小时前", () => {
|
||||
expect(+new Date(parseRelativeDate("1天1小时前"))).toBe(+date - 1 * day - 1 * hour)
|
||||
})
|
||||
|
||||
it("h小时m分钟s秒钟前", () => {
|
||||
expect(+new Date(parseRelativeDate("1小时1分钟1秒钟前"))).toBe(+date - 1 * hour - 1 * minute - 1 * second)
|
||||
})
|
||||
|
||||
it("dd Hh mm ss ago", () => {
|
||||
expect(+new Date(parseRelativeDate("1d 1h 1m 1s ago"))).toBe(+date - 1 * day - 1 * hour - 1 * minute - 1 * second)
|
||||
})
|
||||
|
||||
it("h小时m分钟s秒钟后", () => {
|
||||
expect(+new Date(parseRelativeDate("1小时1分钟1秒钟后"))).toBe(+date + 1 * hour + 1 * minute + 1 * second)
|
||||
})
|
||||
|
||||
it("今天", () => {
|
||||
expect(+new Date(parseRelativeDate("今天"))).toBe(+date.setHours(0, 0, 0, 0))
|
||||
})
|
||||
|
||||
it("today H:m", () => {
|
||||
expect(+new Date(parseRelativeDate("Today 08:00"))).toBe(+date + 8 * hour)
|
||||
})
|
||||
|
||||
it("today, h:m a", () => {
|
||||
expect(+new Date(parseRelativeDate("Today, 8:00 pm"))).toBe(+date + 20 * hour)
|
||||
})
|
||||
|
||||
it("tDA H:m:s", () => {
|
||||
expect(+new Date(parseRelativeDate("TDA 08:00:00"))).toBe(+date + 8 * hour)
|
||||
})
|
||||
|
||||
it("今天 H:m", () => {
|
||||
expect(+new Date(parseRelativeDate("今天 08:00"))).toBe(+date + 8 * hour)
|
||||
})
|
||||
|
||||
it("今天H点m分", () => {
|
||||
expect(+new Date(parseRelativeDate("今天8点0分"))).toBe(+date + 8 * hour)
|
||||
})
|
||||
|
||||
it("昨日H点m分s秒", () => {
|
||||
expect(+new Date(parseRelativeDate("昨日20时0分0秒"))).toBe(+date - 4 * hour)
|
||||
})
|
||||
|
||||
it("前天 H:m", () => {
|
||||
expect(+new Date(parseRelativeDate("前天 20:00"))).toBe(+date - 1 * day - 4 * hour)
|
||||
})
|
||||
|
||||
it("明天 H:m", () => {
|
||||
expect(+new Date(parseRelativeDate("明天 20:00"))).toBe(+date + 1 * day + 20 * hour)
|
||||
})
|
||||
|
||||
it("星期几 h:m", () => {
|
||||
expect(+new Date(parseRelativeDate("星期一 8:00"))).toBe(weekday(1) + 8 * hour)
|
||||
})
|
||||
|
||||
it("周几 h:m", () => {
|
||||
expect(+new Date(parseRelativeDate("周二 8:00"))).toBe(weekday(2) + 8 * hour)
|
||||
})
|
||||
|
||||
it("星期天 h:m", () => {
|
||||
expect(+new Date(parseRelativeDate("星期天 8:00"))).toBe(weekday(7) + 8 * hour)
|
||||
})
|
||||
|
||||
it("invalid", () => {
|
||||
expect(parseRelativeDate("RSSHub")).toBe("RSSHub")
|
||||
})
|
||||
})
|
||||
|
||||
describe("transform Beijing time to UTC in different timezone", () => {
|
||||
const a = "2024/10/3 12:26:16"
|
||||
|
@ -1,9 +1,17 @@
|
||||
import dayjs from "dayjs"
|
||||
import utcPlugin from "dayjs/plugin/utc.js"
|
||||
import timezonePlugin from "dayjs/plugin/timezone.js"
|
||||
import customParseFormat from "dayjs/plugin/customParseFormat"
|
||||
import duration from "dayjs/plugin/duration"
|
||||
import isSameOrBefore from "dayjs/plugin/isSameOrBefore"
|
||||
import weekday from "dayjs/plugin/weekday"
|
||||
|
||||
dayjs.extend(utcPlugin)
|
||||
dayjs.extend(timezonePlugin)
|
||||
dayjs.extend(customParseFormat)
|
||||
dayjs.extend(duration)
|
||||
dayjs.extend(isSameOrBefore)
|
||||
dayjs.extend(weekday)
|
||||
|
||||
/**
|
||||
* 传入任意时区的时间(不携带时区),转换为 UTC 时间
|
||||
@ -14,3 +22,201 @@ export function tranformToUTC(date: string, format?: string, timezone: string =
|
||||
}
|
||||
|
||||
export const day = dayjs
|
||||
|
||||
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 原始日期字符串
|
||||
*/
|
||||
function toDate(date: string) {
|
||||
return date
|
||||
.toLowerCase()
|
||||
.replace(/(^an?\s)|(\san?\s)/g, "1") // 替换 `a` 和 `an` 为 `1`
|
||||
.replace(/几|幾/g, "3") // 如 `几秒钟前` 视作 `3秒钟前`
|
||||
.replace(/[\s,]/g, "")
|
||||
} // 移除所有空格
|
||||
|
||||
/**
|
||||
* 将 `['\d+时', ..., '\d+秒']` 转换为 `{ hours: \d+, ..., seconds: \d+ }`
|
||||
* 用于描述时间长度
|
||||
* @param {Array.<string>} matches 所有匹配结果
|
||||
*/
|
||||
function toDurations(matches: string[]) {
|
||||
const durations: Record<string, string> = {}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
export const parseDate = (date: string | number, ...options: any) => dayjs(date, ...options).toDate()
|
||||
|
||||
export function parseRelativeDate(date: string) {
|
||||
// 预处理日期字符串 date
|
||||
|
||||
const theDate = toDate(date)
|
||||
|
||||
// 将 `\d+年\d+月...\d+秒前` 分割成 `['\d+年', ..., '\d+秒前']`
|
||||
|
||||
const matches = theDate.match(/\D*\d+(?![:\-/]|(a|p)m)\D+/g)
|
||||
|
||||
if (matches) {
|
||||
// 获得最后的时间单元,如 `\d+秒前`
|
||||
|
||||
const lastMatch = matches.pop()
|
||||
|
||||
if (lastMatch) {
|
||||
// 若最后的时间单元含有 `前`、`以前`、`之前` 等标识字段,减去相应的时间长度
|
||||
// 如 `1分10秒前`
|
||||
|
||||
const beforeMatches = /(.*)(?:前|ago)$/.exec(lastMatch)
|
||||
if (beforeMatches) {
|
||||
matches.push(beforeMatches[1])
|
||||
// duration 这个插件有 bug,他会重新实现 subtract 这个方法,并且不会处理 weeks。用 ms 就可以调用默认的方法
|
||||
// 改成这样之后,月又出问题了,我也是服了
|
||||
return dayjs().subtract(dayjs.duration(toDurations(matches))).toDate()
|
||||
}
|
||||
|
||||
// 若最后的时间单元含有 `后`、`以后`、`之后` 等标识字段,加上相应的时间长度
|
||||
// 如 `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()
|
||||
}
|
||||
}
|
||||
}
|
||||
} 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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return date
|
||||
}
|
||||
|
@ -2,6 +2,7 @@ import { typeSafeObjectFromEntries } from "./type.util"
|
||||
import type { OriginSource, Source, SourceID } from "./types"
|
||||
|
||||
const Time = {
|
||||
test: 1,
|
||||
half: 30 * 60 * 1000,
|
||||
five: 5 * 60 * 1000,
|
||||
}
|
||||
|
@ -154,14 +154,16 @@ function Num({ num }: { num: number }) {
|
||||
|
||||
function ExtraInfo({ item }: { item: NewsItem }) {
|
||||
const relativeTime = useRelativeTime(item?.extra?.date)
|
||||
if (relativeTime) {
|
||||
return <>{relativeTime}</>
|
||||
if (item?.extra?.info) {
|
||||
return <>{item.extra.info}</>
|
||||
}
|
||||
|
||||
if (item?.extra?.icon) {
|
||||
return (
|
||||
<img src={item.extra.icon} className="w-5 inline" />
|
||||
)
|
||||
return <img src={item.extra.icon} className="w-5 inline" />
|
||||
}
|
||||
|
||||
if (relativeTime) {
|
||||
return <>{relativeTime}</>
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -25,6 +25,7 @@ export default defineConfig({
|
||||
imports: ["$fetch", "ofetch"],
|
||||
}],
|
||||
dirs: ["server/utils"],
|
||||
dts: "dist/.nitro/types/nitro-imports.d.ts",
|
||||
}),
|
||||
],
|
||||
})
|
||||
|
Loading…
x
Reference in New Issue
Block a user