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],
|
title: i.editor_title || load(i.message).text().split("\n")[0],
|
||||||
url: i.shareUrl,
|
url: i.shareUrl,
|
||||||
extra: {
|
extra: {
|
||||||
date: new Date(i.dateline * 1000).getTime(),
|
info: i.targetRow?.subTitle,
|
||||||
|
// date: new Date(i.dateline * 1000).getTime(),
|
||||||
},
|
},
|
||||||
})).slice(0, 20)
|
})).slice(0, 20)
|
||||||
})
|
})
|
||||||
|
@ -1,4 +1,165 @@
|
|||||||
import { describe, expect, it } from "vitest"
|
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", () => {
|
describe("transform Beijing time to UTC in different timezone", () => {
|
||||||
const a = "2024/10/3 12:26:16"
|
const a = "2024/10/3 12:26:16"
|
||||||
|
@ -1,9 +1,17 @@
|
|||||||
import dayjs from "dayjs"
|
import dayjs from "dayjs"
|
||||||
import utcPlugin from "dayjs/plugin/utc.js"
|
import utcPlugin from "dayjs/plugin/utc.js"
|
||||||
import timezonePlugin from "dayjs/plugin/timezone.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(utcPlugin)
|
||||||
dayjs.extend(timezonePlugin)
|
dayjs.extend(timezonePlugin)
|
||||||
|
dayjs.extend(customParseFormat)
|
||||||
|
dayjs.extend(duration)
|
||||||
|
dayjs.extend(isSameOrBefore)
|
||||||
|
dayjs.extend(weekday)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 传入任意时区的时间(不携带时区),转换为 UTC 时间
|
* 传入任意时区的时间(不携带时区),转换为 UTC 时间
|
||||||
@ -14,3 +22,201 @@ export function tranformToUTC(date: string, format?: string, timezone: string =
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const day = dayjs
|
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"
|
import type { OriginSource, Source, SourceID } from "./types"
|
||||||
|
|
||||||
const Time = {
|
const Time = {
|
||||||
|
test: 1,
|
||||||
half: 30 * 60 * 1000,
|
half: 30 * 60 * 1000,
|
||||||
five: 5 * 60 * 1000,
|
five: 5 * 60 * 1000,
|
||||||
}
|
}
|
||||||
|
@ -154,14 +154,16 @@ function Num({ num }: { num: number }) {
|
|||||||
|
|
||||||
function ExtraInfo({ item }: { item: NewsItem }) {
|
function ExtraInfo({ item }: { item: NewsItem }) {
|
||||||
const relativeTime = useRelativeTime(item?.extra?.date)
|
const relativeTime = useRelativeTime(item?.extra?.date)
|
||||||
if (relativeTime) {
|
if (item?.extra?.info) {
|
||||||
return <>{relativeTime}</>
|
return <>{item.extra.info}</>
|
||||||
}
|
}
|
||||||
|
|
||||||
if (item?.extra?.icon) {
|
if (item?.extra?.icon) {
|
||||||
return (
|
return <img src={item.extra.icon} className="w-5 inline" />
|
||||||
<img src={item.extra.icon} className="w-5 inline" />
|
}
|
||||||
)
|
|
||||||
|
if (relativeTime) {
|
||||||
|
return <>{relativeTime}</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -25,6 +25,7 @@ export default defineConfig({
|
|||||||
imports: ["$fetch", "ofetch"],
|
imports: ["$fetch", "ofetch"],
|
||||||
}],
|
}],
|
||||||
dirs: ["server/utils"],
|
dirs: ["server/utils"],
|
||||||
|
dts: "dist/.nitro/types/nitro-imports.d.ts",
|
||||||
}),
|
}),
|
||||||
],
|
],
|
||||||
})
|
})
|
||||||
|
Loading…
x
Reference in New Issue
Block a user