Ref T261, timestamp utility functions

This commit is contained in:
Klaus Basan
2018-04-27 02:11:15 +02:00
committed by Roland Winklmeier
parent d390116666
commit d1f5635bd1
4 changed files with 192 additions and 28 deletions

View File

@@ -335,7 +335,7 @@ namespace BlackMisc
switch (i)
{
case IndexOffsetMs: { return QVariant::fromValue(m_timeOffsetMs); }
case IndexAdjustedMisWithOffset: { return QVariant::fromValue(this->getAdjustedMSecsSinceEpoch()); }
case IndexAdjustedMsWithOffset: { return QVariant::fromValue(this->getAdjustedMSecsSinceEpoch()); }
case IndexOffsetWithUnit: { return QVariant::fromValue(this->getTimeOffsetWithUnit()); }
default: break;
}

View File

@@ -154,7 +154,7 @@ namespace BlackMisc
enum ColumnIndex
{
IndexOffsetMs = CPropertyIndex::GlobalIndexITimestampBased + ITimestampBased::IndexMSecsSinceEpoch + 1,
IndexAdjustedMisWithOffset,
IndexAdjustedMsWithOffset,
IndexOffsetWithUnit // keep this as last item
};

View File

@@ -28,6 +28,8 @@
#include "blackmisc/predicates.h"
#include "blackmisc/identifierlist.h"
#include "blackmisc/countrylist.h"
#include "blackmisc/verify.h"
#include "blackconfig/buildconfig.h"
#include <QDateTime>
#include <algorithm>
@@ -35,6 +37,8 @@
#include <iterator>
#include <type_traits>
using namespace BlackConfig;
namespace BlackMisc
{
template <class OBJ, class CONTAINER>
@@ -64,6 +68,14 @@ namespace BlackMisc
});
}
template<class OBJ, class CONTAINER>
OBJ ITimestampObjectList<OBJ, CONTAINER>::findObjectBeforeOrDefault(qint64 msSinceEpoch) const
{
const CONTAINER before = this->findBefore(msSinceEpoch);
if (before.isEmpty()) { return OBJ(); }
return before.latestObject();
}
template <class OBJ, class CONTAINER>
CONTAINER ITimestampObjectList<OBJ, CONTAINER>::findBeforeAndRemove(qint64 msSinceEpoch)
{
@@ -93,6 +105,14 @@ namespace BlackMisc
});
}
template<class OBJ, class CONTAINER>
OBJ ITimestampObjectList<OBJ, CONTAINER>::findObjectAfterOrDefault(qint64 msSinceEpoch) const
{
const CONTAINER after = this->findAfter(msSinceEpoch);
if (after.isEmpty()) { return OBJ(); }
return after.oldestObject();
}
template <class OBJ, class CONTAINER>
CONTAINER ITimestampObjectList<OBJ, CONTAINER>::findInvalidTimestamps() const
{
@@ -179,7 +199,7 @@ namespace BlackMisc
OBJ ITimestampObjectList<OBJ, CONTAINER>::latestObject() const
{
if (this->container().isEmpty()) { return OBJ(); }
const auto latest = std::max_element(container().begin(), container().end(), [](const OBJ & a, const OBJ & b) { return a.getMSecsSinceEpoch() < b.getMSecsSinceEpoch(); });
const auto latest = std::max_element(this->container().begin(), this->container().end(), [](const OBJ & a, const OBJ & b) { return a.getMSecsSinceEpoch() < b.getMSecsSinceEpoch(); });
return *latest;
}
@@ -187,7 +207,7 @@ namespace BlackMisc
OBJ ITimestampObjectList<OBJ, CONTAINER>::oldestObject() const
{
if (this->container().isEmpty()) { return OBJ(); }
const auto oldest = std::min_element(container().begin(), container().end(), [](const OBJ & a, const OBJ & b) { return a.getMSecsSinceEpoch() < b.getMSecsSinceEpoch(); });
const auto oldest = std::min_element(this->container().begin(), this->container().end(), [](const OBJ & a, const OBJ & b) { return a.getMSecsSinceEpoch() < b.getMSecsSinceEpoch(); });
return *oldest;
}
@@ -238,11 +258,9 @@ namespace BlackMisc
template<class OBJ, class CONTAINER>
void ITimestampObjectList<OBJ, CONTAINER>::push_frontKeepLatestFirst(const OBJ &value, int maxElements)
{
Q_ASSERT_X(maxElements < 0 || maxElements > 1, Q_FUNC_INFO, "Max.value wrong range");
CONTAINER &c = this->container();
if (maxElements > 0 && c.size() >= maxElements)
{
c.truncate(maxElements - 1);
}
if (maxElements > 0) { c.truncate(maxElements - 1); }
const bool needSort = !c.isEmpty() && value.isOlderThan(c.front());
c.push_front(value);
if (needSort)
@@ -296,6 +314,23 @@ namespace BlackMisc
this->container().reverse();
}
template<class OBJ, class CONTAINER>
CONTAINER ITimestampWithOffsetObjectList<OBJ, CONTAINER>::getSortedAdjustedLatestFirst() const
{
CONTAINER copy(this->container());
copy.sortAdjustedLatestFirst();
return copy;
}
template<class OBJ, class CONTAINER>
CONTAINER ITimestampWithOffsetObjectList<OBJ, CONTAINER>::getLatestAdjustedTwoObjects(bool alreadySortedLatestFirst) const
{
if (this->container().size() < 2) { return CONTAINER(); }
CONTAINER copy(alreadySortedLatestFirst ? this->container() : this->container().getSortedAdjustedLatestFirst());
copy.truncate(2);
return copy;
}
template<class OBJ, class CONTAINER>
void ITimestampWithOffsetObjectList<OBJ, CONTAINER>::sortAdjustedOldestFirst()
{
@@ -342,10 +377,7 @@ namespace BlackMisc
{
Q_ASSERT_X(maxElements < 0 || maxElements > 1, Q_FUNC_INFO, "Max.value wrong range");
CONTAINER &c = this->container();
if (maxElements > 0 && c.size() >= maxElements)
{
c.truncate(maxElements - 1);
}
if (maxElements > 0) { c.truncate(maxElements - 1); }
const bool needSort = !c.isEmpty() && value.isOlderThanAdjusted(c.front());
c.push_front(value);
if (needSort)
@@ -361,27 +393,37 @@ namespace BlackMisc
// now sorted by timestamp
// this reflects normally the incoming order
CONTAINER &c = this->container();
if (c.size() < 2) { return; }
const ITimestampWithOffsetBased &front = c.front();
ITimestampWithOffsetBased &second = c[1];
if (!front.isOlderThanAdjusted(second)) { return; }
const qint64 maxOs = front.getAdjustedMSecsSinceEpoch() - second.getMSecsSinceEpoch();
const qint64 avgOs = (maxOs + qMax(front.getTimeOffsetMs(), maxOs)) / 2;
second.setTimeOffsetMs(avgOs);
// ts
// 8: os 2 adj 10
// 6: os 2 adj 8
// 5: os 5 adj 10 => max os 3
// 0: os 5 adj 5
CONTAINER &c = this->container();
if (c.size() < 2) { return; }
const ITimestampWithOffsetBased &front = c.front();
ITimestampWithOffsetBased &second = c[1];
if (front.isNewerThanAdjusted(second)) { return; }
const qint64 diffOs = front.getAdjustedMSecsSinceEpoch() - second.getMSecsSinceEpoch() - 1;
const qint64 avgOs = (diffOs + qMin(front.getTimeOffsetMs(), diffOs)) / 2;
second.setTimeOffsetMs(avgOs);
if (CBuildConfig::isLocalDeveloperDebugBuild())
{
Q_ASSERT_X(front.isNewerThanAdjusted(second), Q_FUNC_INFO, "Front/second timestamp");
Q_ASSERT_X(this->isSortedAdjustedLatestFirst(), Q_FUNC_INFO, "Wrong sort order");
}
}
template<class OBJ, class CONTAINER>
void ITimestampWithOffsetObjectList<OBJ, CONTAINER>::prefillLatestAdjustedFirst(const OBJ &value, int elements, qint64 deltaTimeMs)
{
this->container().clear();
const qint64 os = -1 * qAbs(deltaTimeMs < 0 ? value.getTimeOffsetMs() : deltaTimeMs);
const qint64 osTime = value.getTimeOffsetMs();
const qint64 os = -1 * qAbs(deltaTimeMs < 0 ? osTime : deltaTimeMs);
if (CBuildConfig::isLocalDeveloperDebugBuild())
{
BLACK_VERIFY_X(os < 0, Q_FUNC_INFO, "Need negative offset time to prefill time");
}
this->container().push_front(value);
for (int i = 1; i < elements; i++)
{
@@ -394,6 +436,7 @@ namespace BlackMisc
template<class OBJ, class CONTAINER>
bool ITimestampWithOffsetObjectList<OBJ, CONTAINER>::isSortedAdjustedLatestLast() const
{
if (this->container().isEmpty()) { return false; }
if (this->container().size() < 2) { return true; }
qint64 max = -1;
for (const ITimestampWithOffsetBased &obj : this->container())
@@ -408,6 +451,7 @@ namespace BlackMisc
template<class OBJ, class CONTAINER>
bool ITimestampWithOffsetObjectList<OBJ, CONTAINER>::isSortedAdjustedLatestFirst() const
{
if (this->container().isEmpty()) { return false; }
if (this->container().size() < 2) { return true; }
qint64 min = std::numeric_limits <qint64>::max();
for (const ITimestampWithOffsetBased &obj : this->container())
@@ -419,6 +463,40 @@ namespace BlackMisc
return true;
}
template<class OBJ, class CONTAINER>
CONTAINER ITimestampWithOffsetObjectList<OBJ, CONTAINER>::findAfterAdjusted(qint64 msSinceEpoch) const
{
return this->container().findBy([&](const ITimestampWithOffsetBased & obj)
{
return obj.isNewerThanAdjusted(msSinceEpoch);
});
}
template<class OBJ, class CONTAINER>
OBJ ITimestampWithOffsetObjectList<OBJ, CONTAINER>::findObjectAfterAdjustedOrDefault(qint64 msSinceEpoch) const
{
const CONTAINER after = this->findAfterAdjusted(msSinceEpoch);
if (after.isEmpty()) { return OBJ(); }
return after.oldestAdjustedObject();
}
template<class OBJ, class CONTAINER>
CONTAINER ITimestampWithOffsetObjectList<OBJ, CONTAINER>::findBeforeAdjusted(qint64 msSinceEpoch) const
{
return this->container().findBy([&](const ITimestampWithOffsetBased & obj)
{
return obj.isOlderThanAdjusted(msSinceEpoch);
});
}
template<class OBJ, class CONTAINER>
OBJ ITimestampWithOffsetObjectList<OBJ, CONTAINER>::findObjectBeforeAdjustedOrDefault(qint64 msSinceEpoch) const
{
const CONTAINER before = this->findBeforeAdjusted(msSinceEpoch);
if (before.isEmpty()) { return OBJ(); }
return before.latestAdjustedObject();
}
template<class OBJ, class CONTAINER>
OBJ ITimestampWithOffsetObjectList<OBJ, CONTAINER>::findClosestTimeDistanceAdjusted(qint64 msSinceEpoch) const
{
@@ -430,6 +508,50 @@ namespace BlackMisc
return *closest;
}
template <class OBJ, class CONTAINER>
OBJ ITimestampWithOffsetObjectList<OBJ, CONTAINER>::latestAdjustedObject() const
{
if (this->container().isEmpty()) { return OBJ(); }
const auto latest = std::max_element(this->container().begin(), this->container().end(), [](const OBJ & a, const OBJ & b) { return a.getAdjustedMSecsSinceEpoch() < b.getAdjustedMSecsSinceEpoch(); });
return *latest;
}
template <class OBJ, class CONTAINER>
OBJ ITimestampWithOffsetObjectList<OBJ, CONTAINER>::oldestAdjustedObject() const
{
if (this->container().isEmpty()) { return OBJ(); }
const auto oldest = std::min_element(this->container().begin(), this->container().end(), [](const OBJ & a, const OBJ & b) { return a.getAdjustedMSecsSinceEpoch() < b.getAdjustedMSecsSinceEpoch(); });
return *oldest;
}
template <class OBJ, class CONTAINER>
QDateTime ITimestampWithOffsetObjectList<OBJ, CONTAINER>::latestAdjustedTimestamp() const
{
if (this->container().isEmpty()) { return QDateTime(); }
return this->latestAdjustedObject().getUtcTimestamp();
}
template <class OBJ, class CONTAINER>
qint64 ITimestampWithOffsetObjectList<OBJ, CONTAINER>::latestAdjustedTimestampMsecsSinceEpoch() const
{
const QDateTime dt(this->latestTimestamp());
return dt.isValid() ? dt.toMSecsSinceEpoch() : -1;
}
template <class OBJ, class CONTAINER>
QDateTime ITimestampWithOffsetObjectList<OBJ, CONTAINER>::oldestAdjustedTimestamp() const
{
if (this->container().isEmpty()) { return QDateTime(); }
return this->oldestAdjustedObject().getUtcTimestamp();
}
template <class OBJ, class CONTAINER>
qint64 ITimestampWithOffsetObjectList<OBJ, CONTAINER>::oldestAdjustedTimestampMsecsSinceEpoch() const
{
const QDateTime dt(oldestAdjustedTimestamp());
return dt.isValid() ? dt.toMSecsSinceEpoch() : -1;
}
// see here for the reason of thess forward instantiations
// https://isocpp.org/wiki/faq/templates#separate-template-fn-defn-from-decl
//! \cond PRIVATE

View File

@@ -26,24 +26,30 @@ namespace BlackMisc
template<class OBJ, class CONTAINER> class ITimestampObjectList
{
public:
//! List of objects before dateTime
//! List of objects before dateTime (older)
CONTAINER findBefore(const QDateTime &dateTime) const;
//! List of objects before msSinceEpoch
//! List of objects before msSinceEpoch (older)
CONTAINER findBefore(qint64 msSinceEpoch) const;
//! Object before timestamp or default (older)
OBJ findObjectBeforeOrDefault(qint64 msSinceEpoch) const;
//! Get objects before msSinceEpoch and remove those
CONTAINER findBeforeAndRemove(qint64 msSinceEpoch);
//! List of objects before now - offset
CONTAINER findBeforeNowMinusOffset(qint64 msOffset) const;
//! List of objects after dateTime
//! List of objects after dateTime (newer)
CONTAINER findAfter(const QDateTime &dateTime) const;
//! List of objects after msSinceEpoch
//! List of objects after msSinceEpoch (newer)
CONTAINER findAfter(qint64 msSinceEpoch) const;
//! List of objects after msSinceEpoch (newer)
OBJ findObjectAfterOrDefault(qint64 msSinceEpoch) const;
//! Objects without valid timestamp
CONTAINER findInvalidTimestamps() const;
@@ -74,10 +80,10 @@ namespace BlackMisc
//! Oldest timestamp
qint64 oldestTimestampMsecsSinceEpoch() const;
//! Latest value
//! Latest object
OBJ latestObject() const;
//! Latest value
//! Latest object
OBJ oldestObject() const;
//! Remove objects with timestamp before dateTime
@@ -128,6 +134,12 @@ namespace BlackMisc
//! Sort by adjusted timestamp
void sortAdjustedLatestFirst();
//! As sorted copy
CONTAINER getSortedAdjustedLatestFirst() const;
//! Get the latest 2 values
CONTAINER getLatestAdjustedTwoObjects(bool alreadySortedLatestFirst = false) const;
//! Sort by adjusted timestamp
void sortAdjustedOldestFirst();
@@ -158,9 +170,39 @@ namespace BlackMisc
//! \remark all object must have a valid timestamp
bool isSortedAdjustedLatestFirst() const;
//! List of objects after msSinceEpoch (newer)
CONTAINER findAfterAdjusted(qint64 msSinceEpoch) const;
//! List of objects after msSinceEpoch (newer)
OBJ findObjectAfterAdjustedOrDefault(qint64 msSinceEpoch) const;
//! List of objects before msSinceEpoch (older)
CONTAINER findBeforeAdjusted(qint64 msSinceEpoch) const;
//! Object before timestamp (older)
OBJ findObjectBeforeAdjustedOrDefault(qint64 msSinceEpoch) const;
//! Closest adjusted time difference
OBJ findClosestTimeDistanceAdjusted(qint64 msSinceEpoch) const;
//! Latest adjusted object
OBJ latestAdjustedObject() const;
//! Oldest adjusted object
OBJ oldestAdjustedObject() const;
//! Latest adjusted timestamp
QDateTime latestAdjustedTimestamp() const;
//! Oldest adjusted timestamp
QDateTime oldestAdjustedTimestamp() const;
//! Latest adjusted timestamp
qint64 latestAdjustedTimestampMsecsSinceEpoch() const;
//! Oldest adjusted timestamp
qint64 oldestAdjustedTimestampMsecsSinceEpoch() const;
protected:
//! Constructor
ITimestampWithOffsetObjectList();