Copyright © 2005 CrystalClear Software, Inc -- Last Modified: $Date: 2005/09/28 13:40:25 $
The representation of dates and times is a recurring problem for software developers. Almost all non-trivial programs have the need to represent dates or times for one or more purposes. This proposal describes additions and changes to the C++ standard library to facilitate programming with dates and times. Most elements of the proposal are currently implemented in the Boost Date-Time Library.
The primary audience for this proposal is C++ application and library developers. The proposal provides a foundational library to simplify application development that needs to manipulate dates and times. In addition, by providing efficient value types, like date, the library provides an excellent foundation for building higher level interfaces. For example, a socket library that provides a timeout value can use the milliseconds type to provide a cleaner interface than is possible with a primitive type.
Due to the pervasive need for date-time programming, many libraries have been written to support date-time programming. Unfortunately, the date-time domain appears simple and trivial, but is deceptively complex. Thus, many libraries fail to address a broad range of applications by failing to solve some of the difficult date-time problems, or, providing inflexible and inefficient implementations. For example, very few libraries support flexible localized input-output capabilities. The lack of more complete standard library support means the re-invention of various date-time capabilities and a lack of portable capabilities to support C++ programmers. The Boost Date-Time Library and this proposal are both attempts to remedy this condition by building on other standards (such as ISO 8601 and Posix 1003.1) and building a best of breed solution to support a broad range of applications.
A wide variety of modern software applications need to manipulate dates and times. Like numeric values, the span of application types that use dates and times include business, scientific, communications, and many others. The following are some fairly typical uses of dates and times in applications:
Consider an Estimate class that records an estimate for services rendered. To design this class at least two temporal values need to be recorded:
One core function of the Estimate class is to determine if the estimate is still valid. The following is a sketch of how this logic might be implemented using the Boost Date-Time Library:
//Example usage of a date temporal types
class Estimate {
//...
bool is_valid() const
{
date_period valid_period(date_of_estimate, valid_days);
date today = day_clock::local_time(); //read the computer clock
return valid_period.contains(today);
}
date last_valid_day() const
{
return (date_of_estimate + valid_days);
}
private:
std::string estimate_id;
date date_of_estimate;
days valid_days; //number of days estimate is valid
//...
};
In this small example, date, days, and date_period demonstrate the three core temporal types: time points, durations, and time periods. These are described in more detail in the concepts section.
Overall, the main capabilities needed for a standard date-time library include:
Just as programmers are provided with int and double for development of programs involving basic mathematics, temporal types provide concise representation and calculation with dates and times. Clearly defined temporal types enhance coding practice by facilitating the creation of more precise functional interfaces. As with other value types, programmers expect temporal types to support basic value concepts such as assignability, comparability, and streamability.
The temporal type concepts are described in Points, Durations, and Periods. The list of temporal types provided in the proposal can be found in Summary of Temporal Types.
Like int and double the temporal types provide the framework for time-based arithmetic. The following are some examples:
date d(2004,Jan,1);
d += year(1);
d += months(3);
d += days(10);
date_time t(d, hours(5));
t+= minutes(3) - seconds(2);
milliseconds ms_count = hours(3) + milliseconds(100);
The design of these features is described in more detail in Arithmetic Operations on Temporal Types.
Computer applications frequently need to determine the current time or date. To determine the time, applications read a hardware device that provides a representation of the current time. Usually, this representation is a counter that represents a duration offset from a well defined epoch time. For example, std::gmtime is a clock interface that retrieves the number of seconds since 1970-Jan-1 00:00:00 (the epoch) from the local computer clock. The gmtime call provides the time based on the standard Universal Coordinated Time (UTC). Note that many of the clock APIs also embed the concept of local time adjustment. This typically depends on the time zone settings of the computer.
Reading a clock results in the construction of a time point. For example:
//construct UTC time based
date_time t1 = clock::universal_time();
//construct localized time based on time zone setting of computer
date d = day_clock::local_time();
date_time t2 = second_clock::local_time();
date_time t3 = microsecond_clock::local_time();
//construct localized time based on time zone specification
posix_time_zone tz("EST-05EDT,M4.1.0,M10.5.0");
date_time t4 = second_clock::local_time(tz);
People have invented a seemingly infinite set of combinations for representing dates and times, making good input output support quite challenging. The ISO 8601 standard provides a specification for formatting of dates and times. However, most applications have requirements that extend beyond ISO 8601 including the need for localized and custom formats. Some of these requirements include the need to support customized strings for elements of a time representation such as the month name. To support this variety of representations requires a relatively sophisticated input/output capability. The Boost Date-Time Library provides a set of facets that use format strings and interoperate with the temporal types. Based on the current C++ formatting facets and integrated with standard streams they provide for customization of all aspects of input and output. For example:
date d(2005,Jun,25);
cout << pt << endl; // "2005-Jun-25"
//example to customize output to be "LongWeekday LongMonthname day, year"
// "%A %b %d, %Y"
date_facet* facet(new date_facet("%A %B %d, %Y"));
cout.imbue(locale(cout.getloc(), facet));
cout << d << endl;
// "Saturday June 25, 2005"
stringstream ss;
ss.str("Saturday June 25, 2005");
date_input_facet* input_facet(new date_input_facet("%A %B %d, %Y");
ss.imbue(locale(ss.getloc(), input_facet));
date d2; //not_a_date_time
ss >> d2;
The section Extensions to time_put Format Flags describes the additional formatting flags provided in this proposal. The new flags support concepts like fractional seconds formatting which are needed to support new concepts provided by this proposal. The classes date_facet, time_facet, date_input_facet, and time_input_facet provide support for localization and customization of input/output.
Local time adjustment is an example of particularly difficult domain logic that is simplified by the proposed library. The rules associated with these adjustments are needed in many applications and overall the library support is quite poor. Consider, for example, an application that calculates arrival times for an airline. For the end user, it is important to see the arrival time of the flight in the arriving time zone not the departing time zone. Thus the application needs to manage the logic associated with time zone transitions and daylight savings time transitions. Other than that, the core application comes down to adding the length of the flight to the starting time of the flight.
One core element of the local time adjustment is the representation of the time zone. A time zone is quite complex to specify completely since it needs to include everything from output strings to rules that define the start and end of daylight savings time. The most broadly used solution to representing time zones is provided by the POSIX 1003.1 standard for representation of timezones. The posix_time_zone class provides direct support for this standard.
Here's a snippet of the core of this application as written using the proposed library extension including posix_time_zone:
//Red-eye flight from Arizona to New York that transits over
//a daylight savings time (DST) transition boundary. Az
//doesn't ever shift to DST while New York does. Thus at
//2:00 (in the middle of the flight the eastern US shifts
//it's clocks forward an hour. Luckily the application
//developer doesn't need to know these details.
//setup the timezones
posix_time_zone nyc_tz("EST-05EDT,M4.1.0,M10.5.0");
posix_time_zone phx_tz("MST-07:00:00");
//construct the departure time in phx local time
date_time departure_time_phx(date(2004, Oct, 30), hours(23));
//convert to utc time
date_time departure_time_utc = departure_time_phx.to_utc(phx_tz);
//calculate the arrival time in utc
minutes flight_length = hours(4) + minutes(30);
date_time arrival_time_utc = departure_time_utc + flight_length;
//now adjust the arrival time which is in the Phoenix timezone to NY
date_time arrival_time_nyc = arrival_time.to_local(nyc_tz);
Some other significant capabilities that simplify programming with dates and times include:
The following sections describe these capabilities in more detail.
Calendrical algorithms, or date generators, are tools for generating other dates or schedules of dates. These generator algorithms allow the representation of concepts such as "The first Sunday in February". They are useful in performing tasks such as calculating holidays. In this proposal these algorithms are incorporated into the interface of the date class. For example, the following constructors incorporate these algorithms:
//Construct the date for the Last Sunday in January
date d1(2004, Jan, Last_Week, Sunday);
//Construct a date for the Sunday in the 50th week of the year
date d2(2004, 50, Sunday);
Some additional algorithms included in the date class allow for calendar navigation and calculations. For example:
date d2 = d1.next_weekday(Tuesday);
date d3 = d1.previous_weekday(Friday);
days day_count1 = d1.days_until_weekday(Thursday);
days day_count2 = d1.days_before_weekday(Monday);
Compatibility with the current standard library is an important requirement for any new addition to the library. In this proposal there are several aspect of compatibility including:
time_t and tmSince the temporal types in this proposal are value types, they can be used as values in standard library collection classes. In addition, since the temporal types support comparison operations they can be used as a key in a map or as an element of a set.
Input and output using the standard library streaming and facet capabilities is another key aspect of standard library compatibility.
The proposal integrates with time_t and tm by allowing dates and times to construct from these types.
Some of the most difficult issues with programming dates and times is selecting an appropriate epoch, resolution, and size of internal representation. The choices are classic time and space tradeoffs. For this reason, providing a framework for users to provide customization of these elements greatly expands the utility of the library.
This is discussed in more detail in Underlying representations for dates and times.
Special values provide the ability to represent 'logical values' with the various temporal types. For example, the ability to represent not-a-date-time or infinities is helpful in many circumstances. This is similar to how floating point types have values for not-a-number (NAN) and infinity. Infinities are useful in applications that need to represent concepts like 'a-long-time-ago' or 'forever'.
Special values can apply to both time points and time durations and thus alter the rules of calculation. Here are some code examples using special values:
date d; //default construct to not_a_date_time
if (d.as_special() == NOT_A_DATE_TIME) { //true
//...
}
date_time inf(POSITIVE_INFINITY);
date_time t(date(2005, Jan, 1), hours(3));
if (t < inf) { //always true unless t == positive infinity
There are other date-time related functionalities that are not included in this proposal. These decisions are primarily a reflection of the author's experience of useful capabilities in the domain, while keeping the size of the proposal manageable. If a consensus of the committee believes any of these items should be incorporated, the author is willing to incorporate them.
These include:
Recurring intervals are a form of generator that are useful in scheduling and other applications. A recurring interval is typically defined by an initial interval and a recurrence duration. Conceptually this is the idea of "schedule the meeting for 9-10 every Monday starting on July 11, 2004". There are many options and variations on this theme. While these could be included the use of recurring intervals is somewhat specialized and can be built on top of the existing foundation.
The timezone database capabilities of Boost Date-Time Library are highly useful. The primary capability is to support construction of time zones from a regional specification as follows:
tz_database tz_db;
tz_db.load_from_file("date_time_zonespec.csv");
boost::shared_ptr<time_zone_base> nyc_tz =
tz_db.time_zone_from_region("America/New_York");
boost::shared_ptr<time_zone_base> phx_tz =
tz_db.time_zone_from_region("America/Phoenix");
However, this capability introduces the issue of maintaining a set of data associated with the world timezones. This data changes frequently as governments change time zone rules. The management of the data makes this feature inappropriate for standardization.
Boost Date-Time Library has a Time Point type that holds a time zone as well as the time value. While useful, this class is not included in the proposal as it adds significant complexity to the design. For example, a dependency on shared_ptr is introduced to allow for efficient management of the time zone types.
In addition, the overall benefit of this integrated class is somewhat limited. All the local time conversions and other desired operations can be performed without this class.
Iterators provide the ability to generate a set of dates or times based on some starting conditions and an ending point. Based on the foundation of the core temporal types bi-directional iterators can be provided that enable the simple generation of calendars and other time-related constructs.
//print a series of dates
day_iterator start(date(2005,Jul,7));
day_iterator end(date(2005, Jul, 10));
std::copy(start, end, std::ostream_iterator<date>(std::cout, " \n"));
//make a list of 3 dates
std::list<date> dl;
std::copy(start, end, std::back_inserter(dl));
The Boost Date-Time Library provides the following iterator types: day_iterator, week_iterator, month_iterator, year_iterator, time_iterator, and local_time_iterator.
There are two primary types of timers:
Elapsed timers are useful for measuring the duration of activities. For example:
//micro timer reads clock at microsecond resolution
micro_timer mt; //automatically starts timer
cout << mt.elapsed() << endl;
sleep(1);
//elapsed will be about a second - something like: 00:00:01.000123
cout << mt.elapsed() << endl;
sleep(1);
st.pause(); //stop timing
//...
st.resume(); //continue timing
Countdown timers provide the opposite interface from elapsed timers. Starting with a fixed time duration, they gradually subtract time until they reach zero. For example, measuring the time left in a basketball game is an application for a countdown timer.
The date time domain is rich in terminology and problems. The following is a brief introduction to the concepts reflected in this proposal.
There are 3 basic concepts that serve as the foundation for the representing times and dates:
as illustrated in the figure below.

Durations can be used to measure how long something takes. Consider the following:
The examples above illustrate how resolution plays into durations. The resolution of interest depends very much on the application domain. And a single application may make broad use of many different resolutions. Also note that durations can have negative or positive values. This is useful in many applications and fits into the calculation rules described later.
Time points describe when an event has or will occur. Consider the following:
A single time point may be described by one or more 'labels' of varying precision. For example,
2004-Jun-10 15:00:00 == 2004-Jun-10 3 pm
//UTC is Universal Time Coordinated - EDT is Eastern Daylight Time
2004-Jun-10 15:00:00 UTC == 2004-Jun-10 19:00:00 EDT
Note that these labels belie the complexities of what time they actually represent. All these examples assume we are using a gregorian calendar (discussed more later). In addition, the first example puts aside the issue of local time adjustments by leaving the time zone unspecified.
Time periods express a range of time. Consider the following:
These examples of time periods illustrate that a time period is simply a starting time and an ending time or a starting time and a time duration. Periods are essential to the simplification of logic associated with planning and scheduling systems. To accomplish this, periods can be shifted, intersected, merged, and combined in various ways. In addition, they can be tested for adjacency, containment, and relative location.
These same concepts are discussed and reflected in "Patterns for things that change with time" by Martin Fowler. More recently, these same concepts have also been recognized in the ISO 8601 standard for representations of dates and times. Unlike dimensionless numbers, these different concepts serve different roles in application development. My personal background has included the development of a planning and scheduling system used to schedule thousands of daily events for a major satellite phone system. This experience, as well as development of other applications, led me to many of the same concepts described by Fowler.
Each of these temporal types has a 'Resolution' which is defined by the smallest representable duration. Thus, a 'date' is a 'time point' with a resolution of 1 day. For representing a 'time' the resolution of the duration must increased. For example, time_t is a time-point with a resolution of 1 second. The ptime class in the Boost Date-Time Library has an adjustable resolution typically set at 1 micro-second or 1 nano-second.
The smallest duration representable within date, days, and date_period
is one day. Thus these types have a resolution of one day. Other temporal
types such as hours and milliseconds provide higher resolutions.
Calendars describe the rules for for mapping the observable solar cycles into patterns such as days, weeks, months, and years. The Gregorian system, adopted in the sixteenth century, is the most widely used calendar system today. It defines the sequence of days and months in a year, as well as adjustments (such as leap years). The proposal uses a 'proleptic Gregorian' calendar which extends the Gregorian system back in time prior to it's adoption.
The ISO Calendar, defined in the ISO 8601 standard, maps to the Gregorian calendar, but has a unique technique for calculating week numbers. This proposal provides direct support for calculation of ISO week numbers. The ISO Week Data Calendar illustrates the iso week information.
There are many other calendar systems such as the Chinese, Mayan, Islamic, and Hebrew. This proposal does not attempt to provide direct extensibility to all other calendar systems. However, the concepts reflected in the proposal can be applied to other calendar systems which differ in the details of how days are labeled and how concepts such as years and months are treated.
A Time system provides all these categories of temporal types as well as the rules for labeling and calculating with time points. These systems may include additional adjustment rules such as 'leap seconds'.
For the calculation of dates and times it is convenient to calculate using
a count of days, seconds, or some other time unit starting from a particular
point in the time continuum. The starting point for this count is called
the Epoch Time. The Epoch time for time_t is 1/1/1900 00:00:00.
Like leap years, leap seconds help keep a clock measured time in alignment with observed solar times. Leap years follow a regular pattern while leap seconds are declared as the need arises due to wobbles in the earth's rotation. The irregular nature of leap seconds makes them difficult to incorporate into computer based time systems.
More information on leap seconds is described at the US Navy web site.
Most local time systems are based on Coordinated Universal Time (UTC) but are also adjusted for earth rotation so that daylight hours are similar everywhere. In addition, some local times include Daylight Savings Time (DST) adjustments to shift the daylight hours during the summer.
UTC (Coordinated Universal Time) is a widely used standard based on the solar time at the Prime Meridian. Formerly known as Greenwich Mean Time (GMT) it is also often referred to as 'Zulu Time' by the military organizations.
UTC is adjusted for earth rotation at longitude 0 by the use of Leap Seconds.
Daylight Savings Time (called Summer Time in Europe) adjusts the clock forward during summer months so that the 'active' hours match the hours of daylight. This practice became widespread during the twentieth century. Wikipedia provides details on the practice.
For computer programs, DST presents a challenge. In addition to the complex rules that define the start and end of DST, the 'jumping of times' creates
Consider the switch to daylight savings for the eastern time zone in the United States. For 2005 this happened on April 3rd at 02:00:00 local time. During this transition, the clock ticks from 01:59:59 to 03:00:00. Thus, the time labels from 02:00:00 to 02:59:59 are said to be invalid.
//2005-Apr-03 01:00:00 EST
//2005-Apr-03 01:30:00 EST
//2005-Apr-03 03:00:00 EDT
//2005-Apr-03 03:30:00 EDT
During the switch back from daylight savings time the problem of ambiguous time labels occurs. That is, a time without an adornment for daylight savings cannot be calculated to be clearly in, or out of, daylight savings time. Thus, in the eastern United States timezone, on the switch back from daylight savings, the times between 01:00:00 and 01:59:59 repeat twice.
//2005-Oct-30 00:30:00 EDT
//2005-Oct-30 01:00:00 EDT
//2005-Oct-30 01:30:00 EDT
//2005-Oct-30 01:00:00 EST
//2005-Oct-30 01:30:00 EST
//2005-Oct-30 02:00:00 EST
A Clock Device is software component (tied to some hardware) that provides the current date or time with respect to a time system. A clock can measure the current time to a known resolution which may be higher, or lower, than a particular time representation.
Different software systems have different needs for software clocks. Many modern applications need high resolution, millisecond or higher, clock measurements. Modern computers routinely provide these higher resolution clocks. In addition, dedicated clock hardware often provides even higher level resolutions than typical computer hardware provides.
This is a pure library proposal, it does not add any new language features. It does include changes to the existing facets to support enhanced date-time input and output.
The current C/C++ standard library recognizes the need for tools to manipulate dates and times. Although useful, the types in the current standard are mostly derived from the C library and do not reflect modern practice.
Some of the key issues with the current capabilities include:
time_ttime_get and time_put facets
tm is unspecified
The time_t type provided in the C standard has a beginning epoch of 1970-Jan-1
00:00:00. For any application that needs to represent times prior to 1970 time_t is
insufficient. Because of this, simple applications like representing birthdays cannot be
written using time_t. Overall, the core function of time_t is to serve as an interface to
computer clocks. Unfortunately, with a resolution of one second it is insufficient for many
modern applications which require fractional seconds to measure event times. These applications
cannot use time_t or tm to represent times.
Another limitation with the current standard is that it does not provide elegant support for comparison and mathematical operations on time values. For example, consider the following 'typical' date manipulation code:
//only want to calculate with dates, but time_t
//doesn't support that...
time_t start_time(0);
start_time += 5*(60*60*24) + 2*(60*60*24*7);
A value object approach has some significant advantages in code clarity:
date d(1970, Jan, 1);
d += days(5) + weeks(2);
Clearly the first code example could be simplified by the use of constants and such, but the lack of a standard here means that unclear code is all to frequently the norm. Also, constants are insufficient because some time durations (months and years) are not fixed lengths of time. A month varies in length from 28 to 31 days and a year varies in length from 365 to 366 days. This issue is discussed in more detail in the section on Arithmetic Operations on Temporal Types.
The tm in the current standard structure is used largely for input-output using
time_get and time_put. Unfortunately the tm structure provided is a bit ambiguous about
the range of times supported. The field tm.year is defined to start at 1900. It is unclear
if years prior to 1900 (negative values) are supported. Many applications need to support year
representations prior to 1900 making this ambiguity an issue for writing portable output code
based on tm.
This proposal includes provisions for advanced input and output of date-time values. To support this, additional formatting flags are introduced to the existing time_put facet. In addition, methods are added to the time_get facet to provide for format-based parsing of dates and times. This is described in more detail below.
One major issue for the library is to support symmetry of input and output operations. For example, it is reasonable to expect that a date, or other temporal type, can be output to a stream and then read back in. For example:
date d(2004, Jan, 1);
std::stringstream ss;
ss << d; //calls `time_put` with format %Y-%b-%d
//ss.str() == "2004-Jan-01"
date d2; //not_a_date_time
ss >> d2; //can't implement directly with current `time_get`
The current time_get facet does not currently provide full support for format-based input.
This is a result of the time_get facet not providing the ability to set input formats
while time_put provides for sophisticated formatting via format strings. Specifically
note the signatures of get_date and put:
time_get<...
iter_type get_date(iter_type s, iter_type end,
ios_base& f, ios_base::iostate& err,
tm* t) const; //no format pattern here
time_put<...
iter_type put(iter_type s, ios_base& f,
char_type fill, const tm* tmb,
const charT* pattern, const charT* pattern_end) const;
The pattern and pattern_end parameter provide for the flexible customization of date
output. So, in essence, a new date_get is needed that supports the following signature:
time_get<...
iter_type get_date(iter_type s, iter_type end,
ios_base& f, ios_base::iostate& err,
const charT* pattern, const charT* pat_end, //<-- added parameters
tm* t) const;
Note that the library working group already has recognized that the current state of these facets makes correct implementations difficult in [http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#461 DR 461]. Adding format parameters should improve the implementability as has been demonstrated in the Boost Date-Time Library.
In addition to enhancements to support format-based input, this proposal expands the set of format flags to support additional concepts such as fractional seconds. These additional format flags are described further in Extensions to time_put Format Flags.
The current standard provides for std::localtime and std::gmtime functions to retrieve locally
adjusted and UTC times respectively. The resolution of these calls is one second. This proposal
recommends providing equivalent functions that provide microsecond resolution. Many platforms have
clocks supporting microsecond resolution. This higher resolution is important for many modern applications.
Proprietary versions of these functions exist on common platforms. Finally, the higher resolution can be
made optional if the platform does not support the higher resolution. The approach of the proposal to
clocks allows for the addition other clock device implementations.
There are so many date-time libraries for C++ and other languages it would be literally impossible to list them all here.
Some important examples include the Rogue Wave library, Recursion Software C++ Toolkit (originally ObjectSpace.foundations) library, Microsoft Foundation Classes, IBM ICU.
Looking at many of these libraries there are lots of differences. However, some important recurring themes emerge:
The following sections highlight some of the key classes and features of these libraries.
The Rogue Wave libraries have long provided support for temporal programming. The Rogue Wave libraries include several key types including:
RWDateTime - date and time to millisecond
RWZone - time zone abstraction
RWZoneSimple - time zone class
RWDate - just a date
RWTimer - second level resolution
RWClockTimer - short interval timer (using clock limits to ~30 minutes on some platforms)
One significant feature of the Rogue Wave libraries is the representation of 'special values'. Called 'Sentinels' in the Rogue Wave library, they allow for the representation of special temporal values such as 'Not A Date Time' or 'Infinity'. This is similar to the special values concepts provided in this proposal.
The Recursion Software C++ Toolkit library supports several key types including:
date - a date
time - a 24 hour time_duration to microsecond resolution
date_and_time - date plus a time
timeperiod - equivalent to time_duration concept in this proposal
stopwatch - a timer
timezone - encapsulation of daylight savings and UTC offset information
The Microsoft Foundation Classes provide two primary classes for manipulation of dates and times: CTime and CTimeSpan. CTime is a point in time with a resolution of one second. CTimeSpan is a time duration. Essentially small value types that wrap the C library, these classes create a basic C++ interface for application developers.
The IBM International Components for Unicode (ICU) libraries have several date-time related capabilities. The ICU date-time user guide provides more information on this C++ library. This library provides a class UDate to represent dates. In addition, ICU includes a TimeZone class to provide for localization of dates.
Most modern languages support a variety of types to support date-time programming.
JAVA provides a number of different types for the representation of dates and times. These include
Confusingly, the JAVA Date actually represents a time to millisecond resolution. It serves as the primary temporal type provided by the JAVA foundation libraries. Calendar is the base class for Gregorian Calendar that provides arithmetic capabilities with times. Locale specific formatting and parsing is specified by DateFormat and implemented using format flags in the implementation SimpleDateFormat. TimeZone provides an interface and SimpleTimeZone provides an implementation for performing local time adjustments.
The Python Date Time Module provides a number of different types for the representation of dates and times. These include
The timedelta class is an example of the time duration concept. datetime and date are time points that are similar to the date_time and date classes in this proposal.
The ISO 8601 standard defines an international standard for formatting of dates and times. Conceptually, ISO 8601 provides for the representation of lengths of time, points in time, and intervals of time at different resolutions. Most of the concepts of time representation in the ISO 8601 standard, with the exception of recurring intervals, are reflected in this proposal. In addition, the proposal provides support for input and output of dates and times as specified in ISO 8601.
ISO 8601 provides for two main format types: normal and extended. The normal representation represents a date or time using numeric values starting at the largest time division down. For example a date is represented as YYYYMMDD where YYYY is a four digit specification of the year, MM is a two digit specification of a month and DD is a two digit specification of a day.
Some examples of Normal representation include:
20040301 is March 1, 2004
20040301T020559,01 is March 1, 2005 02:05:59.01
The extended form includes additional punctuation and thus is a bit easier for humans to read:
2004-03-01 is March 1, 2004
2004-03-01T02:05:59,01 is March 1, 2005 02:05:59.01
One nice feature of ISO 8601 is a consistent ordering of time components from larger to smaller. For example, years before months before days in the date.
This proposal provides direct support for iso 8601 formatting. For example, the facet classes provide for ISO 8601 input and output:
date d(2005,Jun,25);
date_facet* facet(new date_facet());
facet->set_iso_format();
cout.imbue(locale(cout.getloc(), facet));
cout << d << endl; //20050625
facet->set_iso_extended_format();
cout << d << endl; //2005-06-25
While ISO 8601 standard provides an excellent foundation for one standard set of input and output approaches, there are limits. Specifically ISO 8601 does NOT provide for:
So while this proposal provide direct support for input and output in ISO formats, it provides a more sophisticated input-output capability overall.
The IEEE 1003.1 POSIX standard provides a textual specification for the representation of timezones. This is summarized as follow:
"std offset [dst [offset],start[/time],end[/time]]" (w/no spaces).
'std' specifies the abbreviating of the time zone name. '[' and ']' indicate optional fields. 'offset' is the offset from Universal Coordinated Time (UTC). 'dst' specifies the abbrev of the time zone during daylight savings time. The second offset is how many hours changed during Daylight Savings Time (DST). 'start' and 'end' are the dates when DST goes into, and out of, effect. 'offset' takes the form of:
[+|-]hh[:mm[:ss]] {h=0-23, m/s=0-59}
'time' and 'offset' take the same form. 'start' and 'end' can be one of three forms:
Mm.w.d {month=1-12, week=1-5 (5 is always last), day=0-6}
Jn {n=1-365 Feb29 is never counted}
n {n=0-365 Feb29 is counted in leap years}
The following is an example defining the timezone for the West Coast of the United States.
PST-08PDT,45/02,310/02
Breaking this down:
PST-08PDT,45/02,310/02
^^^ ^^^
| |
| When Daylight Savings Time (DST) is in effect the abbreviation is PDT (Pacific Daylight Time)
|
defines PST as the normal abbreviation
PST-08PDT,45/02,310/02
^^^ ^^^^^ ^^^^^
| | Switch back from DST on day 310 02:00:00
| |
| Switch to DST is day 45 at 02:00:00 hours
|
Non DST offset from Universal Coordinated Time (UTC) is -8 hours
The Posix Time Zone section describes the class
posix_time_zone that implements these rules.
The current C standard library, via header <time.h>, provides types and
functions that form the basis of most current time manipulation programs. This includes types such as time_t
and tm, as well as functions such as gmtime, mktime, and strftime.
The author is unaware of any formal proposals to the C standard committee for changes to date and time. However, David Tribble has a number of proposals that may be brought to the C committee at some point in the future.
Overall the proposals in these papers are complementary to this proposal. The suggested enhancements to the C library would serve as an excellent foundation for building the functionality provided in this proposal.
The Object Management Group (OMG) has defined two standards for timing services: Time Service and Enhanced Time Service. These standards serve as another example of a standard that reflects many of the core elements of this proposal. Some of the similar features of these services include:
The current proposal would provide for the basis for simple integration with these services allowing an alternative implementation of a clock from a distributed server.
The Timezone Database provides an open collection of timezone information and tools.
A variety of operating systems provide an extension that provides The work captured in the timezone database demonstrates much of the complexity associated with various time adjustments. Some of the interesting capabilities of the timezone database include:
While the Boost Date-Time Library provides a timezone capable of performing similar functions, that capability is NOT part of this proposal. In addition, historical time zones are not directly supported, although the base time zone and local time interactions allow for extended implementations to provide this capability.
The Boost Date-Time Library serves as the primary foundation for this proposal. Differences between the proposal and Boost date-time are outlined in Difference from Boost Date-Time.
This proposal divides the library interface into several levels:
The core interfaces provide a templated library core that supports flexible resolutions
and other capabilities similar to the way std::basic_string provides a core for string
types. The concrete temporal types are built from the core interfaces and realize a set
of types that serve as the primary programmatic interface. This is similar
to how std::string and std::wstring provide concrete realizations that most C++
programmers use.
The additional types provide implementation of clocks, time zones, and input
output facilities. These classes provide other capabilities not offered directly in the
temporal types. The figure below illustrates the various classes that make up the proposal.

The following classes make up the core interfaces of the library. The core interfaces are templates the separate aspects of the key library interfaces from implementation so that additional temporal type variations can be provided. Typical developers will not use these interfaces directly, but will use the temporal types based on these interfaces.
| Type Name | Description |
|---|---|
| basic_date | Core interface for dates. |
| basic_time | Core interface for combined date and time types. |
| basic_duration | Core interface for duration types. |
| basic_time_period | Core interface for period types. |
| basic_time_zone | Core interface for time zones. |
| ymd_type | Replacement for tm that includes fractional seconds and resolution information. |
| gregorian_calendar | Core implementation of gregrorian / iso calendar. |
To efficiently format dates and times it is convenient to have a structure
that represents the 'broken down' time. This is similar to tm without the
string values and with some additional fields to represent fractional seconds
and other information.
//struct to represent elements of a point in time
struct timepoint {
year_type year; //32 bit unsigned integer -- range depends on calendar
week_type month; //short integer 1-12 -- zero flags invalid
short day_of_year; //short
short day_of_week; //0-6 -- 0 == sunday
short week_number; //1-53 - 0 indicates invalid
short hours; //0-24
short minutes; //0-59
short seconds; //0-60 -- 60 is leap second
short fractional_seconds_count;
short frac_seconds_resolution; //increments of 10 only
};
For input and output this form is a useful tool.
The temporal types provide the main programmer interface to the library. The temporal types are value types that provide for efficient comparison, assignment, calculation, and other operations. In general, these types are designed to approach the efficiency of a regular integer type in terms of size, comparison and calculation efficiency. Some of the design decisions impacting these types is summarized in Temporal Types - Key Design Decisions.
| Type Name | Temporal Type | Description |
|---|---|---|
| date | point | Represent a date using gregorian calendar. |
| date_period | period | Represent a date period using date and days. |
| days | duration | Represent a count of days. |
| weeks | duration | Represent a count of weeks. |
| months | duration | Represent a count of months. |
| years | duration | Represent a count of years. |
| date_time | point | Represent a combined date and time with 1 microsecond resolution. |
| date_time_period | period | Represent a period using date_time. |
| seconds | duration | Represent a count of seconds. |
| milliseconds | duration | Represent a count of milliseconds. |
| microseconds | duration | Represent a count of microseconds. |
| nanoseconds | duration | Represent a count of nanoseconds. |
Note that user defined temporal types can be added as needed. For example, a user can use basic_date_time to create a time point with nanosecond level resolution. To accomplish this might require expanding the size of the underlying type to 96 bits or changing the epoch times.
These 3 temporal types form the foundation for enabling sophisticated calculations using dates and times. For example, no matter the resolution of time points and durations we can say that the following calculations apply where --> means 'results in'.
| Rule --> Result Type | Notes |
|---|---|
| Timepoint + Duration --> Timepoint | Valid only for durations of equal or greater resolution. |
| Timepoint - Duration --> Timepoint | Valid only for durations of equal or greater resolution. |
| Timepoint - Timepoint --> Duration | Timepoints of the same resolution only. |
| Duration + Duration --> Duration | In mixed resolution durations, higher resolution duration must be on the left. |
| Duration - Duration --> Duration | In mixed resolution durations, higher resolution duration must be on the left. |
| Duration * Integer --> Duration | |
| Integer * Duration --> Duration | |
| Duration / Integer --> Duration | Integer Division rules. |
| Duration + Timepoint --> Undefined | Compilation error. |
| Duration - Timepoint --> Undefined | Compilation error. |
| Timepoint + Timepoint --> Undefined | Compilation error. |
Note that the above typing rules are somewhat different from those used in Boost Date-Time Library. After some experimenting with various solutions to handling multi-resolution arithmetic, the proposed approach seems to offer a pragmatic approach that enables clean application coding while avoiding truncation error surprises for application developers.
The effect of the above rules can be seen more clearly with some examples:
date d(...);
d += seconds(10); //compilation error -- dates have resolution of 1 day
d += days(1); //ok.
d += months(2); //ok.
d += days(3) + weeks(2) //ok.
d += weeks(2) + days(2); //compilation error
date_time dt(...); //microsecond resolution time point
dt += seconds(100); //ok.
dt += days(2); //ok.
dt += nanoseconds(1); //compile error -- resolution insufficient
dt += microseconds(1); //ok.
In addition, to the rules above, there are additional rules associated with special value handling:
| Rule --> Result Type | Notes |
|---|---|
| Timepoint(NADT) + Duration --> Timepoint(NADT) | |
| Timepoint(∞) + Duration --> Timepoint(∞) | |
| Timepoint + Duration(∞) --> Timepoint(∞) | |
| Timepoint - Duration(∞) --> Timepoint(-∞) | |
| Timepoint(+∞) + Duration(-∞) --> NADT | |
| Duration(+∞) + Duration(-∞) --> NADT | |
| Duration(∞) * Zero --> NADT | |
| Duration(∞) * Integer(Not Zero) --> Duration(∞) | |
| Duration(+∞) * -Integer --> Duration(-∞) | |
| Duration(∞) / Integer --> Duration(∞) |
While most time durations behave exactly as would be expected by normal numeric operations, years and months exhibit odd behavior because they are not of a fixed length. That is, sometimes a year is 365 days and sometimes it is 366 (leap year).
In addition to the basic capabilities above most mathematical operations on dates and times are reversible.
That is,
date d(2004,Jan,1);
d1 = d + months(1); //d1 == Feb 1
d1 -= months(1); //d1 == d
However the following is a difficulty:
date d(2004,Jan,31);
date d1 = d + months(1); //Feb 29
date d2 = d1 - months(1); //oops Jan 29th
d1 - months(1) == d; //false.
Not all temporal types follow the same rules as integer types in calculations. For example, months and years are not directly convertible to a number of days. A month is between 28 and 31 days. A year is either 365 or in a leap year 366 days. Logic that needs to 'add months' or 'add years' needs to manage this issue.
The following W3C XML reference describes the proposed arithmetic rules in more detail XML Schema Adding Durations .
The following are some additional classes that augment the temporal types to provide input/output and local time adjustment capabilities.
| Type Name | Description |
|---|---|
| time_facet | Format based output facet for localization/customization of output. |
| time_input_facet | Format based input facet for localization/customization of input. |
The library provides several clock types for measuring the current time to different levels of resolution.
| Type Name | Description |
|---|---|
| day_clock | Measures the current time at day level resolution. |
| second_clock | Measures the current time to one second resolution. |
| microsecond_clock | Measures the current time to microsecond resolution. |
The concrete time zone classes provide the ability to perform local time adjustments.
| Type Name | Description |
|---|---|
| time_zone_base | Narrow char instantiation of basic_time_zone for use with date_time. |
| custom_time_zone | Class that provides for the ability to provide custom timezones in an application. |
| posix_time_zone | Provides implementation of Posix 1003.1 time zone specifications. |
All errors are signaled by exceptions. While this makes the library less suitable for some contexts, exceptions are a usual part of modern C++ code. Exceptions are thrown in the following circumstances:
All exceptions derive from std::out_of_range or std::logic_error and are summarized in the table below.
| Type | Method | Exception | Base Type | Description |
|---|---|---|---|---|
| date | constructor, operator>> | bad_day_of_month | std::out_of_range | Thrown if day of month is out of range (eg: Jan 32) |
| date | constructor, operator>> | bad_day_of_year | std::out_of_range | Thrown if day of year is invalid. (note this is t.b.d. since there is no constructor) |
| date | constructor, operator>> | bad_month | std::out_of_range | Thrown if the specified month is out of range (eg: month 13) |
| date | constructor, operator>> | bad_year | std::out_of_range | Thrown if the year is out of range (eg: >= 10000, or < 1400) |
The following table describes the new flags to be supported by time_put facet.
| Flag | Description |
|---|---|
| %f | Fractional seconds and separator - always used even when value is zero. |
| %F | Fractional seconds and separator - only output when non-zero. |
| %s | Seconds separator and fractional seconds |
| %T | Time in 24-hour notation %H:%M:%S (from strftime) |
| %q | ISO time zone |
| %Q | ISO extended time zone |
| %r | Time in AM/PM notation - same as '%I:%M:%S %p' (from strftime) |
| %V | ISO week number in range from 0 to 53 (from strftime) |
| %Z | Time zone name - long (eg: 'Eastern Standard Time'). |
| %ZP | Posix time zone string (eg: EST-05EDT+01,M4.1.0/02:00,M10.5.0/02:00 |
In addition to adding new formatting flags this proposal recommends changing the default formatting of dates and times. The following table