absl::Time Format·Parse
#FormatTime — 단일 진입점
표준 라이브러리의 시간 포맷팅은 std::put_time, std::strftime, std::format(C++20) 셋으로 갈라져 있다. Abseil은 absl::FormatTime 하나로 통합한다.
#include "absl/time/time.h"
absl::Time t = absl::Now();absl::TimeZone utc = absl::UTCTimeZone();
std::string s = absl::FormatTime("%Y-%m-%dT%H:%M:%S%Ez", t, utc);// "2026-05-24T16:23:45+00:00"세 가지 형태가 있다.
| 호출 | 결과 |
|---|---|
FormatTime(t) | 기본 — RFC3339, UTC, 나노초 포함 |
FormatTime(format, t, tz) | 사용자 포맷 |
FormatTime(format, t, tz, fmt_kind) | 추가 옵션(드물게 사용) |
#포맷 specifier
strftime 계열을 따르되 RFC3339 친화 확장을 추가한다.
absl::Time t = absl::FromCivil(absl::CivilSecond(2026, 5, 24, 16, 23, 45), utc);
absl::FormatTime("%Y-%m-%d %H:%M:%S", t, utc);// "2026-05-24 16:23:45"
absl::FormatTime("%E4Y-%m-%d", t, utc);// "2026-05-24" — %E4Y는 4자리 연도 보장
absl::FormatTime("%H:%M:%E*S", t, utc);// "16:23:45" — %E*S는 가변 정밀도 초(소수점 자동 트림)
absl::FormatTime("%Ez", t, utc);// "+00:00" — RFC3339 시간대 오프셋%E 접두는 Abseil 확장이다. * 부분은 정밀도 가변, 숫자는 고정 자릿수.
| Specifier | 의미 |
|---|---|
%Y, %m, %d | 연/월/일 |
%H, %M, %S | 시/분/초 |
%E4Y | 4자리 연도(음수도 처리) |
%E*S | 초 + 자동 소수점 |
%E3S | 초 + 3자리 소수점(ms) |
%E6S | 초 + 6자리 소수점(μs) |
%Ez | +HH:MM 시간대 |
%E*z | +HHMM 또는 +HH:MM(원본 유지) |
#미리 정의된 포맷 문자열
자주 쓰는 포맷은 상수로 제공한다.
absl::FormatTime(absl::RFC3339_full, t, utc);// "2026-05-24T16:23:45.123456789+00:00"
absl::FormatTime(absl::RFC3339_sec, t, utc);// "2026-05-24T16:23:45+00:00"
absl::FormatTime(absl::RFC1123_full, t, utc);// "Sun, 24 May 2026 16:23:45 +0000"
absl::FormatTime(absl::RFC1123_no_wday, t, utc);// "24 May 2026 16:23:45 +0000"코드 리뷰에서 직접 만든 포맷 문자열 보다 상수를 권장한다. 일관성과 검색성이 좋아진다.
#ParseTime — 반대 방향
absl::Time parsed;std::string err;if (!absl::ParseTime("%Y-%m-%d %H:%M:%S", "2026-05-24 16:23:45", utc, &parsed, &err)) { LOG(ERROR) << "parse failed: " << err;}시그니처는 출력 매개변수 두 개를 받는다.
bool ParseTime(absl::string_view format, absl::string_view input, absl::TimeZone tz, absl::Time* time, std::string* err);세 번째 인자가 TimeZone이다. 포맷에 시간대 specifier(%z, %Ez)가 없을 때 이 시간대로 해석한다. 있으면 입력이 우선한다.
// 입력에 시간대 없음 → utc로 해석absl::ParseTime("%Y-%m-%d %H:%M:%S", "2026-05-24 16:00:00", utc, &t, &err);
// 입력에 +09:00 있음 → KST로 해석 (utc 인자는 무시)absl::ParseTime("%Y-%m-%dT%H:%M:%S%Ez", "2026-05-24T16:00:00+09:00", utc, &t, &err);#흔한 파싱 함정
#1. ISO8601 ‘Z’ 표기
// 회피 — Z를 직접 매칭 시도absl::ParseTime("%Y-%m-%dT%H:%M:%SZ", "2026-05-24T16:00:00Z", utc, &t, &err);// ❌ false — Z는 specifier로 인식 안 됨
// Good — %Ez가 Z를 함께 처리absl::ParseTime("%Y-%m-%dT%H:%M:%S%Ez", "2026-05-24T16:00:00Z", utc, &t, &err);#2. 가변 정밀도 초
// 회피 — 고정 자릿수 specifier로 가변 입력 파싱absl::ParseTime("%H:%M:%E6S", "16:00:00.123", utc, &t, &err);// ❌ 실패 — 입력은 3자리, specifier는 6자리
// Good — %E*S로 가변 매칭absl::ParseTime("%H:%M:%E*S", "16:00:00.123", utc, &t, &err);#3. 빈 err 문자열
ParseTime은 실패 시에만 err에 쓴다. 호출 전 비워둘 필요는 없지만, 성공/실패 분기는 반드시 반환값으로 판단한다.
// 회피absl::ParseTime(fmt, in, utc, &t, &err);if (!err.empty()) { /* ... */ } // ❌ 이전 호출의 잔재일 수 있음
// Goodif (!absl::ParseTime(fmt, in, utc, &t, &err)) { /* ... */ }#로깅·직렬화 표준화
Abseil의 LOG(INFO) << absl::Now()는 자동으로 RFC3339_full을 사용한다. 직접 포맷팅이 필요한 곳에서도 RFC3339 상수를 쓰면 ELK·Splunk 같은 로그 수집기가 그대로 인식한다.
// 권장 — 로그/JSON/protobuf 직렬화std::string ts = absl::FormatTime(absl::RFC3339_full, t, absl::UTCTimeZone());
// 회피 — locale 의존 strftimechar buf[64];std::time_t tt = absl::ToTimeT(t);std::strftime(buf, sizeof(buf), "%c", std::localtime(&tt));// ❌ locale·timezone에 따라 결과가 흔들림#작은 예시 — HTTP 요청 처리
absl::StatusOr<RequestStamp> ParseStamp(absl::string_view header) { absl::Time t; std::string err; if (!absl::ParseTime(absl::RFC1123_full, header, absl::UTCTimeZone(), &t, &err)) { return absl::InvalidArgumentError( absl::StrCat("bad Date header: ", err)); }
if (t > absl::Now() + absl::Minutes(5)) { return absl::InvalidArgumentError("clock skew too large"); }
return RequestStamp{t};}#정리
FormatTime/ParseTime— 시간 ↔ 문자열 변환의 단일 진입점.%E*S,%E4Y,%Ez등 Abseil 확장이 RFC3339 정확도를 보장한다.- 미리 정의된
RFC3339_full,RFC1123_full상수를 우선 사용. ParseTime은 반환값bool로 분기,err는 진단용으로만.- 로그·직렬화는 UTC + RFC3339_full로 통일하면 다운스트림 도구와 호환된다.
#다음 장 예고
Part 7-03: CivilTime — 시간대와 무관한 달력 산술.
#관련 항목
Abseil Code Review · 41 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::time_zone 분석
absl::TimeZone — IANA TZ database 위에 얹은 안전한 변환 layer. UTC가 아닌 시각을 다루는 모든 코드가 거쳐야 할 관문.
같은 시리즈에서 이어 읽기
absl::CivilTime 분석
absl::CivilDay와 가족들 — 시간대 없는 달력 산술. 윤년·월말을 직접 처리할 필요가 없게 한다.
같은 시리즈에서 이어 읽기