6

I am using strptime to parse a date and time with a specific format. The format uses the Python formatting directives, which are exactly the same as the C directives except there is an additional %f directive in Python for milliseconds defined as Microsecond as a decimal number, zero-padded on the left.. So for example:

std::tm tmb;
strptime("2010-12-30T01:20:30.0Z", "%Y-%m-%dT%H:%M:%S.%fZ", &tmb);

This would cause a parsing error because of the %f. I cannot do a straight remove because directives like %m and %d can take either one or two digits.

Additionally, this is a problem because the %f directive can take from 1 to 6 digits. Is there a way to work around this issue?

I am working in C++, if there are any built-in functions that can help.

Stargateur
  • 24,473
  • 8
  • 65
  • 91
Mohak Saxena
  • 81
  • 1
  • 4
  • Are you trying to keep the sub-second data or simply ignore it? https://stackoverflow.com/a/25155338/1364178 says you can't store sub-second data in tm. If you're trying to ignore it, can't you split the string based on the period after seconds? – David Vereb Jun 06 '17 at 20:38
  • 1
    You may want not only to thank Howard but to also accept the answer – mementum Mar 28 '18 at 09:33

1 Answers1

5

Using Howard Hinnant's free, open-source, C++11, header-only datetime library, you can easily parse any precision you want. You just can't put it into a tm since that is limited to seconds precision.

For example:

#include "date.h"
#include <cassert>
#include <sstream>

int
main()
{
    std::chrono::system_clock::time_point tp;
    std::istringstream ss{"2010-12-30T01:20:30.123456Z"};
    ss >> date::parse("%FT%TZ", tp);
    assert(!ss.fail());
    using namespace date;
    using namespace std::chrono_literals;
    assert(tp == sys_days(2010_y/dec/30) + 1h + 20min + 30s + 123456us);
}

This uses C++14 chrono literals. If you're in C++11, the last two lines would look like:

    using namespace std::chrono;
    assert(tp == sys_days(2010_y/dec/30) + hours{1} + minutes{20} +
                                           seconds{30} + microseconds{123456});

If you're stuck with pre-<chrono> C++ (C++98/C++03), this library won't help you.

The precision of the parse is controlled by the precision of the time_point that you input into the parse function, instead of by a formatting flag. The parse will attempt to parse as many decimal digits as your time_point will hold, but will gracefully give up if it gets less digits:

sys_time<nanoseconds> tp; // type-alias for a system_clock::time_point
                          //   with nanoseconds precision
ss >> date::parse("%FT%TZ", tp);  // still ok

If there are more digits than needed, the parse will fail, but only because it won't see the trailing (required) Z:

sys_time<milliseconds> tp;
ss >> date::parse("%FT%TZ", tp);
assert(ss.fail());  // found "4" instead of "Z"

If the Z hadn't been part of the format, it would have parsed "2010-12-30T01:20:30.123" into the milliseconds-precsion time_point.

This library has complete parsing and formatting facilities available, built on top of the std <chrono> library, so that you never have to deal with the ancient tm structure. But you can convert to/from tm (at seconds precision) for communicating with other code if you need to.

"%F" is a shortcut for "%Y-%m-%d". You can use either.

"%T" is a shortcut for "%H:%M:%S". You can use either.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 1
    Thank you for this! – Mohak Saxena Jun 07 '17 at 01:19
  • 1
    *The precision of the parse is controlled by the precision of the time_point that you input into the parse function, instead of by a formatting flag*. I had already seen the docs of your library and this comment here has clarified my doubts, because `%T` has here parsed microseconds. A note after `%T` in `from_stream parsing` in the docs may help others. Fantastic library. – mementum Mar 28 '18 at 09:31