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:
@@ -20,6 +20,8 @@ from typing import Any
|
||||
from psycopg.rows import dict_row
|
||||
from psycopg_pool import ConnectionPool
|
||||
|
||||
from app.timezone import KST
|
||||
|
||||
from .store import STATUSES, compute_boxes
|
||||
|
||||
|
||||
@@ -558,7 +560,7 @@ class CupangDBStore:
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
@@ -572,7 +574,7 @@ class CupangDBStore:
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
@@ -586,7 +588,7 @@ class CupangDBStore:
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
@@ -603,7 +605,7 @@ class CupangDBStore:
|
||||
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
|
||||
|
||||
@staticmethod
|
||||
@@ -620,5 +622,5 @@ class CupangDBStore:
|
||||
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,9 +11,10 @@ from __future__ import annotations
|
||||
|
||||
import calendar as _calendar
|
||||
import json
|
||||
from datetime import date
|
||||
from typing import Any
|
||||
|
||||
from app.timezone import today_kst
|
||||
|
||||
from fastapi import APIRouter, Body, Depends, Form, HTTPException, Request
|
||||
from fastapi.responses import HTMLResponse, JSONResponse, RedirectResponse
|
||||
|
||||
@@ -98,7 +99,7 @@ def _parse_lines(lines_json: str) -> list[dict[str, Any]]:
|
||||
|
||||
|
||||
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)
|
||||
@@ -128,7 +129,7 @@ async def index(request: Request) -> HTMLResponse:
|
||||
|
||||
# 선택 날짜 (기본: 오늘이 이번 달이면 오늘, 아니면 1일)
|
||||
sel = request.query_params.get("date") or ""
|
||||
today = date.today()
|
||||
today = today_kst()
|
||||
if not sel:
|
||||
sel = today.isoformat() if (today.year == year and today.month == month) else f"{year:04d}-{month:02d}-01"
|
||||
|
||||
@@ -225,7 +226,7 @@ async def new_form(request: Request) -> HTMLResponse:
|
||||
"page_subtitle": "공통 헤더 1개 + 품목 라인",
|
||||
"mode": "new",
|
||||
"shipment": None,
|
||||
"default_date": date.today().isoformat(),
|
||||
"default_date": today_kst().isoformat(),
|
||||
}
|
||||
)
|
||||
return render_template(request, "cupang/form.html", ctx)
|
||||
|
||||
Reference in New Issue
Block a user