Date Time IO SystemAs of version 1.33, the date_time library utilizes a new IO streaming system. This new system gives the user great control over how dates and times can be represented. The customization options can be broken down into two groups: format flags and string elements. Format flags provide flexibility in the order of the date elements as well as the type. Customizing the string elements allows the replacement of built in strings from month names, weekday names, and other strings used in the IO. The output system is based on a date_facet (derived from std::facet), while the input system is based on a date_input_facet (also derived from std::facet). The time and local_time facets are derived from these base types. The output system utilizes three formatter objects, whereas the input system uses four parser objects. These formatter and parser objetcs are also customizable. It is important to note, that while all the examples shown here use narrow streams, there are wide stream facets available as well (see IO Objects for a complete list). It should be further noted that not all compilers are capable of using this IO system. For those compilers the IO system used in previous Exception Handling on StreamsWhen an error occurs during the input streaming process, the // "Turning on" exceptions date d(not_a_date_time); std::stringstream ss; ss.exceptions(std::ios_base::failbit); ss.str("204-Jan-01"); ss >> d; // throws bad_year exception AND sets failbit on stream A simple example of this new system://example to customize output to be "LongWeekday LongMonthname day, year" // "%A %b %d, %Y" date d(2005,Jun,25); date_facet* facet(new date_facet("%A %B %d, %Y")); std::cout.imbue(std::locale(std::cout.getloc(), facet)); std::cout << d << std::endl; // "Saturday June 25, 2005" Many of the format flags this new system uses for output are those used by The following tables list the all the flags available for both date_time IO as well as strftime. Format flags marked with a single asterisk (*) have a behavior unique to date_time. Those flags marked with an exclamation point (!) are not usable for input (at this time). The first table is for dates, and the second table is for times.
* Signifies flags that have a behavior unique to ! Signifies flags that currently do not work for input. The following table lists the available facets. IO Objects
* These links lead to the Formatter/Parser ObjectsTo implement the new i/o facets the date-time library uses a number of new parsers and formatters. These classes are available for users that want to implement specialized input/output routines.
Introduction -
Construction -
Accessors
IntroductionThe Construction
Accessors
Introduction -
Construction -
Accessors
IntroductionThe Construction
Accessors
Introduction -
Construction -
Accessors
IntroductionThe Construction
AccessorsThe time_facet inherits all the public date_facet methods. Therefore, the date_facet methods are not listed here. Instead, they can be found by following this link.
Introduction -
Construction -
Accessors
IntroductionThe Construction
AccessorsThe time_input_facet inherits all the public date_input_facet methods. Therefore, the date_input_facet methods are not listed here. Instead, they can be found by following this link.
Date Time Formatter/Parser ObjectsPeriods | Date Generators | Special Values | Format Date ParserPeriodsThe period_formatter and period_parser provide a uniform interface for the input and output of date_periods, time_periods, and in a future release, local_date_time_periods. The user has control over the delimiters, formats of the date/time components, and the form the period takes. The format of the date/time components is controlled via the date_time input and output facets. Period FormPeriods are constructed with open ranged parameters. The first value is the starting point, and is included in the period. The end value is not included but immediately follows the last value: [begin/end). However, a period can be streamed as either an open range or a closed range. [2003-Jan-01/2003-Dec-31] <-- period holding 365 days [2003-Jan-01/2004-Jan-01) <-- period holding 365 days DelimitersThere are four delimiters. The default values are
A user can provide a custom set of delimiters. Custom delimiters may contain spaces. CustomizationThe period form and delimiters can be set as construction parameters or by means of accessor functions. A custom period parser/formatter can then be used as a construction parameter to a new facet, or can be set in an existing facet via an accessor function. Period Formatter/Parser ReferenceThe complete class reference can be found here:Period Formatter Doxygen Reference and here: Period Parser Doxygen Reference
Period Formatter Construction
Period Formatter Accessors
Period Parser Construction
Period Parser Accessors
_____________________________________________________ Date GeneratorsThe date_generator formatter and parser provide flexibility by allowing the user to use custom "phrase elements". These phrase elements are the "in-between" words in the date_generators. For example, in the date_generator "Second Monday of March", "Second" and "of" are the phrase elements, where "Monday" and "March" are the date elements. Customization of the date elements is done with the facet. The order of the date and phrase elements cannot be changed. When parsing, all elements of the date_generator phrase must parse correctly or an ios_base::failure exception will be thrown. CustomizationThe default "phrase_strings" are:
A custom set of phrase_strings must maintain this order of occurance (Ex: "1st", "2nd", "3rd", "4th", "5th", "last", "prior", "past", "in"). Examples using default phrase_strings and default facet formats for weekday & month: "first Tue of Mar" And using custom phrase_strings: "1st Tue in Mar"
The custom set of phrase elements can be set as construction parameters or through an accessor function.A custom date_generator parser/formatter can then be used as a construction parameter to a new facet, or can be set in an existing facet via an accessor function. IMPORTANT NOTE: Prior to 1.33, partial_date was output as "1 Jan" with a single *or* double digit number for the day. The new behavior is to *always* place a double digit number for the day - "01 Jan". Date Generator ReferenceThe complete class references can be found here:Date Generator Formatter Doxygen Reference and here: Date Generator Parser Doxygen Reference
Date Generator Formatter Construction
Date Generator Formatter Accessors
Date Generator Parser Construction
Date Generator Parser Accessors
_____________________________________________________ Special ValuesThe date_time library uses five special_values. They are:
The default set of strings used to represent these types are: "not-a-date-time", "-infinity", "+infinity", "minimum-date-time", "maximum-date-time". When output, the min_date-time and max_date_time appear as normal date/time representations: "1400-Jan-01" and "9999-Dec-31" repectively. CustomizationThe special values parser/formatter allows the user to set custom strings for these special values. These strings can be set as construction parameters to a new facet, or can be set in an existing facet via an accessor function. Special Values Formatter/Parser ReferenceThe complete class references can be found here:Special Values Formatter Doxygen Reference and here: Special Values Parser Doxygen Reference
Special Values Formatter Constructor
Special Values Formatter Accessors
Special Values Parser Constructor
Special Values Parser Accessors
_____________________________________________________ Format Date ParserThe format date parser is the object that holds the strings for months and weekday names, as well as their abbreviations. Custom sets of strings can be set at construction time, or, the strings in an existing format_date_parser can be replaced through accessor functions. Both the constructor and the accessor functions take a vector of strings as their arguments. Format Date Parser ReferenceThe complete class reference can be found here:Doxygen Reference
Format Date Parser Constructor
Format Date Parser Accessors
Date Time IO TutorialBasic Use | Format Strings | Content Strings | Special Values | Date/Time Periods | Date GeneratorsBasic UseFacets are automatically imbued when operators '>>' and '<<' are called. The list of date_time objects that can be streamed are: Gregorian
Posix_time
Local_time
The following example is of the basic use of the new IO code, utilizing all the defaults. (this example can be found in the date d(2004, Feb, 29); time_duration td(12,34,56,789); stringstream ss; ss << d << ' ' << td; ptime pt(not_a_date_time); cout << pt << endl; // "not-a-date-time" ss >> pt; cout << pt << endl; // "2004-Feb-29 12:34:56.000789" ss.str(""); ss << pt << " EDT-05EDT,M4.1.0,M10.5.0"; local_date_time ldt(not_a_date_time); ss >> ldt; cout << ldt << endl; // "2004-Feb-29 12:34:56.000789 EDT" This example used the default settings for the input and output facets. The default formats are such that interoperability like that shown in the example is possible. NOTE: Input streaming of local_date_time can only be done with a posix time zone string. The default output format uses a time zone abbreviation. The format can be changed so out and in match (as we will see later in this tutorial). Format StringsThe format strings control the order, type, and style of the date/time elements used. The facets provide some predefined formats (iso_format_specifier, iso_format_extended_specifier, and default_date_format) but the user can easily create their own. (continued from previous example)local_time_facet* output_facet = new local_time_facet(); local_time_input_facet* input_facet = new local_time_input_facet(); ss.imbue(locale(locale::classic(), output_facet)); ss.imbue(locale(ss.getloc(), input_facet)); output_facet->format("%a %b %d, %H:%M %z"); ss.str(""); ss << ldt; cout << ss.str() << endl; // "Sun Feb 29, 12:34 EDT" output_facet->format(local_time_facet::iso_time_format_specifier); ss.str(""); ss << ldt; cout << ss.str() << endl; // "20040229T123456.000789-0500" output_facet->format(local_time_facet::iso_time_format_extended_specifier); ss.str(""); ss << ldt; cout << ss.str() << endl; // "2004-02-29 12:34:56.000789-05:00" Format strings are not limited to date/time elements. Extra verbiage can be placed in a format string. NOTE: When extra verbiage is present in an input format, the data being input must also contain the exact verbiage. (continued from previous example)// extra words in format string my_format("The extended ordinal time %Y-%jT%H:%M can also be \ represented as %A %B %d, %Y"); output_facet->format(my_format.c_str()); input_facet->format(my_format.c_str()); ss.str(""); ss << ldt; cout << ss.str() << endl; // matching extra words in input ss.str("The extended ordinal time 2005-128T12:15 can also be \ represented as Sunday May 08, 2005"); ss >> ldt; cout << ldt << endl; Content StringsSo far we've shown how a user can achieve a great deal of customization with very little effort by using formats. Further customization can be achieved through user defined elements (ie strings). The elements that can be customized are: Special value names, month names, month abbreviations, weekday names, weekday abbreviations, delimiters of the date/time periods, and the phrase elements of the date_generators. The default values for these are as follows: Special values
Months
Weekdays
Date generator phrase elements
NOTE: We've shown earlier that the components of a date/time representation can be re-ordered via the format string. This is not the case with date_generators. The elements themselves can be customized but their order cannot be changed. Content StringsTo illustrate the customization possibilities we will use custom strings for months and weekdays (we will only use long names, is all lowercase, for this example). (continued from previous example)// set up the collections of custom strings. // only the full names are altered for the sake of brevity string month_names[12] = { "january", "february", "march", "april", "may", "june", "july", "august", "september", "october", "november", "december" }; vector<string> long_months(&month_names[0], &month_names[12]); string day_names[7] = { "sunday", "monday", "tuesday", "wednesday", "thursday", "friday", "saturday" }; vector<string> long_days(&day_names[0], &day_names[7]); // create date_facet and date_input_facet using all defaults date_facet* date_output = new date_facet(); date_input_facet* date_input = new date_input_facet(); ss.imbue(locale(ss.getloc(), date_output)); ss.imbue(locale(ss.getloc(), date_input)); // replace names in the output facet date_output->long_month_names(long_months); date_output->long_weekday_names(long_days); // replace names in the input facet date_input->long_month_names(long_months); date_input->long_weekday_names(long_days); // customize month, weekday and date formats date_output->format("%Y-%B-%d"); date_input->format("%Y-%B-%d"); date_output->month_format("%B"); // full name date_input->month_format("%B"); // full name date_output->weekday_format("%A"); // full name date_input->weekday_format("%A"); // full name ss.str(""); ss << greg_month(3); cout << ss.str() << endl; // "march" ss.str(""); ss << greg_weekday(3); cout << ss.str() << endl; // "tuesday" ss.str(""); ss << date(2005,Jul,4); cout << ss.str() << endl; // "2005-july-04" Special ValuesCustomizing the input and output of special values is best done by creating a new special_values_parser and special_values_formatter. The new strings can be set at construction time (as in the example below). (continued from previous example)// reset the formats to defaults output_facet->format(local_time_facet::default_time_format); input_facet->format(local_time_input_facet::default_time_input_format); // create custom special_values parser and formatter objects // and add them to the facets string sv[5] = {"nadt","neg_inf", "pos_inf", "min_dt", "max_dt" }; vector<string> sv_names(&sv[0], &sv[5]); special_values_parser sv_parser(sv_names.begin(), sv_names.end()); special_values_formatter sv_formatter(sv_names.begin(), sv_names.end()); output_facet->special_values_formatter(sv_formatter); input_facet->special_values_parser(sv_parser); ss.str(""); ldt = local_date_time(not_a_date_time); ss << ldt; cout << ss.str() << endl; // "nadt" ss.str("min_dt"); ss >> ldt; ss.str(""); ss << ldt; cout << ss.str() << endl; // "1400-Jan-01 00:00:00 UTC" NOTE: even though we sent in strings for min and max to the formatter, they are ignored because those special values construct to actual dates (as shown above). Date/Time PeriodsCustomizing the input and output of periods is best done by creating a new period_parser and period_formatter. The new strings can be set at construction time (as in the example below). (continued from previous example)// all formats set back to defaults (not shown for brevity) // create our date_period date_period dp(date(2005,Mar,1), days(31)); // month of march // custom period formatter and parser period_formatter per_formatter(period_formatter::AS_OPEN_RANGE, " to ", "from ", " exclusive", " inclusive" ); period_parser per_parser(period_parser::AS_OPEN_RANGE, " to ", "from ", " exclusive" , "inclusive" ); // default output ss.str(""); ss << dp; cout << ss.str() << endl; // "[2005-Mar-01/2005-Mar-31]" // add out custom parser and formatter to the facets date_output->period_formatter(per_formatter); date_input->period_parser(per_parser); // custom output ss.str(""); ss << dp; cout << ss.str() << endl; // "from 2005-Feb-01 to 2005-Apr-01 exclusive" Date GeneratorsCustomizing the input and output of date_generators is done by replacing the existing strings (in the facet) with new strings. NOTE: We've shown earlier that the components of a date/time representation can be re-ordered via the format string. This is not the case with date_generators. The elements themselves can be customized but their order cannot be changed. (continued from previous example)// custom date_generator phrases string dg_phrases[9] = { "1st", "2nd", "3rd", "4th", "5th", "final", "prior to", "following", "in" }; vector<string> phrases(&dg_phrases[0], &dg_phrases[9]); // create our date_generator first_day_of_the_week_before d_gen(Monday); // default output ss.str(""); ss << d_gen; cout << ss.str() << endl; // "Mon before" // add our custom strings to the date facets date_output->date_gen_phrase_strings(phrases); date_input->date_gen_element_strings(phrases); // custom output ss.str(""); ss << d_gen; cout << ss.str() << endl; // "Mon prior to" |