Writing and Reading Dates and Times

Problem

You need to display or read dates and times using local formatting conventions.

Solution

Use the time_t type and tm struct from , and the date and time facets provided in , to write and read dates and times (facets are described in the discussion in a moment). See Example 13-4 for a sample.

Example 13-4. Writing and reading dates

#include 
#include 
#include 
#include 
#include 

using namespace std;

void translateDate(istream& in, ostream& out) {

 // Create a date reader
 const time_get& dateReader =
 use_facet >(in.getloc( ));

 // Create a state object, which the facets will use to tell
 // us if there was a problem.
 ios_base::iostate state = 0;

 // End marker
 istreambuf_iterator end;

 tm t; // Time struct (from )

 // Now that all that's out of the way, read in the date from
 // the input stream and put it in a time struct.
 dateReader.get_date(in, end, in, state, &t);

 // Now the date is in a tm struct. Print it to the out stream
 // using its locale. Make sure you only print out what you
 // know is valid in t.
 if (state == 0 || state == ios_base::eofbit) {
 // The read succeeded.
 const time_put& dateWriter =
 use_facet >(out.getloc( ));

 char fmt[] = "%x";

 if (dateWriter.put(out, out, out.fill( ),
 &t, &fmt[0], &fmt[2]).failed( ))
 cerr << "Unable to write to output stream.
";
 } else {
 cerr << "Unable to read cin!
";
 }
}

int main( ) {

 cin.imbue(locale("english"));
 cout.imbue(locale("german"));
 translateDate(cin, cout);
}

This program produces the following output:

3/28/2005
28.03.2005

 

Discussion

Writing and reading date and time data requires some knowledge of locale's design details. Read the introduction to this chapter if you aren't already familiar with the concepts of locales and facets.

C++ does not have a standard class for representing dates and times; the closest it gets is the time_t type and struct tm from . If you want to write and read dates using standard library facilities, you will have to be able to convert whatever nonstandard date representation you are using to a struct tm. It is worthwhile to do so, since the implementation(s) you are using has probably already built in support for formatting locale-sensitive dates.

Earlier, I stated that a facet was an aspect of a locale that requires locale-specific behavior. More concretely, a facet is a const instantiation of a class template for a character type that looks up how it behaves based on the locale class you give it at construction. In Example 13-4, I create an instance of the time_get facet like this:

const time_get& dateReader =
 use_facet >(in.getloc( ));

The function template use_facet looks up a given facet for a given locale. All of the standard facets are class templates that accept a character type parameter, and since I am reading and writing chars, I instantiate my time_get class for chars. The standard requires that an implementation provide template specializations for char and wchar_t, so they are guaranteed to exist (although it is not guaranteed to support a given locale other than the C locale). The time_get object I created is const because the locale functionality provided by an implementation is a set of rules for formatting various kinds of data in different locales, and the rules are not user-editable, thus, the state of a given facet should not be changed by consumer code.

The locale I pass to use_facet is the one associated with the stream I am about to write to. getloc( ) is declared in ios_base and returns the locale associated with an input or output stream. Using the locale that is already associated with the stream you want to read from or write to is the best approach; passing in the locale name as a parameter or being specified in some other manner is error prone.

Once I've created the object that's going to do the actual reading, I need to create something to capture stream state:

ios_base::iostate state = 0;

Facets don't modify the state of the stream itself, e.g., set stream::failbit = 1; instead, they will set the state in your state object to indicate that a date couldn't be read. This is because failure to read a formatted value isn't a problem with the stream necessarilythe input stream of characters may still be perfectly validbut reading it in the format you expect may not be possible.

The actual date information is stored in a struct tm. All I have to do is create a local tm variable and pass its address to the time_get or time_put facets.

Once I have read in the date, I can check the state variable I used to see if all went as expected. If it is equal to zero or ios_base::eofbit, then that indicates that the stream state is okay and that my date was read in with no problem. Since in Example 13-4 I want to write the date out to another stream, I have to create an object for just that purpose. I do it like this:

const time_put& dateWriter =
 use_facet >(out.getloc( ));

This works the same way as the previous instantiation of a time_get class, but in the other direction. After that, I created a formatting string (with printf-like formatting syntax) that will print the date. "%x" prints the date and "%X" prints the time. Be careful though: this example only read in the date, so the members of the struct tm that have to do with time are undefined at this point.

Now, I can write to the output stream. Here's how:

if (dateWriter.put(out, // Output stream iterator
 out, // Output stream
 out.fill( ), // Fill char to use
 &t, // Addr of tm struct
 &fmt[0], // Begin and end of format string
 &fmt[2]
 ).failed( )) // iter_type.failed( ) tells us if
 // there was a problem writing

time_put::put writes the date to the output stream you pass it using the locale it (the time_put object) was created with. time_put::put returns an ostreambuf_iterator, which has a member function failed that you can call to see if the iterator is in a corrupt state.

get_date isn't the only member function you can use to get components of a date from a stream. There are a few of them:

 

get_date

Gets the date from a stream using a locale's formatting rules

 

get_time

Gets the time from a stream using a locale's formatting rules

 

get_weekday

Gets the weekday name, e.g., Monday, lundi,

 

get_year

Gets the year from a stream using a locale's formatting rules

Something else that you may find handy is the date_order member function. It returns an enumeration (time_base::dateorder in ) that indicates the order of month, day, and year in the date. This can be useful if you have to parse the date output by time_get::put. Example 13-5 shows how to check the date order.

Example 13-5. Looking at date order

#include 
#include 
#include 

using namespace std;

int main( ) {

 cin.imbue(locale("german"));

 const time_get& dateReader =
 use_facet >(cin.getloc( ));

 time_base::dateorder d = dateReader.date_order( );

 string s;

 switch (d) {
 case time_base::no_order:
 s = "No order";
 break;
 case time_base::dmy:
 s = "day/month/year";
 break;
 case time_base::mdy:
 s = "month/day/year";
 break;
 case time_base::ymd:
 s = "year/month/day";
 break;
 case time_base::ydm:
 s = "year/day/month";
 break;
 }

 cout << "Date order for locale " << cin.getloc( ).name( )
 << " is " << s << endl;
}

There is also another handy feature you can use when it comes to instantiating facets: has_facet. This is a function template that returns a bool indicating whether the facet you want is defined for a given locale. So to be safe, take advantage of has_facet whenever you are instantiating a facet. If it returns false, you can always default to the classic C locale, since it's guaranteed to be there by a standard-conforming implementation. has_facet looks like this:

if (has_facet >(loc)) {
 const time_put& dateWriter =
 use_facet >(loc);

Once you get over the syntax of the time_get and time_put classes, you will find them straightforward to use. As always, you can use typedef to minimize the number of unsightly angle brackets:

typedef time_put TimePutNarrow;
typedef time_get TimeGetNarrow;
// ...
const TimeGetNarrow& dateReader = use_facet(loc);

Writing and reading dates in locale-specific formats is a bit tedious, but once you have an understanding of locale's expectations of you, it is effective and powerful. Chapter 5 is entirely dedicated to the subject of dates and times, so for more detailed formatting information when writing dates and times, see Recipe 5.2.

See Also

Chapter 5 and Recipe 5.2





C++ Cookbook
Secure Programming Cookbook for C and C++: Recipes for Cryptography, Authentication, Input Validation & More
ISBN: 0596003943
EAN: 2147483647
Year: 2006
Pages: 241
Simiral book on Amazon

Flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net