This Abseil time
library contains abstractions for holding time values, both
in terms of absolute time and civil time. The time library consists of the
following components:
time.h
holds abstractions for absolute time (and durations), and helper
functions for constructing, parsing, and converting these types. Additionally,
this header file contains support for time zones, allowing you to map between
absolute and civil times.civil_time.h
holds abstractions for civil time, and helper functions for
constructing, parsing, and converting these typesclock.h
holds utility functions for creating time objects using the system
clockThese abstractions and utility functions are documented below.
[These concepts were presented at CppCon 2015: video here ]
There are two ways to represent time: as an Absolute Time, and as a Civil
Time. An absolute time uniquely and universally represents a specific instant
in time. Every event occurs at a specific absolute time, and everyone in the
world will agree on the absolute time when the event occurred. A time_t
is a
well-known absolute time type. Consider the moment when Neil Armstrong first put
his left foot on the moon. He did that for the first time only once. He did not
do it again an hour later for the audience one time zone to the west. Everyone
in the world will agree on the absolute time when this event happened.
On the other hand, not everyone will agree on the civil time when that giant leap was made. A civil time is represented as six individual fields that represent a year, month, day, hour, minute, and second (aka YMDHMS). These six fields represent the time as defined by some local government. Your civil time matches the values shown on the clock hanging on your wall and the Dilbert calendar on your desk. Your friend living across the country may, at the same moment, have a different civil time showing on their Far Side calendar and clock. For example, if you lived in New York, then you witnessed Neil Armstrong’s small step on July 20, 1969 at 10:56 PM, whereas your friend in San Francisco saw the same thing at 7:56 PM, and your pen pal in Sydney saw it while eating lunch on July 21, 1969 at 12:56 PM. You all would agree on the absolute time of the event, but you’d disagree about the civil time.
Time zones are geo-political regions within which rules are shared to convert between absolute times and civil times. The geographical nature of time zones is evident in their identifiers, which look like “America/New_York”, “America/Los_Angeles”, and “Australia/Sydney”. A time zone’s rules include things like the region’s offset from the UTC time standard, daylight-saving adjustments, and short abbreviation strings. Since these rules may change at the whim of the region’s local government, time zones have a history of disparate rules that apply only for certain periods. Time zones are tremendously complicated, which is why you should always let a time library do time-zone calculations for you.
Time zones define the relationship between absolute and civil times. Given an absolute or civil time and a time zone, you can compute the other time, as shown in the example below and illustrated in Figure 1.
Civil Time = F(Absolute Time, Time Zone)
Absolute Time = F'(Civil Time, Time Zone)
Figure 1
The concepts described thus far—absolute time, civil time, and time zone—are universal concepts that apply to all programming languages equally because they describe time in the real world. Different programming languages and libraries may model these concepts differently with different classes and sometimes even different names, but these fundamental concepts and relationships will still exist.
The Abseil time library contains several core classes mapping to the above concepts:
absl::Time
class. (See
time.h.) This is a small,
integer-like type that should be passed around by value, and computed using
arithmetic operators and descriptively named functions. An absl::Time
can be
converted to and from other absolute time representations using conversion
functions, and to and from civil time representations with the help of an
absl::TimeZone
.absl::Civil*
object which should
be passed by value. (See
civil_time.h.) These
integer values may be specified as arguments to civil-time constructors (e.g.
absl::CivilYear
or absl::CivilSecond
, or parsed from a formatted time
string.absl::TimeZone
class. (See
time.h.) This mostly-opaque
value type is passed by value to other Abseil time functions that will then
perform the necessary conversions to/from absolute time or civil time. It is a
feature that the Abseil time library itself performs all time-zone arithmetic
on your behalf, virtually eliminating offset calculation bugs from your code.An important auxiliary concept that has not yet been discussed is that of a
fixed-length span of time, which is represented by the absl::Duration
class.
This small, integer-like type should be passed around by value, and computed
using normal integer-like arithmetic operators. The Abseil time library contains
several functions for converting to and from other duration types, as well as
performing floating-point operations. Durations are unit-safe, making them the
ideal type for interfaces accepting timeouts or any other fixed-length span of
time.
Figure 2 illustrates the main Abseil Time types and shows how they match the fundamental time concepts described above.
Figure 2
absl::Time
An absl::Time
represents a specific instant in time, as a count of clock ticks
of some granularity (resolution) from some starting point (epoch). absl::Time
should be passed by value rather than const reference. Arithmetic operators are
provided for naturally expressing time calculations.
NOTE: although absl::Time
resolution is guaranteed at least down to
nanoseconds, in practice it may be finer. Regardless of this fact, both the time
resolution and epoch are implementation details and you should not depend on them.
absl::Time
uses a resolution that is high enough to avoid loss in
precision, and a range that is wide enough to avoid overflow, when
converting between tick counts in most usable time scales (i.e., precision
of at least one nanosecond, and range +/-100 billion years). Conversions
between the time scales are performed by truncating (towards negative
infinity) to the nearest representable point.
absl::Time
assumes there are 60 seconds in a minute, which means the
underlying time scales must be “smeared” to eliminate leap seconds.
See https://developers.google.com/time/smear.
absl::Time
An absl::Time
instance can be constructed directly:
// Construct an absl::Time from the system clock. (See clock.h)
absl::Time t1 = absl::Now();
// Default construction produces an absolute time of the UNIX epoch
absl::Time t2 = absl::Time();
// For clarity, prefer constructing such times directly
absl::Time t3 = absl::UnixEpoch();
absl::Time
values are commonly created from other value types. The Abseil
time library contains a number of absl::From*()
factory functions that accept
the gamut of other time representations.
// Construct an absl::Time from a time_t. time() returns the number of seconds
// since the UNIX epoch.
time_t tt = time(NULL);
absl::Time t1 = absl::FromTimeT(tt);
// Construct an absl::Time from a std::chrono time
auto tp = std::chrono::system_clock::from_time_t(123);
absl::Time t2 = absl::FromChrono(tp);
// Construct an absl::Time using a conversion function. (Assume MyCustomTime()
// returns microseconds since the UNIX epoch.)
int64_t unix_micros = MyCustomTime();
absl::Time t3 = absl::FromUnixMicros(unix_micros);
// Construct an absl::Time from a civil time and time zone
// (See below for more information on these types)
absl::TimeZone nyc;
// LoadTimeZone may fail so it's always better to check for success.
if (!absl::LoadTimeZone("America/New_York", &nyc)) {
// handle error case
}
// My flight leaves NYC on Jan 2, 2017 at 03:04:05
absl::CivilSecond ct(2017, 1, 2, 3, 4, 5);
absl::Time takeoff = absl::FromCivil(ct, nyc);
absl::Time
Formatting and parsing functions are provided for converting to and from
strings. FormatTime()
allows you to take an absolute time and time zone and
return a string representing that time. (See Time Zones below.)
// Construct an absl::Time from the system clock.
absl::Time t1 = absl::Now();
// When formatting a time, a time zone should be passed.
absl::TimeZone utc = absl::UTCTimeZone();
std::cout << absl::FormatTime(t1, utc);
// Outputs, e.g. "2018-08-06T23:35:32.637472794+00:00"
// The stream operator for absl::Time uses FormatTime(), using the
// absl::LocalTimeZone(). Prefer calling FormatTime() directly using an
// explicit time zone, as done above.
std::cout << t1 << "\n";
// Outputs e.g. "2018-08-06T17:35:32.637472794-04:00"
An absl::Duration
represents a signed, fixed-length span of time. A
Duration
is generated using a unit-specific factory function, or is the result
of subtracting one absl::Time
from another. Durations behave like unit-safe
integers and they support all the natural integer-like arithmetic operations.
Arithmetic overflows and saturates at +/- infinity. Duration
should be passed
by value rather than const reference.
Factory functions Nanoseconds()
, Microseconds()
, Milliseconds()
,
Seconds()
, Minutes()
, Hours()
and InfiniteDuration()
allow for creation
of constexpr Duration
values
constexpr absl::Duration ten_ns = absl::Nanoseconds(10);
constexpr absl::Duration min = absl::Minutes(1);
constexpr absl::Duration hour = absl::Hours(1);
absl::Duration dur = 60 * min; // dur == hour
absl::Duration half_sec = absl::Milliseconds(500);
absl::Duration quarter_sec = 0.25 * absl::Seconds(1);
Duration
values can be easily converted to an integral number of units
using the division operator.
constexpr absl::Duration dur = absl::Milliseconds(1500);
int64_t ns = dur / absl::Nanoseconds(1); // ns == 1500000000
int64_t ms = dur / absl::Milliseconds(1); // ms == 1500
int64_t sec = dur / absl::Seconds(1); // sec == 1 (subseconds truncated)
int64_t min = dur / absl::Minutes(1); // min == 0
Additionally, the Abseil time library provides helper functions for converting
duration values into int64_t
or double
values:
ToInt64Nanoseconds()
and ToDoubleNanoseconds()
ToInt64Microseconds()
and ToDoubleMicroseconds()
ToInt64Milliseconds()
and ToDoubleMilliseconds()
ToInt64Seconds()
and ToDoubleSeconds()
ToInt64Minutes()
and ToDoubleMinutes()
ToInt64Hours()
and ToDoubleHours()
absl::Duration d = absl::Milliseconds(1500);
int64_t isec = absl::ToInt64Seconds(d); // isec == 1
absl::Duration d = absl::Milliseconds(1500);
double dsec = absl::ToDoubleSeconds(d); // dsec == 1.5
The term “civil time” refers to the legally recognized human-scale time that is
represented by the six fields YYYY-MM-DD hh:mm:ss
. A “date” is perhaps the
most common example of a civil time. Modern-day civil time follows the Gregorian
Calendar and is a timezone-independent concept: a civil time of “2016-06-01
12:00:00”, for example, is not tied to a time zone. Put another way, a civil
time does not map to a unique point in time; a civil time must be mapped to an
absolute time through a time zone.
Because a civil time is what most people think of as “time,” it is common to map absolute times to civil times to present to users. The Abseil time library allows you to construct such civil times from absolute times; consult the time.h for such functionality.
This library provides six classes for constructing civil-time objects, and provides several helper functions for rounding, iterating, and performing arithmetic on civil-time objects, while avoiding complications like daylight-saving time (DST):
absl::CivilSecond
absl::CivilMinute
absl::CivilHour
absl::CivilDay
absl::CivilMonth
absl::CivilYear
Prefer to pass these absl::Civil*
types by value rather than const reference.
Each of these civil-time types is a simple value type with the same interface for construction and the same six accessors for each of the civil time fields (year, month, day, hour, minute, and second, aka YMDHMS). These classes differ only in their alignment, which is indicated by the type name and specifies the field on which arithmetic operates.
Each of the civil-time types can be constructed in two ways: by directly passing to the constructor up to six integers representing the YMDHMS fields, or by copying the YMDHMS fields from a differently aligned civil-time type. Omitted fields are assigned their minimum valid value. Hours, minutes, and seconds will be set to 0, month and day will be set to 1. Since there is no minimum year, the default is 1970.
absl::CivilDay default_value; // 1970-01-01 00:00:00
// Construct a civil-time object for a specific day
absl::CivilDay a(2015, 2, 3); // 2015-02-03 00:00:00
absl::CivilDay b(2015, 2, 3, 4, 5, 6); // 2015-02-03 00:00:00
absl::CivilDay c(2015); // 2015-01-01 00:00:00
// Construct a civil-time object for a specific second
absl::CivilSecond ss(2015, 2, 3, 4, 5, 6); // 2015-02-03 04:05:06
absl::CivilMinute mm(ss); // 2015-02-03 04:05:00
absl::CivilHour hh(mm); // 2015-02-03 04:00:00
absl::CivilDay d(hh); // 2015-02-03 00:00:00
absl::CivilMonth m(d); // 2015-02-01 00:00:00
absl::CivilYear y(m); // 2015-01-01 00:00:00
m = absl::CivilMonth(y); // 2015-01-01 00:00:00
d = absl::CivilDay(m); // 2015-01-01 00:00:00
hh = absl::CivilHour(d); // 2015-01-01 00:00:00
mm = absl::CivilMinute(hh); // 2015-01-01 00:00:00
ss = absl::CivilSecond(mm); // 2015-01-01 00:00:00
NOTE: fields that are out-of-range are normalized (e.g., October 32 -> November 1) so that all civil-time objects represent valid values. See Normalization below for more information.
Each civil-time class is aligned to the civil-time field indicated in the class’s name after normalization. Alignment is performed by setting all the inferior fields to their minimum valid value (as described above). The following are examples of how each of the six types would align the fields representing November 22, 2015 at 12:34:56 in the afternoon. (Note: the string format used here is not important; it’s just a shorthand way of showing the six YMDHMS fields.)
absl::CivilSecond : 2015-11-22 12:34:56
absl::CivilMinute : 2015-11-22 12:34:00
absl::CivilHour : 2015-11-22 12:00:00
absl::CivilDay : 2015-11-22 00:00:00
absl::CivilMonth : 2015-11-01 00:00:00
absl::CivilYear : 2015-01-01 00:00:00
Each civil-time type performs arithmetic on the field to which it is aligned.
This means that adding 1 to an absl::CivilDay
increments the day field
(normalizing as necessary), and subtracting 7 from an absl::CivilMonth
operates on the month field (normalizing as necessary). All arithmetic produces
a valid civil time. Differences require two similarly aligned civil-time objects
and return the scalar answer in units of the objects’ alignment. For example,
the difference between two absl::CivilHour
objects will give an answer in
units of civil hours.
The alignment of a civil-time object cannot change, but the object may be used
to construct a new object with a different alignment. This is referred to as
“realigning”. When realigning to a type with the same or more precision (e.g.,
absl::CivilDay
-> absl::CivilSecond
), the conversion may be performed
implicitly since no information is lost. However, if information could be
discarded (e.g., absl::CivilSecond
-> absl::CivilDay
), the conversion must
be explicit at the call site.
void UseDay(absl::CivilDay day);
absl::CivilSecond cs;
UseDay(cs); // Won't compile because data may be discarded
UseDay(absl::CivilDay(cs)); // OK: explicit conversion
absl::CivilDay cd;
UseDay(cd); // OK: no conversion needed
absl::CivilMonth cm;
UseDay(cm); // OK: implicit conversion to absl::CivilDay
Normalization takes invalid values and adjusts them to produce valid values.
Within the civil-time library, integer arguments passed to the Civil*
constructors may be out-of-range, in which case they are normalized by
carrying overflow into a field of coarser granularity to produce valid
civil-time objects. This normalization enables natural arithmetic on
constructor arguments without worrying about the field’s range.
// Out-of-range; normalized to 2016-11-01
absl::CivilDay d(2016, 10, 32);
// Out-of-range, negative: normalized to 2016-10-30T23
absl::CivilHour h1(2016, 10, 31, -1);
// Normalization is cumulative: normalized to 2016-10-30T23
absl::CivilHour h2(2016, 10, 32, -25);
NOTE: If normalization is undesired, you can signal an error by comparing the constructor arguments to the normalized values returned by the YMDHMS properties.
Comparison between civil-time objects considers all six YMDHMS fields, regardless of the type’s alignment. Comparison between differently aligned civil-time types is allowed.
absl::CivilDay feb_3(2015, 2, 3); // 2015-02-03 00:00:00
absl::CivilDay mar_4(2015, 3, 4); // 2015-03-04 00:00:00
// feb_3 < mar_4
// absl::CivilYear(feb_3) == absl::CivilYear(mar_4)
absl::CivilSecond feb_3_noon(2015, 2, 3, 12, 0, 0); // 2015-02-03 12:00:00
// feb_3 < feb_3_noon
// feb_3 == absl::CivilDay(feb_3_noon)
// Iterates all the days of February 2015.
for (absl::CivilDay d(2015, 2, 1); d < absl::CivilMonth(2015, 3); ++d) {
// ...
}
Civil-time types support natural arithmetic operators such as addition, subtraction, and difference. Arithmetic operates on the civil-time field indicated in the type’s name. Difference operators require arguments with the same alignment and return the answer in units of the alignment.
absl::CivilDay a(2015, 2, 3);
++a; // 2015-02-04 00:00:00
--a; // 2015-02-03 00:00:00
absl::CivilDay b = a + 1; // 2015-02-04 00:00:00
absl::CivilDay c = 1 + b; // 2015-02-05 00:00:00
int n = c - a; // n = 2 (civil days)
int m = c - absl::CivilMonth(c); // Won't compile: different types.
Each civil-time type has accessors for all six of the civil-time fields: year, month, day, hour, minute, and second.
year()
returning a civil_year_t
valuemonth()
returning an int
day()
returning an int
hour()
returning an int
minute()
returning an int
second()
returning an int
Recall that fields inferior to the type’s aligment will be set to their minimum valid value.
absl::CivilDay d(2015, 6, 28);
// d.year() == 2015
// d.month() == 6
// d.day() == 28
// d.hour() == 0
// d.minute() == 0
// d.second() == 0
The absl::TimeZone
is an opaque, small, value-type class representing a
geo-political region within which particular rules are used for converting
between absolute and civil times. absl::TimeZone
values are named using the
TZ identifiers from the IANA Time Zone Database, such as “America/Los_Angeles”
or “Australia/Sydney”, and may be loaded from the absl::LoadTimeZone()
factory function.
NOTE: strings like “PST” and “EDT” are not valid TZ identifiers.
absl::TimeZone lax;
// Because LoadTimeZone() may fail, it is always safer to load time zones
// checking its return value:
if (!absl::LoadTimeZone("America/Los_Angeles", &lax)) {
// Handle failure
}
Prefer to pass absl::TimeZone
by value rather than const reference.
The Abseil time library also includes several convenience functions for constructing time zones:
absl::UTCTimeZone()
absl::FixedTimeZone()
to construct a time zone as an offset to UTC
(ProTip: avoid using this, if possible. This function is for use with legacy
APIs).absl::LocalTimeZone()
to construct a time zone from the “local” time zone,
if known (and defaults to UTC if not known). Prefer to use an explicit time
zone instead.absl::TimeZone utc = absl::UTCTimeZone();
// Constructing a time zone from a fixed UTC-offset may be necessary when
// working with legacy APIs that aren't really timezone-aware. Modern code
// should avoid this as much as possible.
absl::TimeZone pst = absl::FixedTimeZone(-8 * 60 * 60);
// Similarly, using a local time zone, rather than explicitly loading a
// particular time zone, should be done with caution, because you don't know
// which time zone will actually be returned.
absl::TimeZone loc = absl::LocalTimeZone();
For more background information on time zones, see the IANA Time Zone Database and the Wikipedia article on time zone definitions
An absl::TimeZone
is used to convert between absl::Time
values and
Abseil civil-time values. In both cases, you use the absl::TimeZone::At()
member function. Helper functions also exist for converting to and from absolute
and civil times.
See Converting Absolute Times to Civil Times and
Converting Civil Times to Absolute Times for usage on
converting to and from these time values using absl::TimeZone::At()
.
The Abseil time library contains a set of functions to convert an absl::Time
and an absl::TimeZone
into a civil time:
ToCivilSecond()
ToCivilMinute()
ToCivilHour()
ToCivilDay()
ToCivilMonth()
ToCivilYear()
absl::Time t = ...;
absl::TimeZone tz = ...;
const absl::CivilDay cd = absl::ToCivilDay(t, tz);
If you need more than just the civil time, TimeZone::At(Time)
returns
additional information about the conversion. If the input time is infinite,
the output civil second will be set to CivilSecond::max()
or
CivilSecond::min()
as appropriate, and the subsecond will be infinite.
// Load the Los Angeles TimeZone
absl::TimeZone lax;
if (!absl::LoadTimeZone("America/Los_Angeles", &lax)) {
// handle error case
}
// Determine the civil-time within the Los Angeles time zone for the UNIX Epoch.
const absl::TimeZone::CivilInfo epoch_info = lax.At(absl::UnixEpoch());
// epoch_info.cs == 1969-12-31 16:00:00
// epoch_info.subsecond == absl::ZeroDuration()
// epoch_info.offset == -28800
// epoch_info.is_dst == false
// epoch_info.abbr == "PST"
Converting a civil time to an absolute time requires a bit more care, as some civil times may not map to an actual or unique absolute time. For example, a daylight-saving-time (DST) transition either skips or repeats civil times; in the United States, March 13, 2011 02:15 is a civil time that never occurred, while November 6, 2011 01:15 is a civil time that occurred twice. Requests for such times are not well-defined.
The Abseil time library contains a FromCivil()
function, which can convert a
civil time of any alignment to an absolute time. This function returns an
unambiguous absolute time, but makes certain assumptions about the transition:
This implies for any two civil times, ct1
and ct2
, where ct1 < ct2
,
that FromCivil(ct1) <= FromCivil(ct2)
. (The equal case occurs when two
non-existent civil times map to the same transition time.)
absl::CivilSecond cs = ...;
absl::TimeZone tz = ...;
absl::Time t = absl::FromCivil(cs, tz);
If FromCivil()
does not return your desired answer for non-unique inputs,
TimeZone::At(CivilSecond)
returns an absl::TimeZone::TimeInfo
struct
containing several time values and more complete information about the
conversion:
TimeInfo.pre
holds the absolute time using the time zone’s pre-transition
UTC offset.TimeInfo.post
holds the absolute time using the time zone’s post-transition
UTC offset.TimeInfo.trans
holds the absolute time of the transition discontinuity
itself.TimeInfo.kind
, indicates the context of the conversion, as an enum of one
of the following values:
UNIQUE
indicates that the requested civil time did not occur during a
transition.REPEATED
indicates that the requested civil time occurs twice.
In this case, both the pre
and post
fields will convert to the
requested civil time.SKIPPED
indicates that the requested civil time did not occur at all.
In this case, neither the pre
nor post
fields will convert to the
requested civil time. Instead, the two times are computed using the
UTC offsets surrounding the transition. These are the most likely
alternatives for the non-existent civil time.Examples:
// NOTE: the examples below denote resulting absolute times (`pre`, `post`, and
// `trans` values) using the convention of a civil time + utc offset.
// Midnight on January 1 is a unique civil time in the Los Angeles time zone.
const auto jan01 = lax.At(absl::CivilSecond(2011, 1, 1, 0, 0, 0));
// jan01.kind == TimeZone::TimeInfo::UNIQUE
// jan01.pre is 2011-01-01 00:00:00 -0800
// jan01.trans is 2011-01-01 00:00:00 -0800
// jan01.post is 2011-01-01 00:00:00 -0800
// 2:15 AM on March 13, 2011 is a time which was skipped in the Los Angeles
// time zone during the DST transition when time would "spring forward."
const auto mar13 = lax.At(absl::CivilSecond(2011, 3, 13, 2, 15, 0));
// mar13.kind == TimeZone::TimeInfo::SKIPPED
// mar13.pre is 2011-03-13 03:15:00 -0700
// mar13.trans is 2011-03-13 03:00:00 -0700
// mar13.post is 2011-03-13 01:15:00 -0800
// 1:15 AM on November 6, 2011 is a time that was repeated in the Los Angeles
// time zone during the DST transition when time would "fall back."
const auto nov06 = lax.At(absl::CivilSecond(2011, 11, 6, 1, 15, 0));
// nov06.kind == TimeZone::TimeInfo::REPEATED
// nov06.pre is 2011-11-06 01:15:00 -0700
// nov06.trans is 2011-11-06 01:00:00 -0800
// nov06.post is 2011-11-06 01:15:00 -0800
absl::RFC3339_full
)absl::FormatTime
to format time strings, and absl::ParseTime
to
parse time strings.