4

This is a question about invalid input, not invalid formatting. For example given the following code:

tm bar;

foo >> get_time(&bar, "%Y-%m-%d");

cout >> bar.tm_year >> bar.tm_mon >> bar.tm_mday >> endl;

This is fine if I define: stringstream foo("2001-02-28 non-leap year"); And has a clear error if I have invalid format such as: stringstream foo("bad format 2001-02-28 non-leap year");

But I don't know how to detect if my input was invalid for example:

stringstream foo("2001-02-30 non-leap year");

In this case bar can be read as though nothing was wrong. Is there something I can listen for which will alert me that the input was invalid?

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • At a guess, `if (foo >> get_time(...)) {/*use bar*/}` – chris Mar 27 '15 at 23:05
  • @chris Nope that is equivalent to `if(foo.good())` which is only set to false if the *formatting* is invalid, not if the *input* is invalid. You can typically test on [the cloud Visual Studio compiler](http://webcompiler.cloudapp.net/) but they've been down for a day... – Jonathan Mee Mar 28 '15 at 12:22

2 Answers2

3

Since mktime tries to interpret also out-of-range values (i.e. for 2001-02-30 will be interpreted as 2001-03-01), you can do an mktime followed by a localtime, if you get different values back it means that the original ones were not valid.

Matteo Italia
  • 123,740
  • 17
  • 206
  • 299
1

As is mentioned by Matteo Italia's answer, apart from writing your own Gregorian validator, your only reliable double check is mktime. tms obtained with get_time cannot be validated, as fields may and may not be filled out. Consider the following 2 invalid examples:

Doing this: istringstream("2001-02-30") >> get_time(&bar, "%Y-%m-%d"); results in a result that would change when run thorough mktime:

bar.tm_mday : 30
bar.tm_mon : 1
bar.tm_year : 100

While doing this: istringstream("2001-13-30") >> get_time(&bar, "%Y-%m-%d"); results in a result that would not change when run through mktime:

bar.tm_mday : 13
bar.tm_mon : 0
bar.tm_year : 100

In order to have all invalid dates altered by mktime we need to read the date in without the use of get_time:

int year;
int month;
int day;
istringstream foo("2000-13-30");

foo >> year >> ws;
foo.ignore();
foo >> month >> ws;
foo.ignore();
foo >> day;

tm bar = { 0, 0, 0, day, month - 1, year - 1900 };

At this point any error in bar may be corrected by mktime so our actual validation step will be to check for a non-time_t(-1) return and compare the modified bar back to the original values: if(time_t(-1) != mktime(&bar) && bar.tm_mday == day && bar.tm_mon == month - 1 && bar.tm_year == year - 1900) if this condition is true then the input to bar was valid.

In conclusion, it is possible to validate a tm without writing a Gregorian validator, but it is not possible to validate a tm that has been read in by get_time.

Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288