Copyright © 2006 CrystalClear Software, Inc -- Last Modified: Date: 2006-09-11 07:15:22
Note: This draft is only partially complete and will need to be updated. However, it does show the scope and extent of the proposal.
This paper will focus on the details of the proposal to add a date-time library to the c++ standard. Please see N1900=05-0160 Proposal to Add Date-Time to the C++ Standard Library for an overview of the motivations, design, and major design decisions.
The author fully expects the Library Working Group to have many suggestions and refinements since this is a lengthy proposal and this is the authors first experience writing a standards proposal.
Text in notes is meant as explanatory information about the proposal. It is not to be added to the TR.
This is an example of a note that is NOT part of the TR 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.
Unless otherwise specified, all components described in this clause are declared
in namespace std::tr2::datetime.
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 maxiumum resolution (eg: 1 second).
Time Period: A length of time between two time points
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 will normally be called the 'minimum date' or 'minimum time'.
Maximum Date/Time: The maxiumum representable time in a time system implementation. For time_t the maximum representable time is January 19, 2038 03:14:08.
Valid Date/Time: A date or time that can be represented within the range of a time system with a defined epoch, resolution, and maximum date/time.
The header <datetime> defines a basic date, basic duration, and basic period class templates.
The header <datetime> defines a basic gregorian calendar templates that implements core calendric calcuations.
The header <datetime> defines a series of typedefs to provide date and other time types.
The header <datetime> defines a timezone classes to provide for local time adjustment calculations input and output.
The header <datetime> defines a input output functions and facets for the date time types.
The header <datetime> defines a set of exception classes used to signal errors in construction of incorrect dates and times.
namespace std { namespace tr2 { namespace datetime { enum special_values {negative_infinity, positive_infinity, not_a_date_time, max_date_time, min_date_time, not_special }; enum week_of_month {1st_week = 1, 2nd_week, 3rd_week, 4th_week, 5th_week, last_week_of_month}; //corresponds to iso 8601 day of the week numbering enum day_of_the_week {sunday=1, monday, tuesday, wednesday, thursday, friday, saturday } struct year_month_day; //basic class for time durations template<class duration_rep_traits> class basic_duration; //common calendar functions. template<typename ymd_type_, typename date_int_type_> class basic_calendar; //classes and functions for date template<class date_impl> class basic_date; //Exception classes for construction of dates class bad_date_error; // public std::runtime_error class bad_day_of_month; // public bad_date_error class bad_day_of_year; // public bad_date_error class bad_week_of_year // public bad_date_error class bad_year; // public bad_date_error class bad_month; // public bad_date_error class bad_weekday; // public bad_date_error class bad_date_conversion; //public bad_date_error //high resolution durations template<class time_impl> class basic_time_duration; //Classes and functions for time representation template <class time_impl> class basic_date_time; template <class CharT, class TraitsT> std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const basic_date_time&); template <class CharT, class Traits> std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& is, basic_date_time&); //Classes and functions for time periods template<class point_rep, class duration_rep> class basic_time_period; template<typename time_type, typename CharT = char> class time_zone_base; template<class CharT = char> class time_zone_names_base; //Timezone type used for user customization (TODO????) class time_zone : public time_zone_base<date_time>; //Exception class for conversions of local time values class invalid_time_label; // public std::logic_error template<typename time_type, typename charT=char> class posix_time_zone : public time_zone<time_type, charT>; template<class date_type> class day_clock; template<class time_type> class second_clock; template<class time_type> class microsecond_clock; //...indicates implementation defined typedef basic_date<implementation-defined> date; typedef basic_duration<implementation-defined> days; typedef basic_duration<implementation-defined> weeks; typedef basic_duration<implementation-defined> months; typedef basic_duration<implementation-defined> years; typedef basic_time_period<date, days> date_period; typedef basic_date_time<implementation-defined> date_time; typedef basic_time_duration<implementation-defined> hours; typedef basic_time_duration<implementation-defined> minutes; typedef basic_time_duration<implementation-defined> seconds; typedef basic_time_duration<implementation-defined> milliseconds; typedef basic_time_duration<implementation-defined> microseconds; typedef basic_time_duration<implementation-defined> nanoseconds; //io stuff template <class CharT, class TraitsT> std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, basic_duration&); template <class CharT, class Traits> std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& is, basic_duration&); template <class CharT, class TraitsT> std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const basic_date&); template <class CharT, class Traits> std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& is, basic_date&); template <class CharT, class TraitsT> std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const basic_time_duration&); template <class CharT, class Traits> std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& is, basic_time_duration&); template <class CharT, class TraitsT> std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const basic_time_period&); template <class CharT, class Traits> std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& is, basic_time_period&); template <class CharT, class TraitsT> std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, posix_time_zone&); template <class CharT, class Traits> std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& is, posix_time_zone&); template <class date_type, class CharT, class OutItrT = std::ostreambuf_iterator<CharT, std::char_traits<CharT> > > class date_facet; // : public std::locale::facet template <class date_type, class CharT, class InItrT = std::istreambuf_iterator<CharT, std::char_traits<CharT> > > class date_input_facet; // public std::locale::facet { template <class time_type, class CharT, class OutItrT = std::ostreambuf_iterator<CharT, std::char_traits<CharT> > > class time_facet; template <class time_type, class CharT, class InItrT = std::istreambuf_iterator<CharT, std::char_traits<CharT> > > class time_input_facet; } // namespace datetime } //namespace tr2 } //namespace std
This is a parameterized implementation of a proleptic Gregorian, iso, and julian calendar functions. These functions allow for the calculation of various date-related values including the conversion of values such as the 'year-month-day' into an integer value that can be used to represent a date.
All the methods of this class are static functions, so the intent is to never create instances of this class.
This class is parameterized to allow for alternate date time implemenations to utilize these functions as part of an implementation.
template<typename ymd_type_, typename date_int_type_> class basic_calendar { public: typdef typename ymd_type_::weekday_type weekday_type; //TODO change to traits? typedef ymd_type_ ymd_type; typedef typename ymd_type::month_type month_type; typedef typename ymd_type::day_type day_type; typedef typename ymd_type::year_type year_type; typedef date_int_type_ date_int_type; //iso/gregorian related functions static unsigned short day_of_week (const ymd_type& ymd); static int week_number (const ymd_type& ymd); static bool is_leap_year(year_type y); static unsigned short last_day_of_month(year_type y, month_type m); //TODO document or remove static int days_after_weekday(date_int_type day_number, weekday_type); static int days_before_weekday(date_int_type day_number, weekday_type); static date_int_type day_number (const ymd_type& ymd); static ymd_type from_day_number(date_int_type); //julian calendar functions static date_int_type julian_day_number(const ymd_type& ymd); static long modjulian_day_number(const ymd_type& ymd); static ymd_type from_julian_day_number(long julian_day_number); static ymd_type from_modjulian_day_number(long modjulian_day); //calendar traits functions static ymd_type min_ymd(); static ymd_type max_ymd(); static date_int_type min_count(); static date_int_type max_count(); static unsigned short days_in_week(); };
Remarks:
Returns:
The ymd_type parameter is a simple structure that has the following requirements.
| Expression | Notes |
|---|---|
| ymd_type::year_type | A typedef that provides the integer type used for year field of the ymd_type. |
| ymd_type::month_type | A typedef that provides the integer type used for month field of the ymd_type. |
| ymd_type::day_type | A typedef that provides the integer type used for month field of the ymd_type. |
| ymd_type.year | An integer type that represents the current year. TODO--ragne |
| ymd_type.month | An integer type the represents a month. Values will be assigned with January == 1 thru December ==12 |
| ymd_type.day | An integer type that represents a day within a month. Values will range from 1 to 31. |
| ymd_type.day_of_year | An integer type that represents the day of the year. Values will range from 1 to 366 |
static unsigned short day_of_week (const ymd_type& ymd);
Returns: Day of the week from the specified year-month-day where Sunday is 0, Monday is 1, thru Saturday == 6.
static int week_number (const ymd_type& ymd);
Remarks: Calculate the iso week number for the year-month-day as defined in ISO 8601:2004 Data elements and interchange formats -- Information interchange -- Representation of dates and times
Returns: An integer value from 0 to 53 corresponding to the week number for the year,month,day.
static bool is_leap_year(year_type y);
Returns: True if the specified year is a leap year, false otherwise.
static unsigned short last_day_of_month(year_type y, month_type m);
Remarks: Calculate the last day of the month taking into account leap years. This is a constant for all months except February which returns 28 for a non leap year and 29 for a leap year. For example, the value for January is always 31.
Returns: An integer value between 1 and 31 that represents the last day of the month.
static date_int_type day_number (const ymd_type& ymd);
Remarks: Calculate an integer day count based on the proleptic Gregorian/ISO calendar that corresponds to the date. The number must monotinically increase starting from min_count to max_count with one value for each day.
This function provides the infrastructure to implement the days_between dates externally from the calendar class.
Returns: An integer cooresponding to the specified year-month-day. The return value from this function will map to the same passed year-month-day as calculated by from_day_number.
static ymd_type from_day_number(date_int_type day_number);
Remarks: Converts an integer back to a year-month-day.
Returns: The year-month-day value cooresponding to the specified day_number.
The date_int_type provides
an integer type that is used in all calculations of the date.
The intent of the date_int_type
is to allow implementations to use different integer sizes within the implementation.
For example, some implementations my use 64 bit integer in representing a
combined date time and will find it helpful to directly calculate into this
specific integer type even though a 32 bit integer would be sufficient to
represent the valid date range. In addition, this enables low level user
access to these functions using user defined integer types.
The following functions provide conversions to and from julian an modified julian day numbers.
A freely available resource with descriptions of the algorithms to be implemented here can be found at http://www.tondering.dk/claus/calendar.html
static date_int_type julian_day_number(const ymd_type& ymd);
Remarks: Calculate the julian day number (TODO reference and outside).
Returns: An integer representing the julian day number for the specified year-month-day.
static long modjulian_day_number(const ymd_type& ymd);
Remarks: Calculate the modified julian day number (TODO reference and outside).
Returns: Day of the week from the specified year-month-day
where Sunday=0, Monday1, thru Saturday=6.
static ymd_type from_julian_day_number(long julian_day);
Remarks:
Returns: A year-month-day structure with day, month, year fields set.
static ymd_type from_modjulian_day_number(long mod_julian_day);
Remarks: A year-month-day structure from the modified julian day.
Returns: Day of the week from the specified year-month-day
where Sunday = 0, Monday = 1, thru Saturday == 6.
The following functions provide basic information about the minimum and maximum dates handled by the implementation.
static ymd_type min_ymd();
Remarks: Get the year-month-day of the minimum date provided by the calendar implementation.
Returns: An instance of the ymd_type with the year, month, and day fields set.
For this proposal this will nominally be [1400-01-01
static ymd_type max_ymd();
Remarks: Get the year-month-day of the maximum date provided by the calendar implementation.
Returns: An instance of the ymd_type with the year, month, and day fields set.
For this proposal this will nominally be [10000-01-01
static date_int_type min_count();
Returns: Returns the minimum day number value used by the calendar.
This will typically be 0, but implementations may use 0 for special values internally.
static date_int_type max_count();
Returns: Returns the maximum day number value used by the calendar.
static unsigned short days_in_week();
Remarks: Enables generic programming for non-iso calendar systems.
Returns: Returns 7.
The basic_date template provide the core interface for the date
typedef that will be the primary interface for date programming. Internally,
the basic_date will hold an implementation defined integer type. This allows
the basic_date interface to provide with with different calendar implementations
that provide different valid date ranges the typedef date.
template<class date_impl> class basic_date { public: //typedefs typedef typename date_impl::calendar_type calendar_type; typedef typename date_impl::year_type year_type; typedef typename date_impl::month_type month_type; typedef typename date_impl::day_type day_type; typedef typename date_impl::ymd_type ymd_type; typedef typename date_impl::day_of_week_type day_of_week_type; typedef typename date_impl::day_of_year_type day_of_year_type; typedef typename date_impl::week_of_year_type week_of_year_type; typedef typename date_impl::weekday_type weekday_type; typedef typename date_impl::week_of_month_type week_of_month_type; typedef typename date_impl::special_values_type special_values_type; typedef typename date_impl::date_duration_type date_duration_type; typedef typename date_impl::date_int_type date_int_type; //constructors basic_date(year_type y, month_type m, day_type d); basic_date(const ymd_type& ymd); basic_date(year_type y, day_of_year_type doy); basic_date(const basic_date& rhs); //constructs something like: Sunday in Week 50 of year 2004 basic_date(year_type y, week_of_year_type week_number, weekday_type wd); //constructs something like: 3rd Monday in Feb of 2004 basic_date(year_type y, month_type m, week_of_month_type week_number, weekday_type wd); basic_date(std::time_t t); basic_date(const std::tm& datetm); //construct positive/negative infinity max or min date basic_date(special_values_type sv); //default constuctor == not-a-date-time basic_date(); //Basic accessors year_type year() const; month_type month() const; day_type day() const; day_of_week_type day_of_week() const; //eg: Sun, Mon, ... //additional accessors day_of_year_type day_of_year() const; //1..365 or 1..366 (for leap year) week_of_year_type week_number() const; //ISO 8601 week number 1..53 date_int_type day_number() const; //Return the day number since start of the epoch basic_date end_of_month() const; //Return the last day of the current month //special value accessors bool is_special() const; special_values_type as_special() const; //conversion accessors time_t to_time_t() const; ymd_type year_month_day() const; //Get ymd structure // Conversion to julian calendar date_int_type julian_day() const; long modjulian_day() const; //calculation accessors date_duration_type days_until (weekday_type) const; date_duration_type days_before (weekday_type) const; basic_date next (weekday_type) const; basic_date previous (weekday_type) const; //comparison operators bool operator< (const basic_date&) const; bool operator<= (const basic_date&) const; bool operator> (const basic_date&) const; bool operator>= (const basic_date&) const; bool operator== (const basic_date&) const; bool operator!= (const basic_date&) const; //arithmetic operators date_duration_type operator- (const basic_date&) const; basic_date operator- (const date_duration_type&) const; basic_date operator-= (const date_duration_type&); basic_date operator+ (const date_duration_type&) const; basic_date operator+= (const date_duration_type&); }; template <class CharT, class TraitsT> std::basic_ostream<CharT, TraitsT>& operator<<(std::basic_ostream<CharT, TraitsT>& os, const date&); template <class CharT, class Traits> std::basic_istream<CharT, Traits>& operator>>(std::basic_istream<CharT, Traits>& is, date&); //Exception classes for construction of dates class bad_day_of_month : public std::out_of_range {}; class bad_day_of_year : public std::out_of_range {}; class bad_week_of_year : public std::out_of_range {}; class bad_year : public std::out_of_range {}; class bad_month : public std::out_of_range {}; class bad_weekday : public std::out_of_range {}; //Exception class for conversions of local time values class invalid_time_label : public std::logic_error {};
The date type provides may types of constructors for creating a date type.
basic_date();
Remarks: Construct date with the special value not-a-date-time.
Throws: none
basic_date(const basic_date& rhs);
Remarks: Copy constructor.
Throws: none
basic_date(year_type year, month_type month, day_type day);
Remarks: Construct a month from integer year, month, day values.
Throws: bad_year
if year > calendar::max_ymd().year
Throws: bad_month
if month < 0 or month > 12
Throws: bad_day
if day is greater than calendar::last_day_of_month(year,month)
This last case requires the exception be thrown in the case of a non leap-year and a Feb 29th specification.
basic_date(const ymd_type& ymd);
Remarks: Construct a month from integer year, month, day values in the ymd struct.
Throws: bad_year
if ymd.year > calendar::max_ymd().year
Throws: bad_month
if ymd.month < 0 or month > 12
Throws: bad_day
if ymd.day is greater than calendar::last_day_of_month(year,month)
basic_date(year_type year, day_of_year_type day_of_year);
Remarks: Construct a date based on the year and the day of year value.
Throws: bad_year
if ymd.year > calendar::max_ymd().year
Throws: bad_day_of_year
day_of_year > 366 or (day_of_year = 366 and and year !
leap year)
In the 8601 standard the day_of_year is called the 'ordinal number'. In leap years, dates after Feb 29 have a different day number than in common years. As a result the day_of_year for March 1 is 61 in a leap year and 60 in common years.
basic_date(year_type y, week_of_year_type week_number, weekday_type wd);
Remarks: Construct a date using a specification like: Sunday in Week 50 of year 2004.
Throws: bad_year
if ymd.year > calendar::max_ymd().year
Throws: bad_weekday
Throws: bad_week_of_year
basic_date(year_type y, month_type m, week_of_month_type week_number, weekday_type wd);
Remarks: Constructs something like: 3rd Monday in Feb of 2004
Throws: bad_year
if ymd.year > calendar::max_ymd().year
Throws: bad_weekday
Throws: bad_week_of_year
basic_date(std::time_t t);
Remarks: Constructs date base on offset from time_t epoch of 1970-01-01.
Throws: None.
basic_date(const std::tm& datetm);
Remarks: Constructs date from the fields specified in the tm structure. In particular uses the fields tm_mday, tm_mon, tm_year. Values of tm_year are treated as years since 1900. Negative values of tm_year are interpreted as a negative offset from the year 1900.
Most implementations of date input/output in current standard libraries accept and correctly handle tm_year with negative values. For example, tm_year == -1 logically corresponds to 1899. Recently one implementation made a recent change by that prohibited negative tm_year values. The author has not been able to find anything in the standard or otherwise that specifies how negative values of tm_year should be handled. This may need to change to support portable implementations going forward.
Throws: bad_year
if datetm.tm_year+1900 > calendar::max_ymd().year or datetm.tm_year+1900
< calendar::min_ymd().year
Throws: bad_month
if datetm.tm_month < 0 or datetm.tm_month > 11
Throws: bad_day
if datetm.tm_day is greater than calendar::last_day_of_month(year,month) or
datetm.tm_day < 1
basic_date(special_values_type sv);
Remarks: Constructs date to one of the supported special values.
Throws: None.
year_type year() const;
Returns: Returns year part of the date. Undefined if date is a special_value.
Throws: None.
month_type month() const;
Returns: Returns month part of the date. Undefined if date is a special_value.
Throws: None.
day_type day() const;
Returns: Returns month part of the date. Undefined if date is a special_value.
Throws: None.
day_of_week_type day_of_week() const;
Returns: Returns the iso day of the week represented by the date. Undefined if date is a special value.
Throws: None.
day_of_year_type day_of_year() const; //1..365 or 1..366 (for leap year)
Returns: Returns the iso day of the year (1..366) respresented by the date. Undefined if date is a special value.
Throws: None.
week_of_year_type week_number() const;
Returns: Returns the iso week number (1..53) of the day respresented by the date. Undefined if date is a special value.
Throws: None.
date_int_type day_number() const;
Returns: Returns the count of days from calendar::ymd_min() to date represented by date. Undefined if date is a special value.
Throws: None.
basic_date end_of_month() const;
Returns: Returns the last day in the month of the date as defined by calendar::last_day_of_month(year, month). Undefined if date is a special value.
Throws: None.
bool is_special() const;
Returns: True of the date is set to one of the special values, false otherwise.
Throws: None.
special_values_type as_special() const;
Returns: Returns one of the special values
Throws: None.
time_t to_time_t() const;
Returns: Conversion accessor that returns time_t value represented by the date.
Throws: bad_date_conversion if date value is outside the valid date range represented by time_t or date is a special value.
ymd_type year_month_day() const;
Returns: Returns the date broken into the year, month, and day structure.
date_int_type julian_day() const;
Returns: Conversion accessor that returns julian day value represented by the date.
Throws: bad_date_conversion if date value is outside the valid date range represented by julian_day or date is a special value.
date_int_type modjulian_day() const;
Returns: Conversion accessor that returns time_t value represented by the date.
Throws: bad_date_conversion if date value is outside the valid date range represented by modjulian_day or date is a special value.
The comparison operators will take into account the following rules for handling of special values. All representable dates between the minimum and maximum representable date are described as 'any valid date' below. In general, positive_infinity is considered to be greater than any valid date as well as not_a_date_time. negative_infinity is considered to be less than any valid date as well as not_a_date_time. Since max_date_time and min_date_time special values represent valid dates these special values utilize normal comparison rules.
| description | result | Notes |
|---|---|---|
| negative_infinity < negative_infinity | false | |
| negative_infinity == negative_infinity | true | |
| negative_infinity < not_a_date_time | true | Also true for less-equal |
| negative_infinity < any valid date | true | |
| not_a_date_time == not_a_date_time | true | |
| not_a_date_time < not_a_date_time | false | Same is true for less-equal, greater-equal and greater |
| not_a_date_time == any valid date | false | Same is true for less-equal, greater-equal and greater |
| positive_infinity > any valid date | true | Also true for greater-equal |
| positive_infinity > negative_infinity | true | Also true for greater-equal, less, less-equal |
| positive_infinity > negative_infinity | true | Also true for greater-equal false for less and less-equal |
bool operator< (const basic_date& rhs) const;
Returns: True if date represented by *this < rhs - false otherwise.
bool operator<= (const basic_date&) const;
Returns: True if date represented by *this <= rhs - false otherwise.
bool operator> (const basic_date&) const;
Returns: True if date represented by *this > rhs - false otherwise.
bool operator>= (const basic_date&) const;
Returns: True if date represented by *this >= rhs - false otherwise.
bool operator== (const basic_date&) const;
Returns: True if date represented by *this >= rhs - false otherwise.
bool operator!= (const basic_date&) const;
The following arithmetic operators perform simple subtraction except when the value of the date is a special value. The table defines the rules for special value calculations where 'infinity' represents either positive_infinity or negative_infinity.
| Rule --> Result Type | Notes |
|---|---|
| Timepoint(not_a_date_time) + Duration --> Timepoint(not_a_date_time) | |
| Timepoint(infinity) + Duration --> Timepoint(infinity) | |
| Timepoint + Duration(infinity) --> Timepoint(infinity) | |
| Timepoint - Duration(infinity) --> Timepoint(-infinity) | |
| Timepoint(+infinity) + Duration(-infinity) --> not_a_date_time | |
| Duration(+infinity) + Duration(-infinity) --> not_a_date_time | |
| Duration(infinity) * Zero --> not_a_date_time | |
| Duration(infinity) * Integer(Not Zero) --> Duration(infinity) | |
| Duration(+infinity) * -Integer --> Duration(-infinity) | |
| Duration(infinity) / Integer --> Duration(infinity) |
days operator- (const basic_date&) const;
Returns: Number of days between this and rhs date value, or special value results.
basic_date operator- (const days&) const;
Returns: Date with the specified number of days subtracted or a special value result.
basic_date operator-= (const days&);
Effects: Set the date the the number of days subtracted or a special value result.
Returns: Date with the specified number of days subtracted or a special value result.
basic_date operator+ (const days&) const;
Returns: Date with the specified number of days added or a special value result.
basic_date operator+= (const days&);
Effects: Set the date the the number of days added or a special value result.
Returns: Date with the specified number of days added or a special value result.
The following sections detail classes the represent various errors provided by the library to prevent incorrect construction of invalid date and time values.
The datetime_error is a base exception for all datetime error reporting. Derived classes provide additional detailed error information.
class datetime_error : public std::runtime_error {}
This class derives from std::runtime_error under the presumption that most of the errors involving date construction are a result of bad data input from a user or other data source that can only be read at runtime.
class bad_day_of_month : public bad_date_error { public: bad_day_of_month(int month); int bad_day() const; }; bad_day_of_month(int<