]> gitweb.hamatoma.de Git - reqt/commitdiff
extended date/time parser, refind
authorhama <hama@siduction.net>
Mon, 6 Apr 2015 21:27:39 +0000 (23:27 +0200)
committerhama <hama@siduction.net>
Mon, 6 Apr 2015 21:27:39 +0000 (23:27 +0200)
base/ReQStringUtil.cpp
base/ReQStringUtil.hpp
base/rebase.hpp

index 7dd4c06db907c8483b9a8d24565b6be72cac0ea6..9d52556cb77fcda652b614ea10da1f3e2f82f79a 100644 (file)
@@ -18,6 +18,7 @@
  */
 #include "base/rebase.hpp"
 #include <QtCore/qmath.h>
+#include <QDateTime>
 /**
  * @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<br>
+ *                  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 <code>text</code> to inspect
+ * @param value OUT: the value of the found date. Not changed if result is 0.<br>
+ *              May be NULL
+ * @return      0: no date found<br>
+ *              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 <code>text</code> to inspect
+ * @param allowDateOnly <code>false</code>: if the date is not followed by
+ *                      a time the result will be 0
+ * @param allowTimeOnly <code>false</code>: 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.<br>
+ *                      May be NULL
+ * @return              0: no date found<br>
+ *                      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 <code>text</code> to inspect
+ * @param value OUT: the value of the found time. Not changed if result is 0.<br>
+ *              May be NULL
+ * @return  0: no date found<br>
+ *          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  <code>true</code>: 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<br>
- *          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<br>
- *                only units defined in m_unitList are allowed<br>
- *                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<br>
+ *          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  <code>true</code>: 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<br>
+ *                only units defined in m_unitList are allowed<br>
+ *                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 <code>QDateTime</code> 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" | <date> [<time>] | <time> } [ { '+' | '-' } <second expr>]
+ *          | <second expr>
+ * @param expr  expression to parse, e.g. "now+3weeks-5*30days"
+ * @return      start of the epoche: no valid input<br>
+ *              othewise: the calculated date/time
+ */
+QDateTime ReDateTimeParser::parseDateTime(const QString& expr){
+    m_expr = expr;
+    normalize();
+    QDateTime rc = QDateTime::currentDateTime();
+    bool olderThan = true;
+    int64_t relativeSeconds = 0;
+    if (m_expr.isEmpty())
+        m_message = QObject::tr("empty string is not a date/time");
+    else {
+        QDateTime dateTime;
+        int length2 = 0;
+        bool checkSum = false;
+        if (m_expr.startsWith("now", Qt::CaseInsensitive)){
+            m_expr.remove(0, 3);
+            checkSum = true;
+        } else if ( (length2 = ReQStringUtil::lengthOfDateTime(m_expr, 0, true, true, &dateTime)) > 0){
+            rc = dateTime;
+            m_expr.remove(0, length2);
+        } else {
+            parse();
+            relativeSeconds = m_result;
+        }
+        if (checkSum){
+            if (m_expr.startsWith("+")){
+                olderThan = false;
+                m_expr.remove(0, 1);
+            }
+            parse();
+            relativeSeconds = m_result;
+        }
+    }
+    if (! isValid())
+        rc.setMSecsSinceEpoch(0);
+    else {
+        if (! olderThan)
+            relativeSeconds = -relativeSeconds;
+        rc.addSecs(relativeSeconds);
+    }
+    m_dateTime = rc;
+    return rc;
+}
+
index 7af38e9a1500304d4b2fc3f0bea27d467a9599f1..1b7ccc22cbebabe325514a0803ce296930a385a3 100644 (file)
 
 class ReQStringUtil {
 public:
+    static int lengthOfDate(const ReString& text, int start = 0, QDate* value =
+            NULL);
+    static int lengthOfDateTime(const ReString& text, int start = 0,
+            bool allowDateOnly = true, bool allowTimeOnly = true,
+            QDateTime* value = NULL);
+    static int lengthOfReal(const ReString& text, int start = 0, qreal* value =
+       NULL);
+    static int lengthOfTime(const ReString& text, int start = 0, QTime* value =
+            NULL);
    static int lengthOfUInt64(const ReString& text, int start = 0,
       int radix = 10, uint64_t* value = NULL);
    static int lengthOfUInt(const ReString& text, int start, int radix,
       uint* pValue);
-   static int lengthOfReal(const ReString& text, int start = 0, qreal* value =
-      NULL);
+   static void skipExpected(const ReString& text, QChar expected, int& index, int length);
    /**
     * @brief Returns the value of a hexadecimal digit.
     *
@@ -44,10 +52,11 @@ public:
    int64_t asInt64(int64_t defaultValue = -1ll);
    int asInt(int defaultValue = -1);
    real_t asReal(real_t defaultValue = -1.0);
-private:
+protected:
    void parse();
+   void normalize();
    uint64_t valueOf(const QString& value) const;
-private:
+protected:
    int64_t m_result;
    QString m_expr;
    QString m_message;
@@ -62,6 +71,11 @@ public:
 class ReDateTimeParser : public ReUnitParser {
 public:
     ReDateTimeParser(const QString& expr);
+public:
+    QDateTime parseDateTime(const QString& expr);
+    QDateTime asDateTime() const;
+private:
+    QDateTime m_dateTime;
 };
 
 #endif // RPLQSTRING_HPP
index c8f0d852f5c0329b1222ebe60f7da21465210571..cede8e38e3a0f3026e2f5d1ad6cb4f902340268d 100644 (file)
@@ -30,6 +30,7 @@
 #include <QRegularExpression>
 #include <QtCore/qmath.h>
 #include <QTranslator>
+#include <QDateTime>
 typedef unsigned char uint8_t;
 //typedef qint64 int64_t;
 typedef quint64 uint64_t;