*/
#include "base/rebase.hpp"
#include <QtCore/qmath.h>
+#include <QDateTime>
/**
* @brief Determines the length and vlaue of an integer.
*
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.
*
*/
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.
*
}
/**
- * 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.
*/
}
}
+/**
+ * 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.
*
* @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;
+}
+