QCron::next returns the next date matching the cron pattern.

This commit is contained in:
vincent 2015-12-23 10:03:39 +01:00
parent 0d57baf3f7
commit 311b6689fe
9 changed files with 921 additions and 118 deletions

@ -1,4 +1,5 @@
#include "qcron.hpp"
#include "qcronnode.hpp"
#include <cmath>
@ -87,15 +88,167 @@ _parsePattern(QString & pattern)
}
}
/******************************************************************************/
QList<EField>
getPreviousFields(EField field)
{
QList<EField> fields;
switch (field)
{
case YEAR:
fields << MONTH;
case MONTH:
fields << DOM;
case DOW:
case DOM:
fields << HOUR;
case HOUR:
fields << MINUTE;
case MINUTE:
break;
default:
qFatal("Should not be in getPreviousTimeUnit");
}
return fields;
}
/******************************************************************************/
void
QCron::
add(QDateTime & dt, EField field, int value)
{
switch (field)
{
case YEAR:
dt = dt.addYears(value);
break;
case MONTH:
dt = dt.addMonths(value);
break;
case DOW:
case DOM:
dt = dt.addDays(value);
break;
case HOUR:
dt = dt.addSecs(3600 * value);
break;
case MINUTE:
dt = dt.addSecs(60 * value);
break;
default:
qFatal("Unknown value in add");
}
QList<EField> previous_fields = getPreviousFields(field);
foreach (EField field, previous_fields)
{
_fields[field].reset(dt);
}
}
/******************************************************************************/
void
set(QDateTime & dt, EField field, int value)
{
QDate date = dt.date();
QTime time = dt.time();
switch (field)
{
case YEAR:
dt.setDate(QDate(value, date.month(), date.day()));
break;
case MONTH:
dt.setDate(QDate(date.year(), value, date.day()));
break;
case DOW:
case DOM:
dt.setDate(QDate(date.year(), date.month(), value));
break;
case HOUR:
dt.setTime(QTime(value, time.minute(), 0));
break;
case MINUTE:
dt.setTime(QTime(time.hour(), value, 0));
break;
default:
qFatal("Unknown value in add");
}
}
/******************************************************************************/
void
QCron::
catchUp(QDateTime & dt, EField field, int value)
{
int current_time_unit = _fields[field].getDateTimeSection(dt);
if (current_time_unit < value)
{
add(dt, field, value - current_time_unit);
}
else if (current_time_unit == value)
{
// Nothing
}
else if (current_time_unit > value)
{
if (YEAR == field)
{
dt = QDateTime();
}
else if (MINUTE != field)
{
int max = _fields[field].getMax();
add(dt, field, (max - current_time_unit + value));
}
}
}
/******************************************************************************/
void
QCron::
chiche(QDateTime & dt, EField field)
{
QCronNode * node = _fields[field].getRoot();
if (NULL == node)
{
qFatal("Problem");
}
node->process(this, dt, field);
}
/******************************************************************************/
QDateTime
QCron::
next(QDateTime dt)
{
for (int i = 0; i < 6; ++i)
dt = dt.addSecs(60);
while (!match(dt))
{
_fields[i].next(dt);
//qDebug() << dt << "doesn't match";
for (int i = YEAR; i >= 0; --i)
{
chiche(dt, (EField)i);
//qDebug() << dt << "after Chcihe";
if (!dt.isValid())
{
return dt;
}
while (!_fields[(EField)i].match(dt))
{
//qDebug() << dt << (EField)i << "doesn't match";
int dummy = 1;
_fields[(EField)i].applyOffset(dt, dummy);
}
}
}
return dt;
}
@ -111,3 +264,17 @@ next(int n)
}
/******************************************************************************/
bool
QCron::
match(const QDateTime & dt) const
{
bool does_match = true;
for (int i = 0; i < 6; ++i)
{
does_match &= _fields[i].match(dt);
}
return does_match;
}
/******************************************************************************/

@ -28,6 +28,10 @@ public:
QDateTime next(int n = 1);
QDateTime next(QDateTime dt);
void catchUp(QDateTime & dt, EField field, int value);
bool match(const QDateTime & dt) const;
void add(QDateTime & dt, EField field, int value);
signals:
void activated();
@ -45,7 +49,7 @@ private:
void _parseField(QString & field_str,
EField field);
QString _validCharacters(EField field);
void chiche(QDateTime & dt, EField field);
};
#endif

