Add kboTodayKst helper and support "today" alias in KBO endpoints
- KBO 데이터 갱신 시점(02:00)을 고려하여 03:00 이전 요청 시 전일 데이터를 반환하도록 `kboTodayKst` 유틸리티를 추가했습니다. - `kboHandlers`에서 `day` 파라미터에 "today" 값을 전달할 경우, 위 컷오프 로직이 적용된 날짜를 자동으로 계산하도록 개선했습니다. - 신규 테스트 코드를 통해 새벽 시간대 경계값에서도 날짜가 정확하게 계산되는지 검증했습니다.
This commit is contained in:
parent
526a8ef731
commit
e980410858
@ -5,6 +5,7 @@ import { getPlayerStats, getValidPlayerTypes } from "../services/playerService";
|
|||||||
import { getGameList } from "../services/gameListService";
|
import { getGameList } from "../services/gameListService";
|
||||||
import { getGameDetail } from "../services/gameDetailService";
|
import { getGameDetail } from "../services/gameDetailService";
|
||||||
import { TeamCode } from "../types/panit";
|
import { TeamCode } from "../types/panit";
|
||||||
|
import { kboTodayKst } from "../types/dateString";
|
||||||
|
|
||||||
enum KboPath {
|
enum KboPath {
|
||||||
Rank = "rank",
|
Rank = "rank",
|
||||||
@ -60,9 +61,12 @@ export const kbo = onRequest(async (req, res) => {
|
|||||||
const year = parseIntParam(req.query.year, now.getFullYear());
|
const year = parseIntParam(req.query.year, now.getFullYear());
|
||||||
const month = parseIntParam(req.query.month, now.getMonth() + 1);
|
const month = parseIntParam(req.query.month, now.getMonth() + 1);
|
||||||
const series = req.query.series ? String(req.query.series) : undefined;
|
const series = req.query.series ? String(req.query.series) : undefined;
|
||||||
const day = req.query.day !== undefined
|
const dayRaw = req.query.day;
|
||||||
? parseIntParam(req.query.day, NaN)
|
const day = dayRaw === undefined ?
|
||||||
: undefined;
|
undefined :
|
||||||
|
String(dayRaw) === "today" ?
|
||||||
|
parseInt(kboTodayKst().slice(8, 10), 10) :
|
||||||
|
parseIntParam(dayRaw, NaN);
|
||||||
|
|
||||||
if (year < 1982 || year > 2100) {
|
if (year < 1982 || year > 2100) {
|
||||||
res.status(400).json({ error: `Invalid year: ${year} (1982~2100)` });
|
res.status(400).json({ error: `Invalid year: ${year} (1982~2100)` });
|
||||||
|
|||||||
@ -38,6 +38,18 @@ export function todayKst(): DateString {
|
|||||||
return toDateString(new Date());
|
return toDateString(new Date());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** KBO 데이터 기준일의 컷오프 시각(KST). 일일 갱신(02:00) 직후 버퍼 1시간. */
|
||||||
|
const KBO_DAY_CUTOFF_HOUR = 3;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* KBO 데이터 기준의 "오늘". 일일 갱신 잡이 02:00(KST)에 돌기 때문에
|
||||||
|
* 새벽 03:00 이전에는 당일 데이터가 아직 확정되지 않아 직전일을 반환한다.
|
||||||
|
* `now`는 테스트 주입용(기본 현재 시각).
|
||||||
|
*/
|
||||||
|
export function kboTodayKst(now: Date = new Date()): DateString {
|
||||||
|
return toDateString(new Date(now.getTime() - KBO_DAY_CUTOFF_HOUR * 60 * 60 * 1000));
|
||||||
|
}
|
||||||
|
|
||||||
/** n일 전 KST 날짜. */
|
/** n일 전 KST 날짜. */
|
||||||
export function daysAgoKst(n: number): DateString {
|
export function daysAgoKst(n: number): DateString {
|
||||||
return toDateString(new Date(Date.now() - n * 24 * 60 * 60 * 1000));
|
return toDateString(new Date(Date.now() - n * 24 * 60 * 60 * 1000));
|
||||||
|
|||||||
41
tests/types/dateString.test.ts
Normal file
41
tests/types/dateString.test.ts
Normal file
@ -0,0 +1,41 @@
|
|||||||
|
import { describe, expect, it } from "vitest";
|
||||||
|
import { kboTodayKst } from "../../src/types/dateString";
|
||||||
|
|
||||||
|
// KST 벽시계 시각으로 Date를 만든다(UTC+09:00 명시). 실행 환경 TZ와 무관.
|
||||||
|
function kst(iso: string): Date {
|
||||||
|
return new Date(`${iso}+09:00`);
|
||||||
|
}
|
||||||
|
|
||||||
|
describe("kboTodayKst — 새벽 03:00(KST) 컷오프", () => {
|
||||||
|
it("02:59이면 직전일 (일일 갱신 02:00 직후 버퍼)", () => {
|
||||||
|
expect(kboTodayKst(kst("2026-06-30T02:59:00"))).toBe("2026-06-29");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("00:00(자정)이면 직전일", () => {
|
||||||
|
expect(kboTodayKst(kst("2026-06-30T00:00:00"))).toBe("2026-06-29");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("03:00 정각이면 당일 (컷오프 경계 포함)", () => {
|
||||||
|
expect(kboTodayKst(kst("2026-06-30T03:00:00"))).toBe("2026-06-30");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("03:01이면 당일", () => {
|
||||||
|
expect(kboTodayKst(kst("2026-06-30T03:01:00"))).toBe("2026-06-30");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("정오면 당일", () => {
|
||||||
|
expect(kboTodayKst(kst("2026-06-30T12:00:00"))).toBe("2026-06-30");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("23:59이면 당일", () => {
|
||||||
|
expect(kboTodayKst(kst("2026-06-30T23:59:00"))).toBe("2026-06-30");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("월초 새벽이면 전월 말일로 넘어간다", () => {
|
||||||
|
expect(kboTodayKst(kst("2026-07-01T02:00:00"))).toBe("2026-06-30");
|
||||||
|
});
|
||||||
|
|
||||||
|
it("연초 새벽이면 전년 말일로 넘어간다", () => {
|
||||||
|
expect(kboTodayKst(kst("2026-01-01T01:00:00"))).toBe("2025-12-31");
|
||||||
|
});
|
||||||
|
});
|
||||||
Loading…
x
Reference in New Issue
Block a user