2022年3月31日木曜日

datetime の 曖昧なフォーマットに対応する忘備録

日付のフォーマットと制約が厳しい。
わりとありそうな日付の表記ゆれをパースするグラマーを構築してみた。
#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 で扱うようにした。バグでした。

0 件のコメント: