absl::Time·Duration 분석 — 단단한 type
#왜 또 하나의 시간 라이브러리인가
C++11에 <chrono>가 들어왔는데도 Google이 absl::Time을 따로 만든 이유는 단순하다. std::chrono는 강력하지만 너무 일반화 되어 있고, 일반 엔지니어가 매일 쓰기에는 boilerplate가 많다. 시간대(time zone), 달력(civil time), mocking 같은 일상 작업에서 표준은 침묵한다(C++20에서 일부 보강되었으나 14/17 코드베이스에는 무용지물이다).
Abseil은 두 가지 핵심 타입만 둔다.
absl::Time— 어떤 순간. UTC epoch 기준의 단일 timestamp.absl::Duration— 두 순간 사이의 간격. 부호 있는 길이.
그 외 달력은 absl::CivilDay, 시간대는 absl::TimeZone으로 분리한다. 각 타입이 한 가지 의미만 가지므로 함수 시그니처에 의도가 그대로 드러난다.
std::chrono와의 차이를 항목별로 비교하면 다음과 같다.
#단위 안전 — Duration이 부동소수처럼 보이는 이유
absl::Duration은 내부적으로 nanosecond resolution이지만 사용자는 단위를 의식하지 않는다.
#include "absl/time/time.h"
absl::Duration timeout = absl::Seconds(30);absl::Duration jitter = absl::Milliseconds(250);absl::Duration total = timeout + jitter; // 자동 합산단위 변환이 생성자가 아니라 팩토리 함수로만 일어난다. 그래서 다음과 같은 코드는 컴파일되지 않는다.
// 회피 — 단위 명시 없는 정수 대입absl::Duration d = 30; // ❌ 컴파일 에러void Sleep(absl::Duration d);Sleep(1000); // ❌ 1초인지 1ms인지 모름// Good — 단위가 함수 호출에 박혀 있음Sleep(absl::Seconds(1)); // ✅ 명백Sleep(absl::Milliseconds(1)); // ✅ 명백std::chrono::seconds와 비교하면 차이가 분명하다. chrono는 std::chrono::seconds{1}처럼 타입 자체가 단위를 인코딩한다. Abseil은 단일 타입(Duration)에 팩토리 단위를 분리한다. 이쪽이 API 시그니처가 한결 깔끔하다.
// std::chrono — 단위마다 다른 타입void Wait(std::chrono::seconds s);void Wait(std::chrono::milliseconds ms); // 오버로드 폭발
// abseil — 단일 타입void Wait(absl::Duration d);#Time — UTC epoch 기준 단일 timestamp
absl::Time은 순간만 표현한다. 시간대도, 달력도 모른다.
absl::Time start = absl::Now();DoExpensiveWork();absl::Duration elapsed = absl::Now() - start; // Time - Time = Duration
if (elapsed > absl::Seconds(5)) { LOG(WARNING) << "Slow: " << elapsed;}산술 규칙이 직관적이다.
| 연산 | 결과 |
|---|---|
Time + Duration | Time |
Time - Time | Duration |
Duration + Duration | Duration |
Duration * double | Duration |
Duration / Duration | double (비율) |
Time + Time은 컴파일 에러다. epoch 합산은 의미가 없으므로 타입이 막는다. 이런 정적 안전성은 std::chrono::time_point + time_point도 동일하게 막지만, Abseil은 단일 Time 타입으로 묶어 사용 측 boilerplate를 줄였다.
#Duration의 음수 — 부호 있는 간격
absl::Duration은 부호 있는 값이다.
absl::Duration d = absl::Seconds(-3); // OKabsl::Duration negated = -timeout; // 부호 반전
if (deadline - absl::Now() < absl::ZeroDuration()) { // 이미 지난 deadline}absl::InfiniteDuration()과 -absl::InfiniteDuration()도 valid한 값이다. 산술에 자연스럽게 흡수된다.
absl::Duration timeout = WantsBlocking() ? absl::InfiniteDuration() : absl::Seconds(5);// timeout이 무한이면 Wait()는 영원히 대기mutex.LockWhenWithTimeout(condition, timeout);#단위 추출 — 명시적 변환
Duration → 정수/실수 변환은 손실 가능성을 명시 하는 함수 이름을 쓴다.
absl::Duration d = absl::Microseconds(1500);
int64_t us = absl::ToInt64Microseconds(d); // 1500int64_t ms = absl::ToInt64Milliseconds(d); // 1 (정수 절단)double s = absl::ToDoubleSeconds(d); // 0.0015
// 회피 — 단위 모호한 추출 시도int64_t raw = d.count(); // ❌ 그런 메서드 없음absl::ToInt64*는 0 방향 절단을 한다. 반올림이 필요하면 Trunc/Floor/Ceil/Round를 명시적으로 부른다.
absl::Duration ms = absl::Trunc(d, absl::Milliseconds(1)); // ms 단위로 절단absl::Duration ms2 = absl::Round(d, absl::Milliseconds(1)); // ms 단위로 반올림#한 줄 비교 — chrono vs absl
| 작업 | std::chrono | abseil |
|---|---|---|
| 지금 시각 | std::chrono::system_clock::now() | absl::Now() |
| 5초 후 | now + std::chrono::seconds{5} | absl::Now() + absl::Seconds(5) |
| 정수 ms 추출 | duration_cast<ms>(d).count() | absl::ToInt64Milliseconds(d) |
| 단위 명시 | 타입(seconds, milliseconds…) | 팩토리(Seconds, Milliseconds…) |
| 시간대 변환 | C++20부터 zoned_time | absl::TimeZone + ConvertDateTime |
| Mocking | 표준에 없음 | TestTime 또는 시계 주입 |
#코드 리뷰에서 자주 보는 지적
// 회피 — 단위 없는 raw intvoid RetryAfter(int seconds);RetryAfter(60); // 분? 초?
// Good — Duration으로 단위 박제void RetryAfter(absl::Duration delay);RetryAfter(absl::Minutes(1));// 회피 — 시간 비교에 raw 정수 msif (elapsed_ms > 5000) { /* ... */ }
// Good — Duration끼리 비교if (elapsed > absl::Seconds(5)) { /* ... */ }// 회피 — std::time_t 그대로 노출std::time_t deadline = ...;
// Good — absl::Time으로 캡슐화absl::Time deadline = absl::FromTimeT(legacy);#정리
absl::Time은 순간,absl::Duration은 간격. 두 타입의 산술 규칙이 의미를 강제한다.- 단위는 팩토리 함수에 박혀 있어 정수 대입이 컴파일되지 않는다.
- 추출은
ToInt64*/ToDouble*로 명시. 절단/반올림이 코드에 드러난다. - 무한·음수가 valid value. 분기 없이 산술에 그대로 흡수된다.
- chrono보다 표면적이 좁아 일상 코드의 시그니처가 한결 단순하다.
#다음 장 예고
Part 7-02에서는 FormatTime/ParseTime으로 RFC3339·strftime 호환 포맷을 다룬다.
#관련 항목
- Part 6-01: absl::Mutex vs std::mutex —
Duration기반 timeout API - Part 7-03: CivilTime
- Effective Modern C++ — 항목 7: chrono 활용
- 원문 — Abseil Time
Abseil Code Review · 40 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와 가족들 — 시간대 없는 달력 산술. 윤년·월말을 직접 처리할 필요가 없게 한다.
같은 시리즈에서 이어 읽기