Copyright © 2007 CrystalClear Software, Inc -- Date: 2007-06-23
Table of Contents
This paper proposes a set of date-time types to support sophisticated temporal interfaces for threading in the next C++ standard. The intent is to replace xtime types in proposal N2285 and its successors (N2320) while giving a smooth evolution path to a full date-time library in TR2 as outlined in N1900 and N2058. Most of the elements of this proposal are fully implemented as part of the Boost Date-Time Library. A draft implementation of the proposal is available at n2328_impl.tar.gz.
Overall, the goal is to support code like this:
std::this_thread::sleep(std::seconds(1)); Lock l; std::condition::timed_wait(l, std::microseconds(100)); std::recursive_timed_mutex rtm; rtm.timed_lock(std::milliseconds(20)); std::utc_time now = std::hiresolution_clock::universal_time(); now += std::nanoseconds(500); std::unique_lock<std::mutex> lk(mut); // Wait for 2 seconds on a condition variable std::utc_time time_out = std::hiresolution_clock::universal_time() + std::seconds(2); { bool timed_out = !cv.timed_wait(lk, time_out); if (timed_out) // deal with time out }
This proposal introduces 3 kinds of types into the standard. These are
A time point represents an instant in the time continuum (dimensionless). A time duration is a length of time unattached to a any time point. Time durations are signed. A clock type is an interface to a device that can return a time point. These three types work together to provide a high level programming interface for C++ developers.
Time durations and time points operate much like built-in integers except that their purpose is to provide calculations in time. To behave like built-int types they provide all of the usual valuetype concepts including: EqualityComparable, LessThanComparable, CopyConstructable, DefaultConstructable, and Assignable. Duration types are behave like signed integers while time points are more like unsiged integers.
Table 2 summarizes the types introduced in the proposal.
description | type | Notes |
---|---|---|
utc_time | time point | Point in time representing a nanosecond resolution time. Epoch for UTC time is same as time_t 1970-01-01 00:00:00.000000000 |
hiresolution_clock | clock | Clock that can produce the current utc_time. |
hours | time duration | Duration type that represents a count of hours. |
minutes | time duration | Duration type that represents a count of minutes. |
seconds | time duration | Duration type that represents a count of seconds. |
milliseconds | time duration | Duration type that represents a count of milliseconds. |
microseconds | time duration | Duration type that represents a count of microseconds. |
nanoseconds | time duration | Duration type that represents a count of nanoseconds. |
utc_time is a simple time_point type that can be used to represent a current time much like time_t but with nanosecond resolution. It represents a time in UTC (Coordinated Universal Time). UTC is a widely used standard based on the solar time at the Prime Meridian (formerly known as Greenwich Mean Time-GMT). The utc_time class works in conjunction with the hiresolution_clock to provide the current time. The various duration types provide for specification and calculations with utc_time.
These temporal types form the foundation for enabling sophisticated calculations using dates and times and well defined. For example, no matter the resolution of time points and durations we can say that the following calculations apply where --> means 'results in'. Note that date-time calculations should be free from 'floating point round-off'.
Rule --> Result Type | Notes |
---|---|
Timepoint + Duration --> Timepoint | Valid only for timepoints of equal or greater resolution than the Duration. |
Timepoint - Duration --> Timepoint | Valid only for timepoints of equal or greater resolution than the Duration. |
Timepoint - Timepoint --> Duration | Timepoints of the same resolution only. |
Duration + Duration --> Duration | In mixed resolution durations, higher resolution duration must the result. |
Duration - Duration --> Duration | In mixed resolution durations, higher resolution duration must the result. |
Duration * Integer --> Duration | Results in a duration. |
Integer * Duration --> Duration | Results in a duration. |
Duration / Integer --> Duration | Integer Division rules. |
Duration + Timepoint --> Timepoint | Valid only for timepoints of equal or greater resolution than the Duration. |
Duration - Timepoint --> Undefined | Compilation error. |
Timepoint + Timepoint --> Undefined | Compilation error. |
As an example, we can see that these rules support code like this:
//duration based calculations std::nanoseconds ns = std::nanoseconds(3) + std::microseconds(3) - std::seconds(3); ns += std::nanoseconds(3); ns = -ns; std::utc_time now = std::hiresolution_clock::universal_time() + std::seconds(1); now += std::nanoseconds(500);
Conversion between duration types is either automatic or results in a compilation error if the conversion would result in a loss of resolution. This is illustrated below:
std::hours h(1); h += std::minutes(10); //compile error -- loss of resolution std::minutes m; m += std::hours(1); //ok, hours can be converted to minutes if (std::minutes(60) > std::hours(1)) //ok, returns false
To support the needed resolution conversions all time points and durations provide resolution trait information. For time durations less than one second the traits provide information to allow conversion to seconds. For time durations greater than one second the traits provide the number of seconds in a duration.
This proposal provides some support for conversion to and from C time types. In particular utc_time can be constructed from a time_t and can provide a time_t value by calling seconds_since_epoch. This seemingly minimal interface provides enough capability for interaction with C and other non-native programs for time handling.
This proposal provides just enough C compatibility to allow users to convert to and from C types -- primarily time_t. The introduction of additional C interfaces or a broader interface raises many hard to resolve issues with the current C APIs and distracts from the intent of this proposal which is to provide support for a rich time interfaces to support multi-threading.
The utc_time class is a very minimal time_point type type as compared to the primary timepoint (datetime) specified for the TR2 proposal. This is intended to keep this proposal for C++0x as small as possible and focused on support for threading.
The proposal provides no input-output functions whereas the TR2 proposal has an extensive input-output specification. This is a simplification to keep the proposal minimal. Before TR2 becomes available, users can convert the types in this proposal to fundamental types to perform input-output.
This proposal, like the TR2 proposal, is careful to separate the representation of a point in time from the clock types that measure a point in time. This decision has several significant advantages as outlined in N1900.
Although utc_time is specified with a resolution of nanoseconds most platforms do not provide hardware and operating system support this resolution. Typical modern platforms can support clock resolutions of microseconds. This proposal does not mandate a particular clock resolution requirement.
Since the tr2 proposal will likely use a different namespace for time types (either tr2 or tr2::datetime), this may create some confusion since the duration types proposed here will already be in namespace std.
It's unclear if Datetime should become it's own chapter or just extend section 20.7 which is the current date and time section under General utilities library.
Text in notes is meant as explanatory information about the proposal. It is not to be added to the standard.
This is an example of a note that is NOT part of the standard text.
All dates and times in this proposal are supplied in ISO extended form unless
otherwise specified. Specifically year-month-day hour:minute:second.fractional_seconds.
So the 15th day of September 2006 is specified as 2006-09-15
.
This clause contains components that C++ programs may use to manipulate dates, times, and timezones.
Time Point: An instant in the time continuum (dimensionless).
Time Duration: A length of time unattached to a any time point. Time durations have an assigned maximum resolution (eg: 1 second).
Epoch: The start of a given time scale. For time_t the epoch is 1970-01-01 00:00:00. In this text the epoch may be called a 'minimum date' or 'minimum time'.
namespace std { //duration types class hours; class minutes; class seconds; class milliseconds; class microseconds; class nanoseconds; //timepoint class utc_time; template<class time_type> class hiresolution_clock;
class utc_time { public: utc_time(); //epoch utc_time(time_t, nanoseconds ns); ~utc_time(); time_t seconds_since_epoch() const; nanoseconds nanoseconds_since_epoch() const; //traits typedef 'implementation defined' tick_type; static tick_type ticks_per_second(); static tick_type seconds_per_tick(); static bool is_subsecond(); //comparison functions bool operator==(const utc_time& rhs) const; bool operator!=(const utc_time& rhs) const; bool operator>(const utc_time& rhs) const; bool operator>=(const utc_time& rhs) const; bool operator<(const utc_time& rhs) const; bool operator<=(const utc_time& rhs) const; //arithmetic functions nanoseconds operator-(const utc_time& rhs) const template<typename time_duration_type> utc_time operator+(const time_duration_type& td) const; template<typename time_duration_type> utc_time& operator+=(const time_duration_type& td); template<typename time_duration_type> utc_time operator-(const time_duration_type& td) const; template<typename time_duration_type> utc_time& operator-=(const time_duration_type& td) };
The class utc_time provides a timepoint that represents the current UTC time. utc_time must provide a minimum range of 1970-01-01 00:00:00.000000000 + 292 years. This represents the number of nanoseconds that can be represented in a signed 64 bit integer.
utc_time(); //epoch
Effects: Construct a utc_time representing the epoch time point 1970-01-01 00:00:00.000000000
Throws: Nothing
utc_time(time_t, nanoseconds);
Effects: Construct a utc time where time_t represents seconds since 1970-01-01 00:00:00.000000000 and nanaoseconds provides an additional nanosecond offset
Remarks: If the total nanoseconds > 1 second the seconds are incremented appropriately
Throws: Nothing
~utc_time();
Effects: Destroys the time point.
Throws: Nothing
time_t seconds_since_epoch() const;
Returns: Returns the count of seconds since 1970-01-01 00:00:00.
Throws: Nothing
nanoseconds nanoseconds_since_epoch() const;
Returns: Returns the count of seconds since 1970-01-01 00:00:00.
Throws: Nothing
static tick_type ticks_per_second();
Returns: Returns 1000000000
Throws: Nothing
static tick_type seconds_per_tick();
Returns: Returns 0
Remarks: Since this is a subsecond type it returns 0 for seconds_per_tick
Throws: Nothing
static bool is_subsecond();
Returns: true.
Throws: Nothing
bool operator==(const utc_time& rhs) const;
Returns: Returns true if rhs is the same time.
Throws: Nothing
bool operator!=(const utc_time& rhs) const;
Returns: Returns true if rhs is not the same time.
Throws: Nothing
bool operator>(const utc_time& rhs) const;
Returns: Returns true if time is greater than rhs time.
Throws: Nothing
bool operator>=(const utc_time& rhs) const;
Returns: Returns true if time is greater or equal than rhs time.
Throws: Nothing
bool operator<(const utc_time& rhs) const;
Returns: Returns true if time is less than rhs time.
Throws: Nothing
bool operator<=(const utc_time& rhs) const;
Returns: Returns true if time is less than rhs time.
Throws: Nothing
nanoseconds operator-(const utc_time& rhs) const
Returns: Returns the difference between two times in a nanosecond count.
Remarks: If rhs is greater the result will be a negative nanosecond count.
Throws: Nothing
template<typename time_duration_type> utc_time operator+(const time_duration_type& td) const;
Returns: Returns the duration converted to nanosecond resolution and added to the time.
Throws: Nothing
template<typename time_duration_type> utc_time& operator+=(const time_duration_type& td);
Effects: Convert the duration to nanosecond resolution add to nanoseconds to the time.
Returns: Modified value of this.
Throws: Nothing
template<typename time_duration_type> utc_time operator-(const time_duration_type& td) const;
Returns: Returns the duration converted to nanosecond resolution and subtracted from the time.
Throws: Nothing
template<typename time_duration_type> utc_time& operator-=(const time_duration_type& td)
Effects: Convert the duration to nanosecond resolution subtract from the time and return *this.
Returns: Modified value of this.
Throws: Nothing
template<class time_type> class hiresolution_clock { public: static time_type universal_time(); };
The hiresolution_clock provides access to the operating system clock at a resolution up to nanoseconds. The actual resolution will vary from platform to platform.
static time_type universal_time();
Returns: Current UTC time - equvalent of time_t with fractional sections.
Remarks: Function is thread-safe on platforms supporting threading. Successive calls to this function will produce and equal or greater time value in all cases.
Typical modern platforms personal computer platforms achieve microsecond level resolution from calls to the clock. The Boost Date-Time Library has a class that portably implements the proposed interface, but it uses different C-level interfaces depending on the operating system.
Throws: Nothing
The following functions are common functions to all durations types. These functions provide the basis for durations to be EqualityComparable, LessThanComparable as well as the usual aritmetic operations. In the following text duration_type refers to the containing duration type.
class duration_type { //where duration_type== nanoseconds, microseconds, etc //comparison operators template<typename rhs_duration_type> bool operator< (const rhs_duration_type&) const; template<typename rhs_duration_type> bool operator<= (const rhs_duration_type&) const; template<typename rhs_duration_type> bool operator> (const rhs_duration_type&) const; template<typename rhs_duration_type> bool operator>= (const rhs_duration_type&) const; template<typename rhs_duration_type> bool operator== (const rhs_duration_type&) const; template<typename rhs_duration_type> bool operator!= (const rhs_duration_type&) const; //sign inversion duration_type operator-() const //arithmetic operations template<typename rhs_duration_type> duration_type operator- (const rhs_duration_type& d) const template<typename rhs_duration_type> duration_type operator-=(const rhs_duration_type& d) template<typename rhs_duration_type> duration_type operator+ (const rhs_duration_type& d) const template<typename rhs_duration_type> duration_type operator+=(const rhs_duration_type& d) duration_type operator/ (int divisor) const duration_type operator/=(int divisor) duration_type operator* (int rhs) const duration_type operator*=(int divisor) tick_type get_count() const
The following details each of these functions.
template<typename rhs_duration_type> bool operator==(const rhs_duration_type& rhs) const;
Returns: Returns true if rhs duration is greater.
Throws: Nothing
template<typename rhs_duration_type> bool operator!=(const rhs_duration_type& rhs) const;
Returns: Returns true if rhs is not the same time.
Throws: Nothing
template<typename rhs_duration_type> bool operator>(const rhs_duration_type& rhs) const;
Returns: Returns true if the rhs duration is larger.
Throws: Nothing
template<typename rhs_duration_type> bool operator>=(const rhs_duration_type& rhs) const;
Returns: Returns true if greater or equal than the rhs duration.
Throws: Nothing
template<typename rhs_duration_type> bool operator<(const rhs_duration_type& rhs) const;
Returns: Returns true if less than the rhs duration.
Throws: Nothing
template<typename rhs_duration_type> bool operator<=(const rhs_duration_type& rhs) const;
Returns: Returns true if less or equal to the rhs duration.
Throws: Nothing
//sign inversion duration_type operator-() const
Returns: Negated value of the duration.
Throws: Nothing
//arithmetic operations template<typename rhs_duration_type> duration_type operator- (const rhs_duration_type& d) const
Returns: A duration value equal to this-rhs_duration.
Remarks: This will fail to compiler if the rhs_duration_type is of higher resolution.
Throws: Nothing
template<typename rhs_duration_type> duration_type operator-=(const rhs_duration_type& d)
Effects: Modifies to value equal to this-rhs_duration.
Returns: this
Remarks: This will fail to compiler if the rhs_duration_type is of higher resolution.
Throws: Nothing
template<typename rhs_duration_type> duration_type operator+ (const rhs_duration_type& d) const
Returns: Duration equal to this+rhs_duration.
Remarks: This will fail to compiler if the rhs_duration_type is of higher resolution.
Throws: Nothing
template<typename rhs_duration_type> duration_type operator+=(const rhs_duration_type& d)
Effects: Modifies to value equal to this+rhs_duration.
Returns: this
Remarks: This will fail to compiler if the rhs_duration_type is of higher resolution.
Throws: Nothing
duration_type operator/ (int divisor) const
Returns: Duration with value equal to this/divisor according to integer arithmetic rules.
Throws: Nothing
duration_type operator/=(int divisor)
Effects: Change value of this by this/divisor according to integer arithmetic rules.
Returns: this
Throws: Nothing
duration_type operator* (int rhs) const
Returns: Duration with value equal to this*rhs
Throws: Nothing
duration_type operator*=(int rhs)
Effects: Modifies to value equal to this*rhs.
Returns: this
Throws: Nothing
tick_type get_count() const
Returns: Returns the count at the resolution of the time duration type.
Throws: Nothing
class nanoseconds { public: nanoseconds(long long=0); nanoseconds(const nanoseconds& rhs); ~nanoseconds(); //traits information static tick_type ticks_per_second(); static tick_type seconds_per_tick(); static bool is_subsecond(); typedef 'implementation-defined' tick_type; //+ common functions ;
Class nanoseconds represents a count of nanoseconds.
nanoseconds(long long=0);
Effects: Construct a count of nanoseconds - default is zero.
Throws: Nothing
nanoseconds(const nanoseconds& rhs);
Effects: Copy construction.
Throws: Nothing
~nanoseconds();
Effects: Destruct count.
Throws: Nothing
static tick_type ticks_per_second();
Returns: 1000000000
Throws: Nothing
static tick_type seconds_per_tick();
Returns: 0
Throws: Nothing
static bool is_subsecond();
Returns: true
Throws: Nothing
class microseconds { public: microseconds(long long=0); microseconds(const microseconds& rhs); ~microseconds(); //conversions operator nanoseconds() const //traits information static tick_type ticks_per_second(); static tick_type seconds_per_tick(); static bool is_subsecond(); typedef 'implementation-defined' tick_type; ;
Class microseconds represents a count of microseconds.
microseconds(long long=0);
Effects: Construct a count of microseconds - default is zero.
Throws: Nothing
microseconds(const microseconds& rhs);
Effects: Copy construction.
Throws: Nothing
~microseconds();
Effects: Destruct count.
Throws: Nothing
//conversions operator nanoseconds() const
Returns: microsecond count converted to nanoseconds
Throws: Nothing
static tick_type ticks_per_second();
Returns: 1000000
Throws: Nothing
static tick_type seconds_per_tick();
Returns: 0
Throws: Nothing
static bool is_subsecond();
Returns: true
Throws: Nothing
class milliseconds { public: milliseconds(int_type=0); milliseconds(const milliseconds& rhs); ~milliseconds(); //conversions operator nanoseconds() const; operator microseconds() const; //traits information static tick_type ticks_per_second(); static tick_type seconds_per_tick(); static bool is_subsecond(); typedef 'implementation-defined' tick_type; };
Class milliseconds represents a count of milliseconds.
milliseconds(long long=0);
Effects: Construct a count of milliseconds - default is zero.
Throws: Nothing
milliseconds(const milliseconds& rhs);
Effects: Copy construction.
Throws: Nothing
~milliseconds();
Effects: Destruct count.
Throws: Nothing
operator nanoseconds() const
Returns: millisecond count converted to nanoseconds
Throws: Nothing
operator microseconds() const
Returns: millisecond count converted to microseconds
Throws: Nothing
static tick_type ticks_per_second();
Returns: 1000
Throws: Nothing
static tick_type seconds_per_tick();
Returns: 0
Throws: Nothing
static bool is_subsecond();
Returns: true
Throws: Nothing
class seconds { public: seconds(int_type s=0); seconds(const seconds& rhs); ~seconds(); //conversions operator nanoseconds() const operator microseconds() const operator milliseconds() const //traits information static tick_type ticks_per_second(); static tick_type seconds_per_tick(); static bool is_subsecond(); typedef 'implementation-defined' tick_type; };
Class seconds represents a count of seconds.
seconds(long long=0);
Effects: Construct a count of seconds - default is zero.
Throws: Nothing
seconds(const seconds& rhs);
Effects: Copy construction.
Throws: Nothing
~seconds();
Effects: Destruct count.
Throws: Nothing
operator nanoseconds() const
Returns: second count converted to nanoseconds
Throws: Nothing
operator microseconds() const
Returns: second count converted to microseconds
Throws: Nothing
operator milliseconds() const
Returns: second count converted to milliseconds
Throws: Nothing
static tick_type ticks_per_second();
Returns: 1
Throws: Nothing
static tick_type seconds_per_tick();
Returns: 1
Throws: Nothing
static bool is_subsecond();
Returns: false
Throws: Nothing
class minutes { public: minutes(int_type s=0); minutes(const minutes& rhs); ~minutes(); //conversions operator nanoseconds() const operator microseconds() const operator milliseconds() const operator seconds() const //traits information static tick_type ticks_per_second(); static tick_type seconds_per_tick(); static bool is_subsecond(); typedef 'implementation-defined' tick_type; };
Class minutes represents a count of minutes.
minutes(long long=0);
Effects: Construct a count of minutes - default is zero.
Throws: Nothing
minutes(const minutes& rhs);
Effects: Copy construction.
Throws: Nothing
~minutes();
Effects: Destruct count.
Throws: Nothing
operator nanoseconds() const
Returns: minute count converted to nanoseconds
Throws: Nothing
operator microseconds() const
Returns: minute count converted to microseconds
Throws: Nothing
operator milliseconds() const
Returns: minute count converted to milliseconds
Throws: Nothing
operator seconds() const
Returns: minute count converted to seconds
Throws: Nothing
static tick_type ticks_per_second();
Returns: 0
Throws: Nothing
static tick_type seconds_per_tick();
Returns: 60
Throws: Nothing
static bool is_subsecond();
Returns: false
Throws: Nothing
class hours { public: hours(int_type s=0); hours(const hours& rhs); ~hours(); //conversions operator nanoseconds() const operator microseconds() const operator milliseconds() const operator seconds() const operator minutes() const //traits information static tick_type ticks_per_second(); static tick_type seconds_per_tick(); static bool is_subsecond(); typedef 'implementation-defined' tick_type; };
Class hours represents a count of hours.
hours(long long=0);
Effects: Construct a count of hours - default is zero.
Throws: Nothing
hours(const hours& rhs);
Effects: Copy construction.
Throws: Nothing
~hours();
Effects: Destruct count.
Throws: Nothing
operator nanoseconds() const
Returns: hour count converted to nanoseconds
Throws: Nothing
operator microseconds() const
Returns: hour count converted to microseconds
Throws: Nothing
operator milliseconds() const
Returns: hour count converted to milliseconds
Throws: Nothing
operator seconds() const
Returns: hour count converted to seconds
Throws: Nothing
operator minutes() const
Returns: hour count converted to seconds
Throws: Nothing
static tick_type ticks_per_second();
Returns: 0
Throws: Nothing
static tick_type seconds_per_tick();
Returns: 3600
Throws: Nothing
static bool is_subsecond();
Returns: false
Throws: Nothing
First thanks goes to the Boost Community for all the constructive suggestions for evolving Boost Date-Time Library into a great C++ date-time library. Thanks to Howard Hinnant for taking and interest and helping mold this into a reasonable proposal. Special thanks goes to my family for allowing me to work on this.