@ -1,4 +1,5 @@
#include "qcronfield.hpp"
#include "qcronnode.hpp"
#include <QDebug>
@ -29,7 +30,6 @@ _parseInt(QString & str)
.arg(str));
}
str.remove(0, char_idx);
//qDebug() << "Parsing int" << value;
if (value < _min || _max < value)
{
throw QCronFieldException(QString("Value %1 out of range [%2;%3]")
@ -44,7 +44,6 @@ QCronRangeNode*
QCronField::
_parseRange(QString & str)
{
// qDebug() << "Parsing a Range";
if (_last_node == NULL)
{
throw QCronFieldException(QString("Syntax error at %1: range has no beginning")
@ -77,7 +76,6 @@ QCronEveryNode*
QCronField::
_parseEvery(QString & str)
{
// qDebug() << "Parsing an Every";
str.remove(0, 1);
return new QCronEveryNode(_last_node, _parseInt(str));
}
@ -88,13 +86,15 @@ QCronListNode*
QCronField::
_parseList(QString & str)
{
// qDebug() << "Parsing a List";
QCronListNode * list = new QCronListNode();
list->nodes() << _last_node;
_last_node = list;
while (str[0] == ',')
while (!str.isEmpty())
{
if (str[0] == ',')
{
str.remove(0, 1);
}
QCronNode * node = _parseNode(str);
list->nodes() << node;
_last_node = node;
@ -108,69 +108,75 @@ QCronNode *
QCronField::
_parseNode(QString & str)
{
//qDebug() << "Parsing a node";
QCronNode * node = NULL;
QChar c = str[0];
if (c.isDigit())
{
return _parseInt(str);
node = _parseInt(str);
}
else if ("-" == c)
{
return _parseRange(str);
node = _parseRange(str);
}
else if ("/" == c)
{
return _parseEvery(str);
node = _parseEvery(str);
}
else if ("*" == c)
{
return new QCronAllNode;
str.remove(0, 1);
node = new QCronAllNode;
}
else if ("," == c)
{
return _parseList(str);
node = _parseList(str);
}
if (NULL == node)
{
throw QCronFieldException(QString("Unexpected character %1").arg(c));
}
node->setField(this);
return node;
}
/******************************************************************************/
void
QCronField::
parse(QString & str)
{
try
{
_last_node = NULL;
_root = _parseNode(str);
if (!str.isEmpty())
while (!str.isEmpty())
{
_last_node = _root;
_root = _parseNode(str);
}
_is_valid = true;
}
catch (int)
{
_is_valid = false;
}
}
/******************************************************************************/
int
QCronField::
getDateTimeSection(QDateTime & dt) const
getDateTimeSection(const QDateTime & dt) const
{
switch (_field)
{
case MINUTE: return dt.time().minute();
case HOUR: return dt.time().hour();
case DOM: return dt.date().day();
case MONTH: return dt.date().month();
case DOW: return dt.date().dayOfWeek();
case YEAR: return dt.date().year();
default: qFatal("Shouldn't be here");
case MINUTE:
return dt.time().minute();
case HOUR:
return dt.time().hour();
case DOM:
return dt.date().day();
case MONTH:
return dt.date().month();
case DOW:
return dt.date().dayOfWeek();
case YEAR:
return dt.date().year();
default:
qFatal("Shouldn't be here");
}
}
@ -178,40 +184,34 @@ getDateTimeSection(QDateTime & dt) const
void
QCronField::
applyOffset(QDateTime & dt, int offset) const
applyOffset(QDateTime & dt, int & offset) const
{
bool overflow = offset < 0 || (offset == 0 && _field == MINUTE);
offset += overflow ? 1 : 0;
switch (_field)
{
case MINUTE:
{
offset -= overflow ? 1 : 0;
offset += offset <= 0 ? 60 : 0;
dt = dt.addSecs(60 * offset);
break;
}
case HOUR:
{
dt.addSecs(3600 * offset);
dt = dt.addSecs(3600 * offset);
break;
}
case DOM:
case DOW:
{
dt.addDays(offset);
dt = dt.addDays(offset);
break;
}
case MONTH:
{
dt.addMonths(offset);
dt = dt.addMonths(offset);
break;
}
case YEAR:
{
dt.addYears(offset);
dt = dt.addYears(offset);
break;
}
default:
@ -223,11 +223,54 @@ applyOffset(QDateTime & dt, int offset) const
/******************************************************************************/
void
int
QCronField::
next(QDateTime & dt)
{
int time_section = getDateTimeSection(dt);
int offset = _root->next(time_section);
applyOffset(dt, offset);
return _root->next(time_section);
}
/******************************************************************************/
void
QCronField::
reset(QDateTime & dt)
{
int value = _min;
QDate date = dt.date();
QTime time = dt.time();
switch (_field)
{
case YEAR:
dt.setDate(QDate(value, date.month(), date.day()));
break;
case MONTH:
dt.setDate(QDate(date.year(), value, date.day()));
break;
case DOW:
case DOM:
dt.setDate(QDate(date.year(), date.month(), value));
break;
case HOUR:
dt.setTime(QTime(value, time.minute(), 0));
break;
case MINUTE:
dt.setTime(QTime(time.hour(), value, 0));
break;
default:
qFatal("Unknown value in add");
}
}
/******************************************************************************/
bool
QCronField::
match(const QDateTime & dt) const
{
return _root->match(getDateTimeSection(dt));
}
/******************************************************************************/

@ -4,7 +4,6 @@
#include <QDateTime>
#include <QList>
#include <QString>
#include "qcronnode.hpp"
enum EField
{
@ -16,6 +15,12 @@ enum EField
YEAR
};
class QCronIntNode;
class QCronRangeNode;
class QCronNode;
class QCronEveryNode;
class QCronListNode;
class QCronFieldException
{
public:
@ -47,21 +52,36 @@ public:
case DOM: _min = 1; _max = 31; break;
case MONTH: _min = 1; _max = 12; break;
case DOW: _min = 1; _max = 7 ; break;
case YEAR: _min = 2016; _max = 2099; break;
case YEAR: _min = 1; _max = 2099; break;
default: throw 42;
}
}
EField getField() const
{ return _field; }
int getMax() const
{ return _max; }
int getMin() const
{ return _min; }
// Features.
void parse(QString & str);
bool isValid() const
{ return _is_valid; }
bool match(const QDateTime & dt) const;
void next(QDateTime & dt);
QCronNode * getRoot() const
{ return _root; }
int next(QDateTime & dt);
int getDateTimeSection(const QDateTime & dt) const;
void applyOffset(QDateTime & dt, int & offset) const;
void reset(QDateTime & dt);
int getDateTimeSection(QDateTime & dt) const;
void applyOffset(QDateTime & dt, int offset) const;
private:
int _min;

@ -1,4 +1,10 @@
#include "qcronnode.hpp"
#include "qcronfield.hpp"
#include "qcron.hpp"
#include <QDebug>
#include <QDebug>
/******************************************************************************/
@ -9,6 +15,16 @@ QCronNode::
/******************************************************************************/
void
QCronNode::
setField(QCronField * field)
{
_field = field;
}
/******************************************************************************/
/******************************************************************************/
QCronIntNode::
QCronIntNode(int v)
: _value(v)
@ -35,6 +51,27 @@ next(int t) const
/******************************************************************************/
void
QCronIntNode::
process(QCron * cron,
QDateTime & dt,
EField field)
{
cron->catchUp(dt, field, _value);
}
/******************************************************************************/
bool
QCronIntNode::
match(int tu) const
{
return tu == _value;
}
/******************************************************************************/
/******************************************************************************/
int
QCronStrNode::
next(int t) const
@ -44,16 +81,61 @@ next(int t) const
/******************************************************************************/
void
QCronStrNode::
process(QCron *,
QDateTime &,
EField )
{
// TODO
}
/******************************************************************************/
bool
QCronStrNode::
match(int tu) const
{
return tu;
}
/******************************************************************************/
/******************************************************************************/
int
QCronAllNode::
next(int t) const
{
Q_UNUSED(t);
return 1;
return 0;
}
/******************************************************************************/
void
QCronAllNode::
process(QCron * cron,
QDateTime & dt,
EField field)
{
Q_UNUSED(cron);
Q_UNUSED(dt);
Q_UNUSED(field);
}
/******************************************************************************/
bool
QCronAllNode::
match(int tu) const
{
Q_UNUSED(tu);
return true;
}
/******************************************************************************/
/******************************************************************************/
QCronRangeNode::
QCronRangeNode(const QCronIntNode * begin,
const QCronIntNode * end)
@ -64,19 +146,71 @@ QCronRangeNode(const QCronIntNode * begin,
/******************************************************************************/
void
QCronRangeNode::
process(QCron * cron,
QDateTime & dt,
EField field)
{
int begin = beginValue();
int end = endValue();
int current_time_unit = _field->getDateTimeSection(dt);
if (current_time_unit < begin || end < current_time_unit)
{
cron->catchUp(dt, field, begin);
}
else
{
if (field == MINUTE)
{
cron->add(dt, field, 1);
}
}
}
/******************************************************************************/
int
QCronRangeNode::
beginValue() const
{
return _begin->value();
}
/******************************************************************************/
int
QCronRangeNode::
endValue() const
{
return _end->value();
}
/******************************************************************************/
int
QCronRangeNode::
next(int t) const
{
if (_begin->value() <= t && t < _end->value())
if (_begin->value() <= t && t <= _end->value())
{
return 0;
return 1;
}
return _begin->value() - t;
}
/******************************************************************************/
bool
QCronRangeNode::
match(int tu) const
{
return _begin->value() <= tu && tu <= _end->value();
}
/******************************************************************************/
/******************************************************************************/
QCronEveryNode::
QCronEveryNode(QCronNode * what,
QCronIntNode * freq)
@ -85,27 +219,82 @@ QCronEveryNode(QCronNode * what,
{
}
/******************************************************************************/
void
QCronEveryNode::
process(QCron * cron ,
QDateTime & dt,
EField field)
{
int freq = _freq->value();
_what->process(cron, dt, field);
while (_field->getDateTimeSection(dt) % freq)
{
cron->add(dt, field, 1);
_what->process(cron, dt, field);
}
}
/******************************************************************************/
int
QCronEveryNode::
next(int t) const
{
int next = _what->next(t) % _freq->value();
int what_next = _what->next(t);
if (what_next == 1)
{
/* what_next == 1 if we're in the right range AND if we're one
* time unit ahead of it */
int next = t % _freq->value();
return _freq->value() - next;
}
return what_next;
}
/******************************************************************************/
QList<QCronNode *>
bool
QCronEveryNode::
match(int tu) const
{
return _what->match(tu) && tu % _freq->value() == 0;
}
/******************************************************************************/
/******************************************************************************/
QList<QCronNode *> &
QCronListNode::
nodes() const
nodes()
{
return _nodes;
}
/******************************************************************************/
void
QCronListNode::
process(QCron * cron,
QDateTime & dt,
EField field)
{
foreach (QCronNode * node, _nodes)
{
int node_next = node->next(_field->getDateTimeSection(dt));
if (node_next >= 0)
{
node->process(cron, dt, field);
return;
}
}
_nodes[0]->process(cron, dt, field);
}
/******************************************************************************/
int
QCronListNode::
next(int t) const
@ -113,7 +302,7 @@ next(int t) const
foreach (const QCronNode * node, _nodes)
{
int node_next = node->next(t);
if (t > 0)
if (node_next > 0 && node_next != _field->getMax() + 1)
{
return node_next;
}
@ -122,3 +311,20 @@ next(int t) const
}
/******************************************************************************/
bool
QCronListNode::
match(int tu) const
{
foreach (const QCronNode * node, _nodes)
{
if (node->match(tu))
{
return true;
}
}
return false;
}
/******************************************************************************/
/******************************************************************************/

@ -4,12 +4,27 @@
#include <QList>
#include <QtGlobal>
#include "qcronfield.hpp"
class QCron;
class QCronField;
class QCronNode
{
public:
virtual ~QCronNode();
void setField(QCronField * field);
virtual int next(int t) const = 0;
virtual bool match(int tu) const = 0;
virtual void process(QCron * cron,
QDateTime & dt,
EField field) = 0;
protected:
QCronField * _field;
};
class QCronValueNode : public QCronNode
@ -24,6 +39,11 @@ public:
int value() const;
virtual int next(int t) const Q_DECL_OVERRIDE;
virtual bool match(int tu) const Q_DECL_OVERRIDE;
virtual void process(QCron * cron,
QDateTime & dt,
EField field) Q_DECL_OVERRIDE;
private:
int _value;
@ -33,12 +53,20 @@ class QCronStrNode : public QCronValueNode
{
public:
virtual int next(int t) const Q_DECL_OVERRIDE;
virtual bool match(int tu) const Q_DECL_OVERRIDE;
virtual void process(QCron * cron,
QDateTime & dt,
EField field) Q_DECL_OVERRIDE;
};
class QCronAllNode : public QCronValueNode
{
public:
virtual int next(int t) const Q_DECL_OVERRIDE;
virtual bool match(int tu) const Q_DECL_OVERRIDE;
virtual void process(QCron * cron,
QDateTime & dt,
EField field) Q_DECL_OVERRIDE;
};
class QCronRangeNode : public QCronNode
@ -47,7 +75,15 @@ public:
QCronRangeNode(const QCronIntNode * begin,
const QCronIntNode * end);
int beginValue() const;
int endValue() const;
virtual int next(int t) const Q_DECL_OVERRIDE;
virtual bool match(int tu) const Q_DECL_OVERRIDE;
virtual void process(QCron * cron,
QDateTime & dt,
EField field) Q_DECL_OVERRIDE;
private:
const QCronIntNode * _begin;
@ -60,6 +96,11 @@ public:
QCronEveryNode(QCronNode *, QCronIntNode *);
virtual int next(int t) const Q_DECL_OVERRIDE;
virtual bool match(int tu) const Q_DECL_OVERRIDE;
virtual void process(QCron * cron,
QDateTime & dt,
EField field) Q_DECL_OVERRIDE;
private:
QCronNode * _what;
@ -69,9 +110,13 @@ private:
class QCronListNode : public QCronNode
{
public:
QList<QCronNode *> nodes() const;
QList<QCronNode *> & nodes();
virtual int next(int t) const Q_DECL_OVERRIDE;
virtual bool match(int tu) const Q_DECL_OVERRIDE;
virtual void process(QCron * cron,
QDateTime & dt,
EField field) Q_DECL_OVERRIDE;
private:
QList<QCronNode*> _nodes;

@ -11,7 +11,7 @@ void
QCronTest::
init()
{
_dnow = QDate::currentDate();
_dnow = QDate(1, 1, 1);
_tnow = QTime(0, 0, 0);
}
@ -28,29 +28,338 @@ actual(QString & pattern)
/******************************************************************************/
void
QCronTest::
minutes()
{
// Star
QString pattern = "* * * * * *";
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
// Int
pattern = "30 * * * * *";
QCOMPARE(actual(pattern), now().addSecs(60 * 30));
_tnow.setHMS(0, 31, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 59));
_tnow.setHMS(0, 30, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 60));
pattern = "0 * * * * *";
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 60));
_tnow.setHMS(0, 31, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 29));
_tnow.setHMS(0, 59, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
pattern = "59 * * * * *";
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 59));
_tnow.setHMS(0, 58, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 59, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 60));
// Range
pattern = "10-20 * * * * *";
_tnow.setHMS(0, 5, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 5));
_tnow.setHMS(0, 9, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 10, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 15, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 20, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 50));
_tnow.setHMS(0, 30, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 40));
// Every - easy
pattern = "*/5 * * * * *";
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 5));
_tnow.setHMS(0, 4, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 5, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 5));
_tnow.setHMS(0, 16, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 4));
_tnow.setHMS(0, 54, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 57, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 3));
// Every - medium
pattern = "14-28/2 * * * * *";
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 14));
_tnow.setHMS(0, 13, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 14, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 2));
_tnow.setHMS(0, 15, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 16, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 2));
_tnow.setHMS(0, 28, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 46));
// List - easy
pattern = "10,15,16 * * * * *";
_tnow.setHMS(0, 5, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 5));
_tnow.setHMS(0, 9, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 10, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 5));
_tnow.setHMS(0, 14, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 15, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 16, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 54));
_tnow.setHMS(0, 17, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 53));
// List - medium
pattern = "10,15-20,30 * * * * *";
_tnow.setHMS(0, 5, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 5));
_tnow.setHMS(0, 9, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 10, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 5));
_tnow.setHMS(0, 15, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 19, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(0, 20, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 10));
_tnow.setHMS(0, 30, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 40));
}
/******************************************************************************/
QDateTime
QCronTest::
expected(int offset)
now()
{
QDateTime exp(_dnow, _tnow);
return exp.addSecs(offset * 60);
return QDateTime(_dnow, _tnow);
}
/******************************************************************************/
void
QCronTest::
years()
{
// Int
QString pattern = "* * * * * 30";
_dnow.setDate(15, 1, 2);
QCOMPARE(actual(pattern), now().addYears(15).addDays(-1));
_dnow.setDate(31, 1, 1);
QCOMPARE(actual(pattern), QDateTime());
_dnow.setDate(30, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(30, 12, 31);
_tnow.setHMS(23, 59, 0);
QCOMPARE(actual(pattern), QDateTime());
pattern = "* * * 6 * 30";
_dnow.setDate(15, 1, 1);
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), QDateTime(QDate(30, 6, 1), _tnow));
_dnow.setDate(31, 1, 1);
QCOMPARE(actual(pattern), QDateTime());
_dnow.setDate(30, 6, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
// Range
pattern = "* * * * * 10-20";
_dnow.setDate(5, 1, 1);
QCOMPARE(actual(pattern), now().addYears(5));
_dnow.setDate(9, 1, 1);
QCOMPARE(actual(pattern), now().addYears(1));
_dnow.setDate(10, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(15, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(20, 12, 31);
_tnow.setHMS(23, 59, 0);
QCOMPARE(actual(pattern), QDateTime());
_dnow.setDate(20, 12, 31);
QCOMPARE(actual(pattern), QDateTime());
// Every - easy
pattern = "* * * * * */5";
_dnow.setDate(1, 1, 1);
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addYears(4));
_dnow.setDate(4, 1, 1);
QCOMPARE(actual(pattern), now().addYears(1));
_dnow.setDate(5, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(16, 1, 1);
QCOMPARE(actual(pattern), now().addYears(4));
_dnow.setDate(54, 1, 1);
QCOMPARE(actual(pattern), now().addYears(1));
_dnow.setDate(57, 1, 1);
QCOMPARE(actual(pattern), now().addYears(3));
// Every - medium
pattern = "* * * * * 14-28/2";
_dnow.setDate(1, 1, 1);
QCOMPARE(actual(pattern), now().addYears(13));
_dnow.setDate(13, 1, 1);
QCOMPARE(actual(pattern), now().addYears(1));
_dnow.setDate(14, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(15, 1, 1);
QCOMPARE(actual(pattern), now().addYears(1));
_dnow.setDate(16, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(28, 12, 31);
QCOMPARE(actual(pattern), now().addSecs(60));
_tnow.setHMS(23, 59, 0);
QCOMPARE(actual(pattern), QDateTime());
// List - easy
pattern = "* * * * * 10,15,16";
_dnow.setDate(5, 1, 1);
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addYears(5));
_dnow.setDate(9, 1, 1);
QCOMPARE(actual(pattern), now().addYears(1));
_dnow.setDate(10, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(14, 1, 1);
QCOMPARE(actual(pattern), now().addYears(1));
_dnow.setDate(15, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(16, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(16, 12, 31);
_tnow.setHMS(23, 59, 00);
QCOMPARE(actual(pattern), QDateTime());
// List - medium
pattern = "* * * * * 10,15-20,30";
_dnow.setDate(5, 1, 1);
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addYears(5));
_dnow.setDate(9, 1, 1);
QCOMPARE(actual(pattern), now().addYears(1));
_dnow.setDate(10, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(15, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(20, 1, 1);
QCOMPARE(actual(pattern), now().addSecs(60));
_dnow.setDate(20, 12, 31);
_tnow.setHMS(23, 59, 0);
QCOMPARE(actual(pattern), now().addSecs(60).addYears(9));
// Star
pattern = "* * * * * *";
_dnow.setDate(1, 1, 1);
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addSecs(60));
}
/******************************************************************************/
void
QCronTest::
minutes()
hours()
{
QString pattern = "* * * * * *";
QCOMPARE(actual(pattern), expected(1));
// Int
QString pattern = "* 1 * * * *";
_dnow.setDate(2016, 1, 1);
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 60));
_tnow.setHMS(0, 59, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(1, 0, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(1, 58, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 1));
_tnow.setHMS(1, 59, 0);
QCOMPARE(actual(pattern), now().addDays(1).addSecs(-60 * 59));
pattern = "30 1,3 * * * *";
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 90));
_tnow.setHMS(1, 30, 0);
QCOMPARE(actual(pattern), now().addSecs(60 * 120));
_tnow.setHMS(3, 30, 0);
QCOMPARE(actual(pattern), now().addDays(1).addSecs(-120 * 60));
pattern = "* * * * * 2017";
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addYears(1));
pattern = "* 2 * * * 2017";
QCOMPARE(actual(pattern), now().addYears(1).addSecs(60 * 120));
}
/******************************************************************************/
void
QCronTest::
dow()
{
// 1 = Mon; 7 = Sun
QString pattern = "* * * * 1 *"; // Mon
_dnow.setDate(2016, 1, 1); // Fri
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addDays(3));
pattern = "* * * * 5 *"; // Fri
QCOMPARE(actual(pattern), now().addSecs(60));
_tnow.setHMS(23, 59, 59);
QCOMPARE(actual(pattern), now().addSecs(1).addDays(6));
pattern = "* * * * 3 *"; // Wed
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(pattern), now().addDays(5));
}
/******************************************************************************/
void
QCronTest::
realLife()
{
/* From monday to friday, from 8:00 to 11:59 and from 14:00 to 17:59. */
QString working_hours = "* 8-11,14-17 * * 1-5 *";
_dnow.setDate(2016, 1, 4); // Mon
_tnow.setHMS(0, 0, 0);
QCOMPARE(actual(working_hours), now().addSecs(8 * 3600));
_tnow.setHMS(7, 59, 59);
QCOMPARE(actual(working_hours), now().addSecs(60));
_tnow.setHMS(8, 0, 0);
QCOMPARE(actual(working_hours), now().addSecs(60));
_tnow.setHMS(11, 58, 0);
QCOMPARE(actual(working_hours), now().addSecs(60));
_tnow.setHMS(11, 59, 0);
QCOMPARE(actual(working_hours), now().addSecs(60 + 3600 * 2));
_tnow.setHMS(13, 59, 0);
QCOMPARE(actual(working_hours), now().addSecs(60));
_tnow.setHMS(14, 0, 0);
QCOMPARE(actual(working_hours), now().addSecs(60));
_tnow.setHMS(17, 58, 0);
QCOMPARE(actual(working_hours), now().addSecs(60));
_tnow.setHMS(17, 59, 0);
QCOMPARE(actual(working_hours), now().addSecs(60 + 14 * 3600));
_dnow.setDate(2016, 1, 8); // Fri
QCOMPARE(actual(working_hours), now().addSecs(60 + 14 * 3600).addDays(2));
pattern = "30 * * * * *";
QCOMPARE(actual(pattern), expected(30));
_tnow.setHMS(0, 31, 0);
QCOMPARE(actual(pattern), expected(59));
_tnow.setHMS(0, 30, 0);
QCOMPARE(actual(pattern), expected(60));
}
/******************************************************************************/

