"""서버 시각 글 — 저장·전송은 UTC(끝에 Z) · 서버가 만드는 파일 글(엑셀 · PDF · 메일 · 파일 이름)은 한국 시각 고정. 화면 글은 `common_util_time.ts`(보는 PC 시간대) — 서버 PC 시간대에 기대지 않음(한국은 서머타임 없음 = 고정 +9). """ from datetime import date, datetime, timedelta, timezone KST = timezone(timedelta(hours=9), "KST") def utc_text(value: datetime | None) -> str | None: """시간대 없는 값 = UTC 로 보고 「2026-10-30T01:02:03Z」 · 시간대 있는 값은 UTC 로 바꿈 · 없음 = None.""" if value is None: return None if value.tzinfo is not None: value = value.astimezone(timezone.utc) return value.replace(tzinfo=None, microsecond=0).isoformat() + "Z" def utc_now_text() -> str: """지금 — UTC 글(저장용).""" return utc_text(datetime.now(timezone.utc)) or "" def utc_from_timestamp(seconds: float) -> str: """파일 수정 시각 등 epoch 초 → UTC 글.""" return utc_text(datetime.fromtimestamp(seconds, timezone.utc)) or "" def kst_now() -> datetime: """지금 — 한국 시각(서버 PC 시간대와 무관).""" return datetime.now(KST) def kst_today() -> date: return kst_now().date() def kst_from_timestamp(seconds: float) -> datetime: return datetime.fromtimestamp(seconds, KST) def utc_now_naive() -> datetime: """지금 — DB 칸(UTC, 시간대 표시 없음)과 견주는 값.""" return datetime.now(timezone.utc).replace(tzinfo=None) def install_json_utc() -> None: """응답의 datetime(DB 값 = UTC) 을 끝에 Z 붙은 글로 — 화면이 보는 사람 시간대로 바꿈.""" from fastapi.encoders import ENCODERS_BY_TYPE ENCODERS_BY_TYPE[datetime] = lambda value: utc_text(value) or ""