N2328=07-0188 - Proposal for Date-Time Types in C++0x To Support Threading APIs

Jeff Garland (jeff-at-crystalclearsoftware.com)


Table of Contents

Introduction
Overview of New Types and Functions
Design Decisions
Issues
Proposed Text
Header <date_time> Synopsis
class utc_time
template class hiresolution_clock
Common Duration Functions
class nanoseconds
class microseconds
class milliseconds
class seconds
class minutes
class hours
Acknowledgements
References

Introduction

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
}

Overview of New Types and Functions

This proposal introduces 3 kinds of types into the standard. These are

  • time point
  • time duration(s)
  • clock

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.

Type Overview

descriptiontypeNotes
utc_timetime pointPoint in time representing a nanosecond resolution time. Epoch for UTC time is same as time_t 1970-01-01 00:00:00.000000000
hiresolution_clockclockClock that can produce the current utc_time.
hourstime durationDuration type that represents a count of hours.
minutestime durationDuration type that represents a count of minutes.
secondstime durationDuration type that represents a count of seconds.
millisecondstime durationDuration type that represents a count of milliseconds.
microsecondstime durationDuration type that represents a count of microseconds.
nanosecondstime durationDuration 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'.

Calculation Results and Typing

Rule --> Result TypeNotes
Timepoint + Duration --> TimepointValid only for timepoints of equal or greater resolution than the Duration.
Timepoint - Duration --> TimepointValid only for timepoints of equal or greater resolution than the Duration.
Timepoint - Timepoint --> DurationTimepoints of the same resolution only.
Duration + Duration --> DurationIn mixed resolution durations, higher resolution duration must the result.
Duration - Duration --> DurationIn mixed resolution durations, higher resolution duration must the result.
Duration * Integer --> DurationResults in a duration.
Integer * Duration --> DurationResults in a duration.
Duration / Integer --> DurationInteger Division rules.
Duration + Timepoint --> TimepointValid only for timepoints of equal or greater resolution than the Duration.
Duration - Timepoint --> UndefinedCompilation error.
Timepoint + Timepoint --> UndefinedCompilation 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.

Design Decisions

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.

Issues

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.

Proposed Text

Text in notes is meant as explanatory information about the proposal. It is not to be added to the standard.

Note

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.

Chapter (tbs) - Datetime Library

This clause contains components that C++ programs may use to manipulate dates, times, and timezones.

FW.2 Definitions

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'.

Header <date_time> Synopsis

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

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 hiresolution_clock

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.

Note

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

Common Duration Functions

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

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

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

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

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

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

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

Acknowledgements

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.

References