Question

I would like to convert an int date like:

20111201

to string:

01DEC2011

Is there a fast date format conversion built into C++ (or maybe a bash system command I can execute instead) to do this or am I stuck making a switch for all of the months?

Was it helpful?

Solution

You could use the strptime to convert your string to a struct tm, then use strftime to reformat it:

#include <ctime>
#include <iostream>
#include <sstream>

int main()
{
  std::ostringstream date1;
  date1 << 20111201;

  struct tm tm;
  strptime(date1.str().c_str(), "%Y%m%d", &tm);

  char date2[10];
  strftime(date2, sizeof(date2), "%d%b%Y", &tm);

  std::cout << date1.str() << " -> " << date2 << std::endl;
}

Output is:

20111201 -> 01Dec2011

Just need to convert the Dec to upper case if it's necessary.

OTHER TIPS

Don't use bash here. The way to go is to use Boost in C++ for more reasons than I've time to list here, but ultimately it will be just as fast as most other solutions you'll encounter and unless your functionality is absolutely time critical, it won't make a great deal of difference anyway.

Also, It's going to be far more flexible and maintainable than all those crappy little hard coded date conversion routines that you always encounter.

The following code will do what you want.

#include <iostream>
#include <sstream>

#include <boost/date_time/gregorian/gregorian.hpp>
#include <boost/algorithm/string.hpp>

using namespace boost::gregorian;
using namespace std;

int main(int argc, char **argv)
{
    int dateIn = 20111201;

    // Read the date in from ISO format as an int.
    ostringstream ss;
    ss << dateIn;
    date d(from_undelimited_string( ss.str() ));

    // Set the output format
    date_facet *fct = new date_facet("%d%b%Y");    // [1]
    locale loc = locale(locale::classic(), fct);

    // Render the date as a string;
    ss.str("");
    ss.imbue(loc);
    ss << d;
    string dateOut( ss.str() );
    boost::to_upper( dateOut );

    cout << dateOut << endl;
}

This gives the following output:

01DEC2011

Just changing the format string "%d%b%Y" at ref [1] will change to a different output format but remember I've converted it to uppercase as well.

There's nothing directly built-in, since this format for dates is relatively rare. The simplest solution here would be to break the date up into year month day using % and / operators (e.g. month is value / 100 % 100), then format the three values normally, using std::ostream, and looking up the date in a table. (This would obviously require some error checking, since not all integral values yield valid dates.)

New answer to old question. This answer traffics through the C++11/14 <chrono> library instead of C's tm or boost::date_time. Otherwise it is very similar to the existing answers. It requires this free, open-source library for the parsing and formatting.

#include "tz.h"
#include <iostream>
#include <locale>
#include <sstream>

int
main()
{
    auto date1 = 20111201;

    std::stringstream stream;
    stream.exceptions(std::ios::failbit);
    stream << date1;

    std::chrono::system_clock::time_point tp;
    date::parse(stream, "%Y%m%d", tp);

    auto str = date::format("%d%b%Y", tp);

    auto& ct = std::use_facet<std::ctype<char>>(std::locale::classic());
    ct.toupper(&str.front(), &str.back()+1);
    std::cout << str << '\n';
}

I've included stream.exceptions(std::ios::failbit); to noisily detect invalid "integer dates". And I've included old C++98 code to convert the string to uppercase (the locale dance at the end).

01DEC2011

One of the advantages of using a modern C++ date/time library is the ease with which changes can be made. For example, what if now you need to parse the timestamp not with day-precision, but with millisecond precision? Here is how that might be done:

auto date1 = 20111201093357.275L;

std::stringstream stream;
stream.exceptions(std::ios::failbit);
stream << std::fixed << date1;

std::chrono::system_clock::time_point tp;
date::parse(stream, "%Y%m%d%H%M%S", tp);

auto str = date::format("%d%b%Y %T", tp);

auto& ct = std::use_facet<std::ctype<char>>(std::locale::classic());
ct.toupper(&str.front(), &str.back()+1);
std::cout << str << '\n';

which outputs:

01DEC2011 09:33:57.275000

Or perhaps these timestamps are known to originate from Chatham Island off the coast of New Zealand and you need them in UTC. Just add one line after the parse:

tp = date::locate_zone("Pacific/Chatham")->to_sys(tp);

And now the output is:

30NOV2011 19:48:57.275000

Taking into account arbitrary timezones and subsecond precision is currently beyond the capabilities of all other C++ libraries.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top