Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ : convert date/time string to tm struct

Consider this somewhat of a follow up to this question. Essentially, the C++ date/time formatting facilities seem to be hopelessly broken - so much so that in order to do something as simple as convert a date/time string into an object, you really have to resort to either Boost.Datetime or good old C strftime/strptime facilities.

The problem is that neither of these solutions work directly with the C++ locale settings imbued onto a particular iostream object. The C facilities use the global C/POSIX locale settings, whereas the I/O facilities in Boost.Datetime seem to bypass iostream locale settings entirely by allowing the user to directly set the names of months, weekdays, etc., regardless of locale.

So, I wanted something that would respect the locale settings imbued onto a particular I/O stream that would allow me to convert a string into a struct tm. It seemed easy enough, but I ran into obstacles around every corner. At first, I noticed that some implementations of the STL provide a non-standard std::time_get::get function, so I decided to implement something similar. Basically, I would simply iterate over the format string and whenever I hit a format flag, I would use one of the time_get facilities (like get_monthname, get_weekday, get_year, etc.) to convert the input string into a struct tm. This seems easy enough, except each one of these functions requires an exact iterator range. You can't convert "Monday,", it has to be "Monday" exactly, or the conversion fails. Since the iterators have to be istreambuf_iterator, you can't simply scan ahead, because each increment changes the get position in the stream buffer. So, basically you have to first iterate over the stream, copying each character into another streambuffer, and then when you hit a delimiter (like a space or a comma), use the second streambuffer with the time_get facilities. It's literally as if the C++ designers went out of their way to make this as annoying as possible.

So, is there an easier solution? What do most C++ programmers do when they need to convert a date/time string to an object? Do we just have to use the C facilities, and lose the advantages that come along with different locale settings imbued on different iostream objects?

like image 954
Charles Salvia Avatar asked Nov 06 '22 08:11

Charles Salvia


1 Answers

Boost uses the standard locale(s) by default; you don't have to bypass anything:

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

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

  std::locale::global(std::locale(""));
  std::locale german("German_Germany");
  std::locale french("French_France");

  date d1(day_clock::local_day());
  date d2;
  std::stringstream ss("2002-May-01");

  std::cout << "Mine: " << d1 << " | ";
  ss >> d2;
  std::cout << d2 << '\n';

  std::cout.imbue(german);
  std::cout << "Germany: " << d1 << " | ";
  ss.imbue(german);
  ss << "2002-Mai-01";
  ss >> d2;
  std::cout << d2 << '\n';

  std::cout.imbue(french);
  std::cout << "France: " << d1 << " | " << d2 << '\n';

  std::tm t = to_tm(d1);
  std::cout << "tm: " << asctime(&t);
}

(Those locale names are specific to Windows, of course.) Output:

Mine: 2010-Oct-28 | 2002-May-01
Germany: 2010-Okt-28 | 2002-Mai-01
France: 2010-oct.-28 | 2002-mai-01
tm: Thu Oct 28 00:00:00 2010
like image 79
Steve M Avatar answered Nov 09 '22 04:11

Steve M