absl::time_zone 분석
#시간대는 분류 문제다
UTC·로컬시간·IANA 이름·POSIX TZ 환경 변수 — 동일한 개념을 표현하는 방법이 너무 많다. Abseil은 한 가지만 인정한다: IANA 이름("Asia/Seoul", "America/Los_Angeles"). 이걸 통해 DST·역사적 오프셋 변경·UTC 변환을 정확히 처리한다.
#include "absl/time/time.h"
absl::TimeZone seoul;if (!absl::LoadTimeZone("Asia/Seoul", &seoul)) { return absl::InternalError("TZ DB missing");}
absl::TimeZone utc = absl::UTCTimeZone();absl::TimeZone local = absl::LocalTimeZone(); // 시스템 기본#TimeZone의 의미론
absl::TimeZone은 값 이다. 가볍게 복사·전달 가능. 내부적으로 IANA DB의 항목 포인터만 들고 있다.
LoadTimeZone은 비싸지만(파일 로드) 캐시된다. 같은 이름을 반복해 불러도 첫 호출 이후는 빠르다.
#변환의 세 시나리오
#1. UTC Time → Civil (지역 시계)
absl::Time t = absl::Now();absl::CivilSecond cs = absl::ToCivilSecond(t, seoul);// "지금이 한국 시계로 몇 시인가"#2. Civil + TimeZone → UTC Time
absl::CivilSecond cs(2026, 5, 24, 9, 0, 0);absl::Time t = absl::FromCivil(cs, seoul);// "한국 시각 09:00은 절대 시간으로 언제인가"#3. Time → Time (시간대만 바꾼 표현)
absl::Time은 시간대를 모른다. UTC epoch 기준 절대값이므로 “시간대 변환”이라는 개념이 없다. 변환은 항상 표시 시점에 일어난다.
absl::Time t = absl::Now();std::string seoul_str = absl::FormatTime(absl::RFC3339_full, t, seoul);std::string la_str = absl::FormatTime(absl::RFC3339_full, t, la);// 같은 t, 다른 표시이 점이 가장 헷갈리는데, 한 번 익히면 코드가 훨씬 깨끗해진다. 내부 저장은 항상 absl::Time(UTC epoch), 표시·파싱 시점에만 TimeZone이 등장한다.
#DST 모호성 처리
봄/가을의 DST 전환 시각은 민감하다. 한 시각이 사라지거나 두 번 나타난다.
absl::TimeZone la;absl::LoadTimeZone("America/Los_Angeles", &la);
absl::CivilSecond skipped(2026, 3, 8, 2, 30, 0); // DST 시작 — 02:30 없음absl::TimeZone::CivilInfo info = la.At(skipped);
switch (info.kind) { case absl::TimeZone::CivilInfo::UNIQUE: // 보통 경우 break; case absl::TimeZone::CivilInfo::SKIPPED: // DST 시작 — 시각이 존재하지 않음 // info.trans: 전환 직후 시각 // info.pre / info.post: 두 가지 해석 break; case absl::TimeZone::CivilInfo::REPEATED: // DST 종료 — 시각이 두 번 일어남 break;}FromCivil은 분기 없이 부르면 합리적 기본값(보통 post-transition)을 선택한다. 청구·알람처럼 정확도가 중요한 곳만 At()로 명시 처리한다.
#시스템 TimeZone
absl::LocalTimeZone()은 OS에 따라 다음을 본다.
| OS | 출처 |
|---|---|
| Linux/macOS | TZ 환경 변수 → /etc/localtime symlink |
| Windows | 레지스트리 + ICU |
서버 코드는 명시적 TZ를 권장한다. 운영자가 머신 시간대를 바꾸면 동작이 달라지는 일을 막는다.
// 회피 — 호스트 의존auto local = absl::LocalTimeZone();
// Good — 서비스 TZ 명시absl::TimeZone svc;ABSL_CHECK(absl::LoadTimeZone(absl::GetFlag(FLAGS_service_timezone), &svc));#TZ DB 가용성
LoadTimeZone은 OS의 zoneinfo 파일(/usr/share/zoneinfo)을 읽는다. 컨테이너 베이스 이미지가 slim이면 zoneinfo가 없을 수 있다.
# Debian slimRUN apt-get install -y tzdata
# AlpineRUN apk add --no-cache tzdataAbseil은 zoneinfo 파일이 없으면 LoadTimeZone이 false를 반환한다. 빌드에 임베드된 zoneinfo가 필요하면 absl::FixedTimeZone(offset_seconds)로 폴백한다.
absl::TimeZone svc;if (!absl::LoadTimeZone("Asia/Seoul", &svc)) { LOG(WARNING) << "tzdata missing, using +09:00 fixed"; svc = absl::FixedTimeZone(9 * 60 * 60);}FixedTimeZone은 DST·역사적 변경을 모른다. 비상용에 한정.
#작은 예시 — 사용자 별 알림 시간
struct User { std::string id; std::string tz_name; // "Asia/Seoul" 등 absl::CivilSecond preferred_local; // 09:00:00 등};
absl::Time NextNotification(const User& u, absl::Time now) { absl::TimeZone tz; if (!absl::LoadTimeZone(u.tz_name, &tz)) { tz = absl::UTCTimeZone(); }
absl::CivilDay today = absl::ToCivilDay(now, tz); absl::CivilSecond candidate(today, u.preferred_local.hour(), u.preferred_local.minute(), u.preferred_local.second()); absl::Time t = absl::FromCivil(candidate, tz);
if (t <= now) { // 오늘 시각이 지났으면 내일 candidate = absl::CivilSecond(today + 1, u.preferred_local.hour(), u.preferred_local.minute(), u.preferred_local.second()); t = absl::FromCivil(candidate, tz); } return t;}저장은 absl::Time 하나, 표시는 사용자 TZ. DST 전환일에도 동작이 명확하다.
#코드 리뷰 체크리스트
// 회피 — 시간대 가정int hour = (epoch_seconds / 3600) % 24; // 어디 시간?
// Good — TZ 명시absl::Time t = absl::FromUnixSeconds(epoch_seconds);int hour = absl::ToCivilHour(t, user_tz).hour();// 회피 — 문자열로 TZ 표현std::string tz_offset = "+0900";
// Good — IANA 이름std::string tz_name = "Asia/Seoul"; // DST·역사 변경 자동 처리// 회피 — TZ 로드 실패 무시absl::TimeZone tz;absl::LoadTimeZone(name, &tz); // 반환값 무시
// Goodif (!absl::LoadTimeZone(name, &tz)) { return absl::InvalidArgumentError(absl::StrCat("unknown tz: ", name));}#정리
absl::TimeZone은 IANA 이름을 통해 DST·역사 변경을 정확히 처리.absl::Time은 시간대 모름. 변환은 표시/파싱 시점에만.- DST 전환 시각은
At()로 SKIPPED/REPEATED 분기 처리 가능. 보통은FromCivil기본값으로 충분. - 컨테이너 이미지에는
tzdata패키지 필수. FixedTimeZone은 비상용 — DST를 모른다.
#다음 장 예고
Part 7-05: Time mocking — 테스트에서 시간을 통제하는 패턴.
#관련 항목
- Part 7-03: CivilTime
- Part 12-01: ABSL_FLAG —
--service_timezone같은 명시 TZ flag - 원문 — Time Zones
Abseil Code Review · 43 of 79
- 1 Abseil Code Review — Google production-grade C++ 라이브러리 분석
- 2 Abseil 개요 — Google이 std를 보완한 이유
- 3 Abseil 설계 철학 — std 호환과 추가 기능의 균형
- 4 Abseil 빌드와 의존성 — Bazel vs CMake
- 5 Abseil LTS vs HEAD 릴리스 모델 분석
- 6 Abseil Versioning과 ABI 호환성 정책
- 7 Abseil 매크로 — ABSL_HAVE_*·ABSL_ATTRIBUTE_*
- 8 Abseil ABSL_PREDICT_TRUE/FALSE — branch hint
- 9 absl::LogSeverity — 로그 레벨 타입
- 10 Abseil type_traits — negation·conjunction·void_t
- 11 Abseil Conformance·Policy 분석
- 12 Abseil Memory utilities 분석
- 13 Abseil raw_logging — heap-free 로깅
- 14 Abseil thread_annotations — clang TSA 통합
- 15 absl::Status — exception-free error handling
- 16 absl::StatusOr<T> — 값 또는 에러
- 17 absl status_macros — ASSIGN_OR_RETURN·RETURN_IF_ERROR
- 18 absl::Status payload — 구조화된 에러 컨텍스트
- 19 absl::Status ↔ exception 변환 패턴
- 20 absl::string_view — non-owning 문자열 참조
- 21 absl::string_view 함정 — dangling·c_str·임시 객체
- 22 absl::StrCat — 가변 인자 문자열 연결과 AlphaNum
- 23 absl::StrSplit — Delimiter·Predicate·컨테이너 변환
- 24 absl::StrJoin — 컨테이너 결합과 Formatter
- 25 absl::StrFormat — type-safe printf·FormatSpec
- 26 Abseil ASCII 함수 — locale-free 분류·대소문자 변환
- 27 Abseil Escape — CEscape·HexEscape·Base64
- 28 absl::flat_hash_map — Swiss Table 기반 hash map
- 29 absl::flat_hash_set — set 버전 Swiss Table
- 30 absl::node_hash_map — stable pointer가 필요할 때
- 31 absl::btree_map — sorted·cache-friendly B-tree
- 32 absl::FixedArray — 런타임 크기 stack 배열
- 33 absl::InlinedVector — small buffer optimization
- 34 Abseil Swiss Table internals — control byte·SIMD probing
- 35 absl::Mutex — reader-writer·fairness·deadlock 검출
- 36 absl::Mutex Conditional Critical Section — Await로 cv 없애기
- 37 absl::Notification — once-only signal
- 38 absl::BlockingCounter·Barrier — 다중 thread 조율
- 39 absl::Mutex annotations — clang thread-safety로 race를 컴파일 타임에
- 40 absl::Time·Duration 분석 — 단단한 type
- 41 absl::Time Format·Parse
- 42 absl::CivilTime 분석
- 43 absl::time_zone 분석
- 44 absl::Time mocking — 테스트 친화 시간
- 45 absl::BitGen — 모던 난수 생성기
- 46 Abseil Random Distributions — Uniform·Exponential
- 47 Abseil Mocking Random — 테스트 결정성
- 48 Abseil Random Seeding·Entropy
- 49 absl::int128·uint128 분석
- 50 absl::bits — popcount·countl_zero
- 51 absl::optional vs std::optional
- 52 absl::variant 분석
- 53 absl::span 분석
- 54 absl::any 분석
- 55 absl::compare — three-way 비교
- 56 Abseil utility — apply·in_place
- 57 Abseil AbslHashValue 분석
- 58 Abseil HashState chaining
- 59 Abseil Custom hashable 구현
- 60 Abseil LOG·VLOG·CHECK 분석
- 61 Abseil LogSink 분석
- 62 Abseil LogEntry·structured logging
- 63 Abseil Stack trace·failure_signal_handler
- 64 ABSL_FLAG 정의 분석
- 65 Abseil ParseCommandLine 동작
- 66 Abseil Flag introspection·validation
- 67 Google 스타일의 Abseil 사용 패턴
- 68 Abseil 자주 보는 anti-pattern
- 69 std → absl 마이그레이션 전략
- 70 absl::Cleanup — 함수 종료 시 실행 보장
- 71 Abseil algorithm container 확장 — c_sort·c_find_if·c_count_if
- 72 absl::function_ref와 any_invocable — 함수 객체 전달의 두 축
- 73 absl::bind_front와 Overload — 함수 객체 보조 도구
- 74 absl::Cord — 분산 시스템용 대용량 문자열
- 75 absl::from_chars·SimpleAtoi — 빠른 숫자 변환
- 76 absl::Cord vs std::string — 선택 기준과 메모리 프로파일
- 77 absl::GetStackTrace와 Symbolize — crash 시 readable stack
- 78 absl::ComputeCrc32c — 하드웨어 가속 체크섬
- 79 absl::PeriodicSampler — 적응형 샘플링·jitter 회피
관련 글
absl::Time mocking — 테스트 친화 시간
테스트에서 절대 시간을 통제하기 — clock 주입 패턴과 absl::Now() 대체 전략.
같은 시리즈에서 이어 읽기
absl::CivilTime 분석
absl::CivilDay와 가족들 — 시간대 없는 달력 산술. 윤년·월말을 직접 처리할 필요가 없게 한다.
같은 시리즈에서 이어 읽기
absl::Time Format·Parse
FormatTime, ParseTime — Abseil이 strftime/RFC3339를 한 함수로 흡수하는 방법.
같은 시리즈에서 이어 읽기