Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I convert a std::string to a boost::gregorian::date?

I'm trying to convert a std::string to a boost::gregorian::date like this:

using namespace boost::gregorian;

std::string str = "1 Mar 2012";
std::stringstream ss(str);
date_input_facet *df = new date_input_facet("%e %b %Y");
ss.imbue(std::locale(ss.getloc(), df));
date d;

ss >> d;  //conversion fails to not-a-date-time

std::cout << "'" << d << "'" << std::endl;  //'not-a-date-time'

But if the string contains "01 Mar 2012", the conversion succeeds.

How can I convert strings like "1 Mar 2012" to the equivalent boost::gregorian::date?

like image 211
coredumped Avatar asked Mar 01 '12 12:03

coredumped


People also ask

How to convert string to DateTime in cpp?

The most general C++ way is to use Boost. DateTime. While this is a simple way to do it, it doesn't really provide any real insight into the process and it's always possible he may not have access to this.

How to convert to date in C++?

The strftime() function in C++ converts the given date and time from a given calendar time time to a null-terminated multibyte character string according to a format string. The strftime() function is defined in <ctime> header file.


1 Answers

This would seem to be a problem with the use of %e in your input facet.

The Boost.Gregorian documentation specifies that:

%d       Day of the month as decimal 01 to 31

%e #    Like %d, the day of the month as a decimal number, but a leading zero is replaced by a space

The problem is that if you look at the top part of the documentation, you will notice this warning:

The flags marked with a hash sign (#) are implemented by system locale and are known to be missing on some platforms

I've tried the following cases:

input_string = " 1"
date_format = "%e"
result = failed

input_string = "01"
date_format = "%e"
result = success

input_string = "2000 Mar 1"
date_format = "%Y %b %e"
result = failed

input_string = "2000 Mar  1"
date_format = "%Y %b %e"
result = success

input_string = "2000 Mar 01"
date_format = "%Y %b %e"
result = success

It seems thus that this is a limitation of the Boost implementation (or at least, the fact that it relies on a specific locale for the parsing of %e): The parsing fails when %e is the first item in the input string, and a space is used instead of a leading 0.

My (blind) guess would be that the issue comes from stringstream's tendency to skip whitespaces. I've tried to find a solution with the std::noskipws, however, could not find something that worked.

As a workaround, I would recommend adding a leading zero, or if possible, use a different date format.

Another workaround would be to manually add the space, and reverse the order of the "words" in the string. I've accomplished a working solution like this:

#include "boost/date_time/gregorian/gregorian.hpp"
#include <iostream>
#include <string>

int main(void) {
    using namespace boost::gregorian;

    std::string input_date("1 Mar 2000");

    { // local scope to remove temporary variables as soon as possible
        std::stringstream tmp_ss(input_date);
        std::string tmp;
        input_date.clear(); // empty the initial string
        while (tmp_ss >> tmp) {
            input_date.insert(0, tmp); // insert word at beginning of string
            if(tmp.size() == 1) // if word is one char long, add extra space
                input_date.insert(0, " ");
            input_date.insert(0, " "); // add space to separate words
        }
    }

    std::stringstream ss(input_date);

    // The order of the date is reversed.
    date_input_facet *df = new date_input_facet("%Y %b %e");
    ss.imbue(std::locale(ss.getloc(), df));
    
    date d;  //conversion works

    ss >> d;

    std::cout << "'" << d << "'" << std::endl; // ouputs date correctly.

    return 0;
}    

Good luck,

like image 161
teotwaki Avatar answered Sep 18 '22 07:09

teotwaki