@ -10,14 +10,18 @@ class QCronTest : public QObject
private slots:
void init();
void years();
void dow();
void hours();
void minutes();
void realLife();
private:
QDate _dnow;
QTime _tnow;
QDateTime actual(QString & pattern);
QDateTime expected(int offset);
QDateTime now();
};

@ -47,7 +47,12 @@ buildTests(QStringList & good,
buildPattern(pattern_idx, QString("%1,%2,").arg(min).arg(min + 1)) <<
buildPattern(pattern_idx, "1,a") <<
buildPattern(pattern_idx, QString("%1,%2").arg(min).arg(max + 1)) <<
buildPattern(pattern_idx, QString("%1,%2").arg(min).arg(min - 1));
buildPattern(pattern_idx, QString("%1,%2").arg(min).arg(min - 1)) <<
buildPattern(pattern_idx, "1/2/3 * * * * *") <<
buildPattern(pattern_idx, "1-2-3 * * * * *") << // Bad range
buildPattern(pattern_idx, "** * * * * *") << // Bad star
buildPattern(pattern_idx, "1,,2 * * * * *") // Bad list
;
good << buildPattern(pattern_idx, QString::number(min)) <<
buildPattern(pattern_idx, QString("000000000%1").arg(min)) <<
buildPattern(pattern_idx, QString::number(max)) <<
@ -142,7 +147,7 @@ years()
{
QStringList good;
QStringList bad;
buildTests(good, bad, 5, 2016, 2099);
buildTests(good, bad, 5, 1, 2099);
doTest(good, bad);
}
@ -160,7 +165,7 @@ doTest(QStringList good, QStringList bad)
}
foreach (QString s, bad)
{
// qDebug() << s;
qDebug() << s;
QCron cron(s);
QVERIFY(!cron.isValid());
}