fix(시간 표시): 서버 시각 모두 끝 Z · Z 없는 옛 기록 = 한국 시각으로 읽음 (PLAN 593-2 · 요구 456)
- 서버: DB 시각 글(DATE_FORMAT) 끝 Z · B03 올린 때 · D04 확정 시각 · 건강 확인 시각 = UTC Z · 개요 최근 변경 T 치환 뺌 - 서버가 만드는 엑셀(E05 면적 계산 시각) = 한국 시각 글(kst_text · Z 없는 옛 글은 이미 한국 시각) - 화면: Z 없는 글 = 옛 로컬 기록 → +09:00 로 읽음(옛 파일은 고쳐 쓰지 않음) - 시험: test_593_time(UTC · 서울 · 뉴욕 시간대 · Z 없는 길 0 정적 검사 · D04 · kst_text) Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -8,10 +8,10 @@ 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
|
||||
def utc_text(value: datetime | str | None) -> str | None:
|
||||
"""시간대 없는 값 = UTC 로 보고 「2026-10-30T01:02:03Z」 · 시간대 있는 값은 UTC 로 바꿈 · 없음 = None · 글은 그대로."""
|
||||
if value is None or isinstance(value, str):
|
||||
return value or None
|
||||
if value.tzinfo is not None:
|
||||
value = value.astimezone(timezone.utc)
|
||||
return value.replace(tzinfo=None, microsecond=0).isoformat() + "Z"
|
||||
@@ -40,6 +40,20 @@ def kst_from_timestamp(seconds: float) -> datetime:
|
||||
return datetime.fromtimestamp(seconds, KST)
|
||||
|
||||
|
||||
def kst_text(value: datetime | str | None) -> str:
|
||||
"""저장된 시각 → 「2026-10-30 10:02:03」 한국 시각(파일 글용) · Z · +hh:mm 있으면 바꿈 · 없는 옛 글 = 이미 한국 시각."""
|
||||
if value is None or value == "":
|
||||
return ""
|
||||
if isinstance(value, str):
|
||||
try:
|
||||
value = datetime.fromisoformat(value.strip().replace(" ", "T"))
|
||||
except ValueError:
|
||||
return value
|
||||
if value.tzinfo is None:
|
||||
return value.replace(microsecond=0).isoformat(sep=" ")
|
||||
return value.astimezone(KST).replace(tzinfo=None, microsecond=0).isoformat(sep=" ")
|
||||
|
||||
|
||||
def utc_now_naive() -> datetime:
|
||||
"""지금 — DB 칸(UTC, 시간대 표시 없음)과 견주는 값."""
|
||||
return datetime.now(timezone.utc).replace(tzinfo=None)
|
||||
|
||||
@@ -1,20 +1,22 @@
|
||||
/* =============================================================================
|
||||
* common_util_time.ts
|
||||
* 화면 시간 표시 — 저장·전송은 UTC 하나, 보일 때만 보는 사람 PC 시간대로 바꿈.
|
||||
* 서버 글(「2026-10-30 01:02:03」 · 「…T01:02:03」 = 시간대 표시 없음)은 UTC 로 읽음.
|
||||
* 서버가 내는 시각은 끝 Z(UTC) · 시간대 표시 없는 옛 기록(「2026-10-30 10:02:03」 · 「…T10:02:03」)은
|
||||
* 로컬(한국 시각, +09:00)로 적힌 것이라 한국 시각으로 읽음.
|
||||
* 짝: `common_util/common_util_time.py`(서버가 만드는 파일 글 = 한국 시각 고정).
|
||||
* ========================================================================== */
|
||||
|
||||
const DAY_MS = 24 * 3600 * 1000;
|
||||
const HAS_ZONE = /(Z|[+-]\d\d:?\d\d)$/;
|
||||
const OLD_LOCAL_ZONE = "+09:00"; // 옛 기록 = 한국 시각
|
||||
|
||||
/** 서버 시각 글 → 밀리초(UTC 기준) · 못 읽으면 null */
|
||||
/** 시각 글 → 밀리초(UTC 기준) · 시간대 표시 없는 옛 글 = 한국 시각 · 못 읽으면 null */
|
||||
export function parseUtcMs(value: string | number | null | undefined): number | null {
|
||||
if (value === null || value === undefined || value === "") return null;
|
||||
if (typeof value === "number") return Number.isFinite(value) ? value : null;
|
||||
const iso = value.trim().replace(" ", "T");
|
||||
if (!iso.includes("T")) return null; // 날짜만 = 시각이 아니라 바꾸지 않음
|
||||
const ms = Date.parse(HAS_ZONE.test(iso) ? iso : `${iso}Z`);
|
||||
const ms = Date.parse(HAS_ZONE.test(iso) ? iso : `${iso}${OLD_LOCAL_ZONE}`);
|
||||
return Number.isNaN(ms) ? null : ms;
|
||||
}
|
||||
|
||||
|
||||
@@ -360,8 +360,8 @@ async def get_workflow_state(cursor: aiomysql.DictCursor, project_id: str) -> Di
|
||||
await cursor.execute(
|
||||
"""
|
||||
SELECT stage_no, stage_key, state, progress_percent, params, message,
|
||||
DATE_FORMAT(started_at, '%%Y-%%m-%%dT%%H:%%i:%%s') AS started_at,
|
||||
DATE_FORMAT(completed_at, '%%Y-%%m-%%dT%%H:%%i:%%s') AS completed_at
|
||||
DATE_FORMAT(started_at, '%%Y-%%m-%%dT%%H:%%i:%%sZ') AS started_at,
|
||||
DATE_FORMAT(completed_at, '%%Y-%%m-%%dT%%H:%%i:%%sZ') AS completed_at
|
||||
FROM project_workflow_stages
|
||||
WHERE project_id = %s
|
||||
ORDER BY stage_no ASC
|
||||
|
||||
Reference in New Issue
Block a user