import type { WithAbortSignal } from "#Source/abort/index.ts" import { generalFetch } from "#Source/request/index.ts" import type { Lang, Unit } from "../base.ts" const internalTimeMachineWeatherUnitSet = new Set(["m", "i"]) const internalTimeMachineWeatherDatePattern = /^\d{8}$/ /** * @description 调用和风天气天气时光机接口时使用的参数。 * * @example * ;``` * const options: TimeMachineWeatherOptions = { * baseUrl: "https://abcxyz.qweatherapi.com", * jwt: "eyJhbGciOiJFZERTQSJ9.payload.signature", * location: "101010100", * date: "20200725", * lang: "zh-hans", * unit: "m", * timeout: 5_000, * } * ``` */ export interface TimeMachineWeatherOptions extends WithAbortSignal { /** * @description 和风天气 API Base URL,例如 `https://abcxyz.qweatherapi.com`。 */ baseUrl: string /** * @description 用于 `Authorization: Bearer` 请求头的 JWT。 */ jwt: string /** * @description 需要查询地区的 LocationID。 */ location: string /** * @description 需要查询的历史日期,格式为 `yyyyMMdd`。 */ date: string /** * @description 返回结果的多语言代码。 */ lang?: Lang | undefined /** * @description 数据单位设置,`m` 为公制,`i` 为英制。 */ unit?: Unit | undefined /** * @description 请求超时时间,单位为毫秒。 */ timeout?: number | undefined } /** * @description 天气时光机响应中的当日概览数据。 * * @example * ;``` * const weatherDaily: TimeMachineWeatherResultWeatherDaily = { * date: "2020-07-25", * sunrise: "05:08", * sunset: "19:33", * moonrise: "09:54", * moonset: "22:40", * moonPhase: "峨眉月", * tempMax: "33", * tempMin: "23", * humidity: "52", * precip: "0.0", * pressure: "1000", * } * ``` */ export interface TimeMachineWeatherResultWeatherDaily { /** * @description 当天日期。 */ date: string /** * @description 当天日出时间,在高纬度地区可能为空。 */ sunrise: string /** * @description 当天日落时间,在高纬度地区可能为空。 */ sunset: string /** * @description 当天月升时间,可能为空。 */ moonrise: string /** * @description 当天月落时间,可能为空。 */ moonset: string /** * @description 当天月相名称。 */ moonPhase: string /** * @description 当天最高温度。 */ tempMax: string /** * @description 当天最低温度。 */ tempMin: string /** * @description 当天相对湿度。 */ humidity: string /** * @description 当天总降水量。 */ precip: string /** * @description 大气压强。 */ pressure: string } /** * @description 天气时光机响应中的逐小时历史天气数据。 * * @example * ;``` * const weatherHourly: TimeMachineWeatherResultWeatherHourly = { * time: "2020-07-25 00:00", * temp: "28", * icon: "100", * text: "晴", * precip: "0.0", * wind360: "246", * windDir: "西南风", * windScale: "2", * windSpeed: "8", * humidity: "49", * pressure: "1001", * } * ``` */ export interface TimeMachineWeatherResultWeatherHourly { /** * @description 当天时间。 */ time: string /** * @description 当天每小时温度。 */ temp: string /** * @description 当天每小时天气状况图标代码。 */ icon: string /** * @description 当天每小时天气状况文字描述。 */ text: string /** * @description 当天每小时累计降水量。 */ precip: string /** * @description 当天每小时风向 360 角度。 */ wind360: string /** * @description 当天每小时风向。 */ windDir: string /** * @description 当天每小时风力等级。 */ windScale: string /** * @description 当天每小时风速。 */ windSpeed: string /** * @description 当天每小时相对湿度。 */ humidity: string /** * @description 当天每小时大气压强。 */ pressure: string } /** * @description 天气时光机响应中的来源与许可信息。 * * @example * ;``` * const refer: TimeMachineWeatherResultRefer = { * sources: ["QWeather"], * license: ["QWeather Developers License"], * } * ``` */ export interface TimeMachineWeatherResultRefer { /** * @description 原始数据来源或数据源说明。 */ sources?: string[] | undefined /** * @description 数据许可或版权声明。 */ license?: string[] | undefined } /** * @description 和风天气天气时光机接口的响应结果。 * * @example * ;``` * const result: TimeMachineWeatherResult = { * code: "200", * fxLink: "http://hfx.link/2ax6", * weatherDaily: { * date: "2020-07-25", * sunrise: "05:08", * sunset: "19:33", * moonrise: "09:54", * moonset: "22:40", * moonPhase: "峨眉月", * tempMax: "33", * tempMin: "23", * humidity: "52", * precip: "0.0", * pressure: "1000", * }, * weatherHourly: [ * { * time: "2020-07-25 00:00", * temp: "28", * icon: "100", * text: "晴", * precip: "0.0", * wind360: "246", * windDir: "西南风", * windScale: "2", * windSpeed: "8", * humidity: "49", * pressure: "1001", * }, * ], * refer: { * sources: ["QWeather"], * license: ["QWeather Developers License"], * }, * } * ``` */ export interface TimeMachineWeatherResult { /** * @description 接口状态码。 */ code: string /** * @description 当前数据对应的响应式天气页面链接。 */ fxLink: string /** * @description 当日天气概览数据。 */ weatherDaily: TimeMachineWeatherResultWeatherDaily /** * @description 当日逐小时历史天气数据。 */ weatherHourly: TimeMachineWeatherResultWeatherHourly[] /** * @description 响应中的来源与许可信息。 */ refer?: TimeMachineWeatherResultRefer | undefined } const internalValidateTimeMachineWeatherOptions = ( options: TimeMachineWeatherOptions, ): { baseUrl: string jwt: string location: string date: string } => { const baseUrl = options.baseUrl.trim() const jwt = options.jwt.trim() const location = options.location.trim() const date = options.date.trim() if (baseUrl === "") { throw new TypeError("TimeMachineWeather baseUrl must not be empty.") } if (jwt === "") { throw new TypeError("TimeMachineWeather jwt must not be empty.") } if (location === "") { throw new TypeError("TimeMachineWeather location must not be empty.") } if (date === "") { throw new TypeError("TimeMachineWeather date must not be empty.") } if (internalTimeMachineWeatherDatePattern.test(date) === false) { throw new RangeError("TimeMachineWeather date must use yyyyMMdd format.") } if (options.unit !== undefined && internalTimeMachineWeatherUnitSet.has(options.unit) === false) { throw new RangeError("TimeMachineWeather unit must be one of: m, i.") } return { baseUrl, jwt, location, date, } } /** * @description 获取指定地区指定日期的历史天气再分析数据。 * * @see {@link https://dev.qweather.com/docs/api/time-machine/time-machine-weather/} */ export const timeMachineWeather = async ( options: TimeMachineWeatherOptions, ): Promise => { const { baseUrl, jwt, location, date } = internalValidateTimeMachineWeatherOptions(options) const { lang, unit, timeout, abortSignal } = options return await generalFetch< { query: { location: string date: string lang?: Lang | undefined unit?: Unit | undefined } }, { type: "json" data: TimeMachineWeatherResult } >({ baseUrl, path: "/v7/historical/weather", method: "get", headers: { Authorization: `Bearer ${jwt}`, }, query: { location, date, lang, unit, }, responseType: "json", timeout, abortSignal, }).getJson() }