From 5ce03651285cfa952fb3755af2b6fd74d97ea96f Mon Sep 17 00:00:00 2001 From: hama Date: Mon, 6 Apr 2015 23:27:39 +0200 Subject: [PATCH] extended date/time parser, refind --- base/ReQStringUtil.cpp | 394 ++++++++++++++++++++++++++++++++++------- base/ReQStringUtil.hpp | 22 ++- base/rebase.hpp | 1 + 3 files changed, 350 insertions(+), 67 deletions(-) diff --git a/base/ReQStringUtil.cpp b/base/ReQStringUtil.cpp index 7dd4c06..9d52556 100644 --- a/base/ReQStringUtil.cpp +++ b/base/ReQStringUtil.cpp @@ -18,6 +18,7 @@ */ #include "base/rebase.hpp" #include +#include /** * @brief Determines the length and vlaue of an integer. * @@ -90,6 +91,194 @@ int ReQStringUtil::lengthOfUInt(const ReString& text, int start, int radix, return rc; } +/** + * Skips a character in a text at a given position if it has an expected value. + * + * @param text text to inspect + * @param expected the character which is expected + * @param index IN/OUT: the position of the expected character. + * Will be incremented if the expected character is found + * @param length IN/OUT: IN: 0: do nothing
+ * OUT: 0: the expected character was not found. + * otherwise: the length is incremented + */ +void ReQStringUtil::skipExpected(const ReString& text, QChar expected, + int& index, int length){ + if (length == 0){ + // error state, do nothing + } else if (index >= text.length() || text[index] != expected){ + length = 0; + } else { + index++; + length++; + } +} + +/** + * Returns the length of a date in a string. + * + * The syntax of a time is 'dd.mm.yyyy' or 'yyyy.mm.dd'. + * + * @param text text to inspect + * @param start the first index in text to inspect + * @param value OUT: the value of the found date. Not changed if result is 0.
+ * May be NULL + * @return 0: no date found
+ * otherwise: the length of the date in the string + */ +int ReQStringUtil::lengthOfDate(const ReString& text, int start, QDate* value) +{ + uint day = 0; + uint month = 0; + uint year = 0; + int length = lengthOfUInt(text, start, 10, &year); + switch(length){ + case 1: + case 2: + day = year; + year = 0; + break; + case 4: + break; + default: + length = 0; + break; + } + if (length > 0){ + start += length; + if (start >= text.length() || text[start] != '.') + length = 0; + else + start++, length++; + } + int length2; + if (length > 0){ + length2 = lengthOfUInt(text, start, 10, &month); + if (length2 < 1 || length2 > 2) + length = 0; + else + start += length2, length += length; + } + skipExpected(text, '.', start, length); + if (length > 0){ + length2 = lengthOfUInt(text, start, 10, &month); + if (length2 < 1 || length2 > 2) + length = 0; + else + start += length2, length += length2; + } + skipExpected(text, '.', start, length); + if (year > 0){ + length2 = lengthOfUInt(text, start, 10, &day); + if (length2 < 1 || length2 > 2) + length = 0; + else + start += length2, length += length2; + } else{ + length2 = lengthOfUInt(text, start, 10, &day); + if (length2 != 4) + length = 0; + else + start += length2, length += length2; + } + if (day < 1 || day > 31 || month < 1 || month > 12 || year < 1970 || year > 2100) + length = 0; + if (length != 0 && value != NULL) + *value = QDate(year, month, day); + return length; +} + +/** + * Returns the length of a date and/or time in a string. + * + * @param text text to inspect + * @param start the first index in text to inspect + * @param allowDateOnly false: if the date is not followed by + * a time the result will be 0 + * @param allowTimeOnly false: if no date is found at the given + * text position the result will be 0 + * @param value the value of the found date. Not changed if result is 0.
+ * May be NULL + * @return 0: no date found
+ * otherwise: the length of the date in the string + */ +int ReQStringUtil::lengthOfDateTime(const ReString& text, int start, + bool allowDateOnly, bool allowTimeOnly, QDateTime* value) +{ + QDate date; + QTime time; + int length = lengthOfDate(text, start, &date); + if (length == 0){ + if (allowTimeOnly){ + date = QDate::currentDate(); + length = lengthOfTime(text, start, &time); + } + } else { + if (start + length + 1 + 3 <= text.length()) { + start += length; + int length2 = 0; + if (! text[start].isDigit()){ + QTime time2; + length2 = lengthOfTime(text, start + 1, &time2); + if (length2 == 0 && ! allowDateOnly) + length = 0; + else if (length2 > 0){ + length += 1 + length2; + time = time2; + } + } + } + } + if (length > 0 && value != NULL) + *value = QDateTime(date, time); + return length; +} +/** + * Returns the length of a time in a string. + * + * The syntax of a time is hh:mm[:ss] + * + * @param text text to inspect + * @param start the first index in text to inspect + * @param value OUT: the value of the found time. Not changed if result is 0.
+ * May be NULL + * @return 0: no date found
+ * otherwise: the length of the date in the string + */ +int ReQStringUtil::lengthOfTime(const ReString& text, int start, QTime* value) +{ + uint hour = 0; + uint minute = 0; + uint sec = 0; + int length = lengthOfUInt(text, start, 10, &hour); + if (length > 0 && hour > 23) + length = 0; + if (length > 0){ + start += length; + } + int length2; + skipExpected(text, ':', start, length); + if (length > 0){ + length2 = lengthOfUInt(text, start, 10, &minute); + if (length2 < 1 || length2 > 2 || minute >= 60) + length = 0; + else + start += length2, length += length2; + } + if (length > 0 && start < text.length() && text[start] == ':'){ + length++; + start++; + length2 = lengthOfUInt(text, start, 10, &sec); + if (length2 < 1 || length2 > 2 || sec >= 60) + length = 0; + else + start += length2, length += length2; + } + if (length != 0 && value != NULL) + *value = QTime(hour, minute, sec); + return length; +} + /** * @brief Determines the length and value of a floting point number. * @@ -198,32 +387,10 @@ public: */ ReUnitParser::ReUnitParser(const QString& expr, const char* unitList) : m_result(0), m_expr(expr), m_message(), m_unitList(unitList){ - // Replace the '-' operator by '+' as operator and '-' as sign: - // This makes the syntax easier to parse: only one sum operator ('+'). - for (int ii = m_expr.length() - 1; ii > 0; ii--){ - if (m_expr[ii] == '-' && m_expr[ii -1] != '+' && m_expr[ii - 1] != '*'){ - m_expr.insert(ii, '+'); - } - } + normalize(); parse(); } -/** - * Returns whether the given expression is valid. - * - * @return true: the expression is valid, a result was calculated - */ -bool ReUnitParser::isValid() const{ - return m_message.isEmpty(); -} -/** - * Returns an empty string or the error message. - * - * @return "": no error occurred
- * otherwise: the error message - */ -const QString& ReUnitParser::errorMessage() const{ - return m_message; -} + /** * Returns the result of the expression as a 64 bit integer. * @@ -256,47 +423,42 @@ real_t ReUnitParser::asReal(real_t defaultValue){ } /** - * Calculates the value of a number or a (number, unit) pair. + * Returns an empty string or the error message. * - * @param value a non negative number or a number followed by a unit
- * only units defined in m_unitList are allowed
- * examples: "4kByte" returns 4000, "4kibyte" returns 4096 - * @return the value of the number multiplied by the factor given by the unit - * @throws ReParserException + * @return "": no error occurred
+ * otherwise: the error message */ -uint64_t ReUnitParser::valueOf(const QString& value) const{ - uint64_t rc = 0; - int ix = ReQStringUtil::lengthOfUInt64(value, 0, 10, &rc); - if (ix == 0) - throw ReParserException(QObject::tr("number expected: ") + value); - QString unit = value.mid(ix); - if (! unit.isEmpty()){ - QStringList units = QString(m_unitList).split(";"); - QStringList::const_iterator it; - bool found = false; - for (it = units.begin(); it != units.end(); ++it){ - QStringList pair = it->split(":"); - if (pair.count() == 0) - throw ReParserException(QObject::tr("missing ':' in unit definition, e.g. 'k:1000': ") + *it); - if (pair.count() > 2) - throw ReParserException(QObject::tr("too many ':' in unit definition: ") + *it); - bool ok = false; - QString unit2 = *pair.begin(); - QString factor = *++pair.begin(); - uint64_t nFactor = factor.toLongLong(&ok); - if (! ok) - throw ReParserException(QObject::tr("not a number: ") + factor); - if (unit2.startsWith(unit)){ - rc *= nFactor; - found = true; - break; - } - } - if (! found) - throw ReParserException(QObject::tr("unknown unit, allowed: ") + QString(m_unitList)); - } - return rc; +const QString& ReUnitParser::errorMessage() const{ + return m_message; +} + +/** + * Returns whether the given expression is valid. + * + * @return true: the expression is valid, a result was calculated + */ +bool ReUnitParser::isValid() const{ + return m_message.isEmpty(); +} + +/** + * @brief Normalizes the internal stored unit expression. + */ +void ReUnitParser::normalize(){ + // Remove the blanks: + for (int ii = m_expr.length() - 1; ii >= 0; ii--){ + if (m_expr[ii].isSpace()) + m_expr.remove(ii, 1); + } + // Replace the '-' operator by '+' as operator and '-' as sign: + // This makes the syntax easier to parse: only one sum operator ('+'). + for (int ii = m_expr.length() - 1; ii > 0; ii--){ + if (m_expr[ii] == '-' && m_expr[ii -1] != '+' && m_expr[ii - 1] != '*'){ + m_expr.insert(ii, '+'); + } + } } + /** * Evaluate the expression. */ @@ -339,6 +501,49 @@ void ReUnitParser::parse(){ } } +/** + * Calculates the value of a number or a (number, unit) pair. + * + * @param value a non negative number or a number followed by a unit
+ * only units defined in m_unitList are allowed
+ * examples: "4kByte" returns 4000, "4kibyte" returns 4096 + * @return the value of the number multiplied by the factor given by the unit + * @throws ReParserException + */ +uint64_t ReUnitParser::valueOf(const QString& value) const{ + uint64_t rc = 0; + int ix = ReQStringUtil::lengthOfUInt64(value, 0, 10, &rc); + if (ix == 0) + throw ReParserException(QObject::tr("number expected: ") + value); + QString unit = value.mid(ix); + if (! unit.isEmpty()){ + QStringList units = QString(m_unitList).split(";"); + QStringList::const_iterator it; + bool found = false; + for (it = units.begin(); it != units.end(); ++it){ + QStringList pair = it->split(":"); + if (pair.count() == 0) + throw ReParserException(QObject::tr("missing ':' in unit definition, e.g. 'k:1000': ") + *it); + if (pair.count() > 2) + throw ReParserException(QObject::tr("too many ':' in unit definition: ") + *it); + bool ok = false; + QString unit2 = *pair.begin(); + QString factor = *++pair.begin(); + uint64_t nFactor = factor.toLongLong(&ok); + if (! ok) + throw ReParserException(QObject::tr("not a number: ") + factor); + if (unit2.startsWith(unit)){ + rc *= nFactor; + found = true; + break; + } + } + if (! found) + throw ReParserException(QObject::tr("unknown unit, allowed: ") + QString(m_unitList)); + } + return rc; +} + /** * Constructor. * @@ -356,5 +561,68 @@ ReSizeParser::ReSizeParser(const QString& expr) : * @param expr an expression, e.g. "3*3days-5min+3weeks" */ ReDateTimeParser::ReDateTimeParser(const QString& expr) : - ReUnitParser(expr, "minutes:60;hours:3600;days:86400;weeks:604800"){ + ReUnitParser("", "minutes:60;hours:3600;days:86400;weeks:604800"){ + parseDateTime(expr); +} + +/** + * Returns the parser result as a QDateTime instance. + * + * @return the parse result. If invalid input the result is the begin of the epoche + */ +QDateTime ReDateTimeParser::asDateTime() const +{ + return m_dateTime; } + +/** + * Parses a date/time expression. + * + * Syntax: { "now" | [