RiaDateStringParser: add option for day-first parsing of ambiguous date strings.

This commit is contained in:
Kristian Bendiksen 2023-01-13 13:04:44 +01:00
parent abc9e82ff5
commit 061e0d4688
3 changed files with 109 additions and 16 deletions

View File

@ -29,34 +29,27 @@ const std::string MONTH_NAMES[] =
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QDateTime RiaDateStringParser::parseDateString( const QString& dateString )
QDateTime RiaDateStringParser::parseDateString( const QString& dateString, OrderPreference preference )
{
return RiaDateStringParser::parseDateString( dateString.toStdString() );
return RiaDateStringParser::parseDateString( dateString.toStdString(), preference );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
QDateTime RiaDateStringParser::parseDateString( const std::string& dateString )
QDateTime RiaDateStringParser::parseDateString( const std::string& dateString, OrderPreference preference )
{
int year = -1, month = -1, day = -1;
bool parsedOk = false;
if ( hasSeparators( dateString ) )
{
parsedOk = tryParseYearFirst( dateString, year, month, day ) ||
tryParseDayFirst( dateString, year, month, day ) || tryParseMonthFirst( dateString, year, month, day );
parsedOk = parseDateStringWithSeparators( dateString, year, month, day, preference );
}
if ( !parsedOk )
{
auto firstNumerical = dateString.find_first_of( "0123456789" );
if ( firstNumerical != std::string::npos )
{
std::string subString = dateString.substr( firstNumerical );
parsedOk = tryParseYearFirstNoSeparators( subString, year, month, day ) ||
tryParseDayFirstNoSeparators( subString, year, month, day ) ||
tryParseMonthFirstNoSeparators( subString, year, month, day );
}
parsedOk = parseDateStringWithoutSeparators( dateString, year, month, day, preference );
}
QDateTime dt;
@ -66,6 +59,63 @@ QDateTime RiaDateStringParser::parseDateString( const std::string& dateString )
return dt;
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiaDateStringParser::parseDateStringWithSeparators( const std::string& dateString,
int& year,
int& month,
int& day,
OrderPreference preference )
{
auto tryParseAllYearFirst = []( const std::string& dateString, int& year, int& month, int& day ) {
return tryParseYearFirst( dateString, year, month, day ) || tryParseDayFirst( dateString, year, month, day ) ||
tryParseMonthFirst( dateString, year, month, day );
};
auto tryParseAllDayFirst = []( const std::string& dateString, int& year, int& month, int& day ) {
return tryParseDayFirst( dateString, year, month, day ) || tryParseYearFirst( dateString, year, month, day ) ||
tryParseMonthFirst( dateString, year, month, day );
};
return ( preference == OrderPreference::YEAR_FIRST ) ? tryParseAllYearFirst( dateString, year, month, day )
: tryParseAllDayFirst( dateString, year, month, day );
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
bool RiaDateStringParser::parseDateStringWithoutSeparators( const std::string& dateString,
int& year,
int& month,
int& day,
OrderPreference preference )
{
auto tryParseAllYearFirstNoSeparators = []( const std::string& dateString, int& year, int& month, int& day ) {
return tryParseYearFirstNoSeparators( dateString, year, month, day ) ||
tryParseDayFirstNoSeparators( dateString, year, month, day ) ||
tryParseMonthFirstNoSeparators( dateString, year, month, day );
};
auto tryParseAllDayFirstNoSeparators = []( const std::string& dateString, int& year, int& month, int& day ) {
return tryParseDayFirstNoSeparators( dateString, year, month, day ) ||
tryParseYearFirstNoSeparators( dateString, year, month, day ) ||
tryParseMonthFirstNoSeparators( dateString, year, month, day );
};
auto firstNumerical = dateString.find_first_of( "0123456789" );
if ( firstNumerical != std::string::npos )
{
std::string subString = dateString.substr( firstNumerical );
return ( preference == OrderPreference::YEAR_FIRST )
? tryParseAllYearFirstNoSeparators( subString, year, month, day )
: tryParseAllDayFirstNoSeparators( subString, year, month, day );
}
return false;
}
//--------------------------------------------------------------------------------------------------
/// Try parse formats
/// 'yyyy mm dd'

View File

@ -30,10 +30,29 @@
class RiaDateStringParser
{
public:
static QDateTime parseDateString( const QString& dateString );
static QDateTime parseDateString( const std::string& dateString );
enum class OrderPreference
{
YEAR_FIRST,
DAY_FIRST
};
static QDateTime parseDateString( const QString& dateString, OrderPreference preference = OrderPreference::YEAR_FIRST );
static QDateTime parseDateString( const std::string& dateStringString,
OrderPreference preference = OrderPreference::YEAR_FIRST );
private:
static bool parseDateStringWithSeparators( const std::string& dateString,
int& year,
int& month,
int& day,
OrderPreference preference );
static bool parseDateStringWithoutSeparators( const std::string& dateString,
int& year,
int& month,
int& day,
OrderPreference preference );
static bool tryParseYearFirst( const std::string& s, int& year, int& month, int& day );
static bool tryParseDayFirst( const std::string& s, int& year, int& month, int& day );
static bool tryParseMonthFirst( const std::string& s, int& year, int& month, int& day );

View File

@ -131,3 +131,27 @@ TEST( RiaDateStringParserTest, ParseWithoutSeparators )
EXPECT_TRUE( nov24thDT == RiaDateStringParser::parseDateString( nov24thString ) );
}
}
//--------------------------------------------------------------------------------------------------
///
//--------------------------------------------------------------------------------------------------
TEST( RiaDateStringParserTest, ParseDayFirst )
{
std::vector<std::pair<QString, QDate>> dates = {
{ "28-DEC-1985", QDate( 1985, 12, 28 ) },
{ "15-jan-02", QDate( 2002, 1, 15 ) },
{ "15-january-02", QDate( 2002, 1, 15 ) },
{ "2023-january-17", QDate( 2023, 1, 17 ) },
{ "150102", QDate( 2002, 1, 15 ) },
{ "310102", QDate( 2002, 1, 31 ) },
};
for ( auto [dateString, expected] : dates )
{
QDateTime actual =
RiaDateStringParser::parseDateString( dateString, RiaDateStringParser::OrderPreference::DAY_FIRST );
EXPECT_EQ( expected.day(), actual.date().day() );
EXPECT_EQ( expected.month(), actual.date().month() );
EXPECT_EQ( expected.year(), actual.date().year() );
}
}