わりとありそうな日付の表記ゆれをパースするグラマーを構築してみた。
#include <boost/spirit/include/qi.hpp> // boost::spirit::qi を利用します
#include <boost/fusion/adapted/struct/adapt_struct.hpp>
#include <iostream>
#include <string>
#include <boost/date_time.hpp>
namespace pt = boost::posix_time;
namespace qi = boost::spirit::qi;
struct timeinfo {
int16_t year_;
uint16_t month_;
uint16_t day_;
uint16_t hour_;
uint16_t minute_;
uint16_t second_;
//uint32_t millisec_;
std::string millisec_;
};
BOOST_FUSION_ADAPT_STRUCT(
timeinfo,
(int16_t, year_)
(uint16_t, month_)
(uint16_t, day_)
(uint16_t, hour_)
(uint16_t, minute_)
(uint16_t, second_)
//(uint32_t, millisec_)
(std::string, millisec_)
)
template <typename Iterator, typename Skipper>
struct time_grammar : qi::grammar<Iterator, timeinfo(), Skipper> {
qi::rule<Iterator, timeinfo(), Skipper> start_;
qi::int_parser<int16_t,10,4,4> int4_;
qi::uint_parser<uint16_t,10,2,2> uint2_;
qi::rule<Iterator, std::string(), Skipper> numeric_;
time_grammar() : time_grammar::base_type(start_, "time_grammar") {
numeric_ = *qi::char_('0','9');
start_ = (
int4_
>> -(qi::lit('/') | qi::lit('-')) >> uint2_
>> -(qi::lit('/') | qi::lit('-')) >> uint2_
//>> (qi::lexeme[qi::lit(' ')] | qi::lit('T'))
>> -(qi::lit('T'))
>> uint2_
>> -(qi::lit(':')) >> uint2_
>> -(qi::lit(':')) >> uint2_
>> (
//-( (qi::lit('.') | qi::lit(',')) >> qi::uint_)
-( (qi::lit('.') | qi::lit(',')) >> numeric_)
| qi::attr(0)
)
>> -(qi::lit('Z'))
);
}
};
int main() {
std::string input[] = {
"20210412T123455Z",
"20210412T123455,987654321Z",
"20210412T123455.987654321Z",
"2004-03-21 12:45:33",
"2004/03/21 12:45:33",
"23.09.2004 04:12:21",
"2003-02-11"
};
time_grammar<std::string::iterator, qi::standard::space_type> myg;
for( int i = 0; i < sizeof(input)/sizeof(input[0]); i++ ) {
timeinfo ti;
qi::phrase_parse( input[i].begin(), input[i].end(), myg, qi::standard::space, ti );
std::cout << "--------------" << std::endl;
std::cout << ti.year_ << std::endl;
std::cout << ti.month_ << std::endl;
std::cout << ti.day_ << std::endl;
std::cout << ti.hour_ << std::endl;
std::cout << ti.minute_ << std::endl;
std::cout << ti.second_ << std::endl;
std::cout << ti.millisec_ << std::endl;
}
return 0;
}
2023/06/02 追記: lexeme がうまく動作しないのでスペースを無視した。millisec が ,020Z と指定された場合 2 に変換されてしまうので std::string で扱うようにした。バグでした。