feat(time): 프로젝트 전체 시간 KST(UTC+9) 통일
- app/timezone.py 신설: KST 고정오프셋(+9, DST 없음) + now_kst/today_kst/to_kst_iso - DB 직렬화 created_at/updated_at/decided_at/uploaded_at 표시를 UTC→KST 로 변환 (expense/cupang/vacation db.py) - _now_iso(JSON 저장) KST 기준 - 라우터 date.today()/datetime.now() → today_kst()/now_kst() (달력 '오늘'/엑셀 파일명) Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
@@ -22,6 +22,8 @@ from typing import Any
|
||||
from psycopg.rows import dict_row
|
||||
from psycopg_pool import ConnectionPool
|
||||
|
||||
from app.timezone import KST
|
||||
|
||||
from .store import EDITABLE_STATUSES, STATUSES, VACATION_TYPES, compute_days, normalize_half
|
||||
|
||||
|
||||
@@ -449,7 +451,7 @@ class VacationDBStore:
|
||||
for k in ("created_at", "updated_at", "decided_at"):
|
||||
v = out.get(k)
|
||||
if isinstance(v, datetime):
|
||||
out[k] = v.astimezone(timezone.utc).isoformat(timespec="seconds")
|
||||
out[k] = v.astimezone(KST).isoformat(timespec="seconds")
|
||||
out["days"] = cls._num(out.get("days", 0))
|
||||
return out
|
||||
|
||||
@@ -466,7 +468,7 @@ class VacationDBStore:
|
||||
for k in ("created_at", "updated_at"):
|
||||
v = out.get(k)
|
||||
if isinstance(v, datetime):
|
||||
out[k] = v.astimezone(timezone.utc).isoformat(timespec="seconds")
|
||||
out[k] = v.astimezone(KST).isoformat(timespec="seconds")
|
||||
return out
|
||||
|
||||
@classmethod
|
||||
@@ -481,5 +483,5 @@ class VacationDBStore:
|
||||
for k in ("created_at", "updated_at"):
|
||||
v = out.get(k)
|
||||
if isinstance(v, datetime):
|
||||
out[k] = v.astimezone(timezone.utc).isoformat(timespec="seconds")
|
||||
out[k] = v.astimezone(KST).isoformat(timespec="seconds")
|
||||
return out
|
||||
|
||||
@@ -11,7 +11,7 @@ from __future__ import annotations
|
||||
|
||||
import calendar as _calendar
|
||||
import io
|
||||
from datetime import date, datetime
|
||||
from datetime import date
|
||||
from typing import Any
|
||||
|
||||
from fastapi import APIRouter, Depends, Form, HTTPException, Request
|
||||
@@ -22,6 +22,8 @@ from fastapi.responses import (
|
||||
StreamingResponse,
|
||||
)
|
||||
|
||||
from app.timezone import now_kst, today_kst
|
||||
|
||||
from .store import HALF_LABELS, HALVES, VACATION_TYPES
|
||||
|
||||
router = APIRouter(prefix="/vacation", tags=["vacation"])
|
||||
@@ -107,7 +109,7 @@ def _guard(
|
||||
|
||||
|
||||
def _ym(request: Request) -> tuple[int, int]:
|
||||
today = date.today()
|
||||
today = today_kst()
|
||||
try:
|
||||
year = int(request.query_params.get("year") or today.year)
|
||||
month = int(request.query_params.get("month") or today.month)
|
||||
@@ -170,7 +172,7 @@ def _build_calendar(
|
||||
date_from=range_start.isoformat(), date_to=range_end.isoformat()
|
||||
)
|
||||
|
||||
today = date.today()
|
||||
today = today_kst()
|
||||
weeks: list[dict[str, Any]] = []
|
||||
for week in weeks_dates:
|
||||
w_start, w_end = week[0], week[-1]
|
||||
@@ -234,7 +236,7 @@ async def index(request: Request) -> HTMLResponse:
|
||||
|
||||
year, month = _ym(request)
|
||||
sel = request.query_params.get("date") or ""
|
||||
today = date.today()
|
||||
today = today_kst()
|
||||
if not sel:
|
||||
sel = (
|
||||
today.isoformat()
|
||||
@@ -315,7 +317,7 @@ async def new_form(request: Request) -> HTMLResponse:
|
||||
"page_subtitle": "휴가 종류 · 기간 · 사유 입력",
|
||||
"mode": "new",
|
||||
"req": None,
|
||||
"default_date": date.today().isoformat(),
|
||||
"default_date": today_kst().isoformat(),
|
||||
}
|
||||
)
|
||||
return render_template(request, "vacation/form.html", ctx)
|
||||
@@ -419,7 +421,7 @@ async def settings_page(request: Request) -> HTMLResponse:
|
||||
{"reason": "휴가 설정은 관리자만 접근할 수 있습니다.", "is_admin": False},
|
||||
status_code=403,
|
||||
)
|
||||
today = date.today()
|
||||
today = today_kst()
|
||||
try:
|
||||
year = int(request.query_params.get("year") or today.year)
|
||||
except ValueError:
|
||||
@@ -551,7 +553,7 @@ async def export_xlsx(
|
||||
if store is None:
|
||||
raise HTTPException(status_code=503, detail="vacation_db 미설정")
|
||||
|
||||
today = date.today()
|
||||
today = today_kst()
|
||||
try:
|
||||
year = int(request.query_params.get("year") or today.year)
|
||||
month = int(request.query_params.get("month") or 0)
|
||||
@@ -599,7 +601,7 @@ async def export_xlsx(
|
||||
wb.save(buf)
|
||||
buf.seek(0)
|
||||
scope = f"{year}{('%02d' % month) if (1 <= month <= 12) else ''}"
|
||||
fname = f"vacation_{scope}_{datetime.now().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||
fname = f"vacation_{scope}_{now_kst().strftime('%Y%m%d_%H%M%S')}.xlsx"
|
||||
return StreamingResponse(
|
||||
buf,
|
||||
media_type="application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
|
||||
Reference in New Issue
Block a user