views:

70

answers:

2

Hi,

At the moment I am trying to read in a timestring formatted and create a duration from that. I am currently trying to use the boost date_time time_duration class to read and store the value.

boost date_time provides a method time_duration duration_from_string(std::string) that allows a time_duration to be created from a time string and it accepts strings formatted appropriately ("[-]h[h][:mm][:ss][.fff]".).

Now this method works fine if you use a correctly formatted time string. However if you submit something invalid like "ham_sandwich" or "100" then you will instead be returned a time_duration that is not valid. Specifically if you try to pass it to a standard output stream then an assertion will occur.

My question is: Does anyone know how to test the validity of the boost time_duration? and failing that can you suggest another method of reading a timestring and getting a duration from it?

Note: I have tried the obvious testing methods that time_duration provides; is_not_a_date_time(), is_special() etc and they don't pick up that there is an issue.

Using boost 1.38.0

+2  A: 

From the documentation, it looks like you may want to try using the stream operators (operator<<, operator>>); error conditions are described at Date Time Input/Output.

Alternately, I suppose you could validate the string before passing it in. Right offhand, it doesn't look like that particular method has any error handling.

Edit: I'm not sure I would have thought to check the return value like this if it weren't for Brian's answer, but for completeness here's a full example that takes a string as input. You can either check the return value or have it throw an exception (I believe you'd want to catch std::ios_base_failure):

#include <iostream>
#include <sstream>
#include <string>
#include <boost/date_time/posix_time/posix_time.hpp>

using namespace std;
using namespace boost::posix_time;

int main(int argc, char **argv) {
    if (argc < 2) {
        cout << "Usage: " << argv[0] << " TIME_DURATION" << endl;
        return 2;
    }

    // No exception
    stringstream ss_noexcept(argv[1]);
    time_duration td1;
    if (ss_noexcept >> td1) {
        cout << "Valid time duration: " << td1 << endl;
    } else {
        cout << "Invalid time duration." << endl;
    }

    // Throws exception
    stringstream ss2;
    time_duration td2;
    ss2.exceptions(ios_base::failbit);
    ss2.str(argv[1]);
    try {
        ss2 >> td2;
        cout << "Time duration: " << td2 << endl;
    } catch (ios_base::failure e) {
        cout << "Invalid time duration (exception caught). what():\n"
                << e.what() << endl;
    }
}
Sam
I have actually tried using the error conditions but the problem is that the assertion happens inside the time_duration so it doesn't matter that the error condition on the stream is set Specifically what the error conditions allow you to do is that the stream will throw when setting its ios::failbit. So in my case the assertion is occurring inside the time_duration before the stream detects an error.
radman
Hmm... let me throw together some sample code and see if I can see the issue.
Sam
Looks good to me. :)
Brian Neal
yep that did the trick alright. I didn't know that you could use a stream operator to create a time_duration but that is definitely the right way to do it. Thanks for the help.
radman
+2  A: 

Use the stream operators.

time_duration td;
if (std::cin >> td)
{
   // it's valid
}
else
{
   // it isn't valid
}
Brian Neal
Brian you actually got to the answer first (using the stream operators to build the time_duration instead of using duration_from_string). I'm giving the tick to Sam as his answer is more comprehensive and detailed. However if I could vote you up more than once I would!
radman
I'll do it for you. Thanks, Brian, for the pointer.
Sam