mirror of
https://codeberg.org/comaps/comaps
synced 2025-12-20 05:13:58 +00:00
[traffic] Refactor IsoTime into a class
Signed-off-by: mvglasow <michael -at- vonglasow.com>
This commit is contained in:
@@ -367,13 +367,13 @@ void TrafficManager::ConsolidateFeedQueue()
|
|||||||
if (it_i->m_id == it_j->m_id)
|
if (it_i->m_id == it_j->m_id)
|
||||||
{
|
{
|
||||||
// dupe, remove older
|
// dupe, remove older
|
||||||
if (traffxml::operator<(it_i->m_updateTime, it_j->m_updateTime))
|
if (it_i->m_updateTime < it_j->m_updateTime)
|
||||||
{
|
{
|
||||||
// standard case: i has the newer one
|
// standard case: i has the newer one
|
||||||
++it_i;
|
++it_i;
|
||||||
it_j = m_feedQueue[j].erase(it_j);
|
it_j = m_feedQueue[j].erase(it_j);
|
||||||
}
|
}
|
||||||
else if (traffxml::operator<(it_i->m_updateTime, it_j->m_updateTime))
|
else if (it_i->m_updateTime < it_j->m_updateTime)
|
||||||
{
|
{
|
||||||
// j has the newer one
|
// j has the newer one
|
||||||
it_i = m_feedQueue[i].erase(it_i);
|
it_i = m_feedQueue[i].erase(it_i);
|
||||||
@@ -425,7 +425,7 @@ void TrafficManager::UpdateMessageCache(std::map<std::string, traffxml::TraffMes
|
|||||||
auto it = cache.find(message.m_id);
|
auto it = cache.find(message.m_id);
|
||||||
bool process = (it == cache.end());
|
bool process = (it == cache.end());
|
||||||
if (!process)
|
if (!process)
|
||||||
process = (timegm(&(it->second.m_updateTime)) < timegm(&(message.m_updateTime)));
|
process = (it->second.m_updateTime < message.m_updateTime);
|
||||||
if (process)
|
if (process)
|
||||||
cache.insert_or_assign(message.m_id, message);
|
cache.insert_or_assign(message.m_id, message);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
|
|
||||||
#include "geometry/mercator.hpp"
|
#include "geometry/mercator.hpp"
|
||||||
|
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
using namespace std;
|
using namespace std;
|
||||||
|
|
||||||
namespace traffxml
|
namespace traffxml
|
||||||
@@ -76,17 +78,78 @@ const std::map<EventType, uint16_t> kEventDelayMap{
|
|||||||
// TODO Security*, Transport*, Weather* (not in enum yet)
|
// TODO Security*, Transport*, Weather* (not in enum yet)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
std::optional<IsoTime> IsoTime::ParseIsoTime(std::string timeString)
|
||||||
|
{
|
||||||
|
/*
|
||||||
|
* Regex for ISO 8601 time, with some tolerance for time zone offset. If matched, the matcher
|
||||||
|
* will contain the following items:
|
||||||
|
*
|
||||||
|
* 0: 2019-11-01T11:55:42+01:00 (entire expression)
|
||||||
|
* 1: 2019 (year)
|
||||||
|
* 2: 11 (month)
|
||||||
|
* 3: 01 (day)
|
||||||
|
* 4: 11 (hour, local)
|
||||||
|
* 5: 55 (minute, local)
|
||||||
|
* 6: 42.445 (second, local, float)
|
||||||
|
* 7: .445 (fractional seconds)
|
||||||
|
* 8: +01:00 (complete UTC offset, or Z; blank if not specified)
|
||||||
|
* 9: +01:00 (complete UTC offset, blank for Z or of not specified)
|
||||||
|
* 10: +01 (UTC offset, hours with sign; blank for Z or if not specified)
|
||||||
|
* 11: :00 (UTC offset, minutes, prefixed with separator)
|
||||||
|
* 12: 00 (UTC offset, minutes, unsigned; blank for Z or if not specified)
|
||||||
|
*/
|
||||||
|
std::regex iso8601Regex("([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2}(.[0-9]*)?)(Z|(([+-][0-9]{2})(:?([0-9]{2}))?))?");
|
||||||
|
|
||||||
|
std::smatch iso8601Matcher;
|
||||||
|
if (std::regex_search(timeString, iso8601Matcher, iso8601Regex))
|
||||||
|
{
|
||||||
|
int offset_h = iso8601Matcher[10].matched ? std::stoi(iso8601Matcher[10]) : 0;
|
||||||
|
int offset_m = iso8601Matcher[12].matched ? std::stoi(iso8601Matcher[12]) : 0;
|
||||||
|
if (offset_h < 0)
|
||||||
|
offset_m *= -1;
|
||||||
|
|
||||||
|
std::tm tm = {};
|
||||||
|
tm.tm_year = std::stoi(iso8601Matcher[1]) - 1900;
|
||||||
|
tm.tm_mon = std::stoi(iso8601Matcher[2]) - 1;
|
||||||
|
tm.tm_mday = std::stoi(iso8601Matcher[3]);
|
||||||
|
tm.tm_hour = std::stoi(iso8601Matcher[4]) - offset_h;
|
||||||
|
tm.tm_min = std::stoi(iso8601Matcher[5]) - offset_m;
|
||||||
|
tm.tm_sec = std::stof(iso8601Matcher[6]) + 0.5f;
|
||||||
|
// Call timegm once to normalize tm; return value can be discarded
|
||||||
|
timegm(&tm);
|
||||||
|
IsoTime result(tm);
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
LOG(LINFO, ("Not a valid ISO 8601 timestamp:", timeString));
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IsoTime IsoTime::Now()
|
||||||
|
{
|
||||||
|
std::time_t t = std::time(nullptr);
|
||||||
|
std::tm* tm = std::gmtime(&t);
|
||||||
|
return IsoTime(*tm);
|
||||||
|
}
|
||||||
|
|
||||||
|
IsoTime::IsoTime(std::tm tm)
|
||||||
|
: m_tm(tm)
|
||||||
|
{}
|
||||||
|
|
||||||
|
|
||||||
bool operator< (IsoTime lhs, IsoTime rhs)
|
bool operator< (IsoTime lhs, IsoTime rhs)
|
||||||
{
|
{
|
||||||
std::time_t t_lhs = std::mktime(&lhs);
|
std::time_t t_lhs = std::mktime(&lhs.m_tm);
|
||||||
std::time_t t_rhs = std::mktime(&rhs);
|
std::time_t t_rhs = std::mktime(&rhs.m_tm);
|
||||||
return t_lhs < t_rhs;
|
return t_lhs < t_rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool operator> (IsoTime lhs, IsoTime rhs)
|
bool operator> (IsoTime lhs, IsoTime rhs)
|
||||||
{
|
{
|
||||||
std::time_t t_lhs = std::mktime(&lhs);
|
std::time_t t_lhs = std::mktime(&lhs.m_tm);
|
||||||
std::time_t t_rhs = std::mktime(&rhs);
|
std::time_t t_rhs = std::mktime(&rhs.m_tm);
|
||||||
return t_lhs > t_rhs;
|
return t_lhs > t_rhs;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -321,7 +384,7 @@ string DebugPrint(LinearSegmentSource source)
|
|||||||
std::string DebugPrint(IsoTime time)
|
std::string DebugPrint(IsoTime time)
|
||||||
{
|
{
|
||||||
std::ostringstream os;
|
std::ostringstream os;
|
||||||
os << std::put_time(&time, "%Y-%m-%d %H:%M:%S %z");
|
os << std::put_time(&time.m_tm, "%Y-%m-%d %H:%M:%S %z");
|
||||||
return os.str();
|
return os.str();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -35,11 +35,45 @@ constexpr uint8_t kMaxspeedNone = 255;
|
|||||||
* Where no time zone is indicated, the timestamp shall always be interpreted as UTC.
|
* Where no time zone is indicated, the timestamp shall always be interpreted as UTC.
|
||||||
* `IsoTime` currently maps to `std::tm`, but this is not guaranteed.
|
* `IsoTime` currently maps to `std::tm`, but this is not guaranteed.
|
||||||
*/
|
*/
|
||||||
// TODO make this a class with a private std::tm member
|
class IsoTime
|
||||||
using IsoTime = std::tm;
|
{
|
||||||
|
public:
|
||||||
|
/**
|
||||||
|
* @brief Parses time in ISO 8601 format from a string and stores it in an `IsoTime`.
|
||||||
|
*
|
||||||
|
* ISO 8601 timestamps have the format `yyyy-mm-ddThh:mm:ss[.sss]`, optionally followed by a UTC
|
||||||
|
* offset. For example, `2019-11-01T11:45:42+01:00` refers to 11:45:42 in the UTC+1 timezone, which
|
||||||
|
* is 10:45:42 UTC.
|
||||||
|
*
|
||||||
|
* A UTC offset of `Z` denotes UTC and is equivalent to `+00:00` or `-00:00`. UTC is also assumed
|
||||||
|
* if no UTC offset is specified.
|
||||||
|
*
|
||||||
|
* The UTC offset can be specified as `hh:mm`, `hhmm` or `hh`.
|
||||||
|
*
|
||||||
|
* Seconds can be specified as integer or float, but will be rounded to the nearest integer. For
|
||||||
|
* example, 42.645 seconds will be rounded to 43 seconds.
|
||||||
|
*
|
||||||
|
* @param timeString Time in ISO8601 format
|
||||||
|
* @return An `IsoTime` instance corresponding to `timeString`, or `std::nullopt` if `timeString` is not a valid ISO8601 time string.
|
||||||
|
*/
|
||||||
|
static std::optional<IsoTime> ParseIsoTime(std::string timeString);
|
||||||
|
|
||||||
bool operator< (IsoTime lhs, IsoTime rhs);
|
/**
|
||||||
bool operator> (IsoTime lhs, IsoTime rhs);
|
* @brief Returns an `IsoTime` corresponding to current wall clock time.
|
||||||
|
*
|
||||||
|
* @return An `IsoTime` corresponding to current wall clock time.
|
||||||
|
*/
|
||||||
|
static IsoTime Now();
|
||||||
|
|
||||||
|
friend bool operator< (IsoTime lhs, IsoTime rhs);
|
||||||
|
friend bool operator> (IsoTime lhs, IsoTime rhs);
|
||||||
|
private:
|
||||||
|
friend std::string DebugPrint(IsoTime time);
|
||||||
|
|
||||||
|
IsoTime(std::tm tm);
|
||||||
|
|
||||||
|
std::tm m_tm;
|
||||||
|
};
|
||||||
|
|
||||||
// TODO enum urgency
|
// TODO enum urgency
|
||||||
|
|
||||||
@@ -301,9 +335,9 @@ struct TraffMessage
|
|||||||
std::optional<TrafficImpact> GetTrafficImpact();
|
std::optional<TrafficImpact> GetTrafficImpact();
|
||||||
|
|
||||||
std::string m_id;
|
std::string m_id;
|
||||||
IsoTime m_receiveTime = {};
|
IsoTime m_receiveTime = IsoTime::Now();
|
||||||
IsoTime m_updateTime = {};
|
IsoTime m_updateTime = IsoTime::Now();
|
||||||
IsoTime m_expirationTime = {};
|
IsoTime m_expirationTime = IsoTime::Now();
|
||||||
std::optional<IsoTime> m_startTime = {};
|
std::optional<IsoTime> m_startTime = {};
|
||||||
std::optional<IsoTime> m_endTime = {};
|
std::optional<IsoTime> m_endTime = {};
|
||||||
bool m_cancellation = false;
|
bool m_cancellation = false;
|
||||||
|
|||||||
@@ -196,49 +196,13 @@ bool TimeFromXml(pugi::xml_attribute attribute, IsoTime & tm)
|
|||||||
std::string timeString;
|
std::string timeString;
|
||||||
if (!StringFromXml(attribute, timeString))
|
if (!StringFromXml(attribute, timeString))
|
||||||
return false;
|
return false;
|
||||||
/*
|
|
||||||
* Regex for ISO 8601 time, with some tolerance for time zone offset. If matched, the matcher
|
|
||||||
* will contain the following items:
|
|
||||||
*
|
|
||||||
* 0: 2019-11-01T11:55:42+01:00 (entire expression)
|
|
||||||
* 1: 2019 (year)
|
|
||||||
* 2: 11 (month)
|
|
||||||
* 3: 01 (day)
|
|
||||||
* 4: 11 (hour, local)
|
|
||||||
* 5: 55 (minute, local)
|
|
||||||
* 6: 42.445 (second, local, float)
|
|
||||||
* 7: .445 (fractional seconds)
|
|
||||||
* 8: +01:00 (complete UTC offset, or Z; blank if not specified)
|
|
||||||
* 9: +01:00 (complete UTC offset, blank for Z or of not specified)
|
|
||||||
* 10: +01 (UTC offset, hours with sign; blank for Z or if not specified)
|
|
||||||
* 11: :00 (UTC offset, minutes, prefixed with separator)
|
|
||||||
* 12: 00 (UTC offset, minutes, unsigned; blank for Z or if not specified)
|
|
||||||
*/
|
|
||||||
std::regex iso8601Regex("([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2}(.[0-9]*)?)(Z|(([+-][0-9]{2})(:?([0-9]{2}))?))?");
|
|
||||||
|
|
||||||
std::smatch iso8601Matcher;
|
std::optional<IsoTime> result = IsoTime::ParseIsoTime(timeString);
|
||||||
if (std::regex_search(timeString, iso8601Matcher, iso8601Regex))
|
if (!result)
|
||||||
{
|
|
||||||
int offset_h = iso8601Matcher[10].matched ? std::stoi(iso8601Matcher[10]) : 0;
|
|
||||||
int offset_m = iso8601Matcher[12].matched ? std::stoi(iso8601Matcher[12]) : 0;
|
|
||||||
if (offset_h < 0)
|
|
||||||
offset_m *= -1;
|
|
||||||
|
|
||||||
tm.tm_year = std::stoi(iso8601Matcher[1]) - 1900;
|
|
||||||
tm.tm_mon = std::stoi(iso8601Matcher[2]) - 1;
|
|
||||||
tm.tm_mday = std::stoi(iso8601Matcher[3]);
|
|
||||||
tm.tm_hour = std::stoi(iso8601Matcher[4]) - offset_h;
|
|
||||||
tm.tm_min = std::stoi(iso8601Matcher[5]) - offset_m;
|
|
||||||
tm.tm_sec = std::stof(iso8601Matcher[6]) + 0.5f;
|
|
||||||
// Call timegm once to normalize tm; return value can be discarded
|
|
||||||
timegm(&tm);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
LOG(LINFO, ("Not a valid ISO 8601 timestamp:", timeString));
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
tm = result.value();
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -261,7 +225,7 @@ bool TimeFromXml(pugi::xml_attribute attribute, IsoTime & tm)
|
|||||||
*/
|
*/
|
||||||
std::optional<IsoTime> OptionalTimeFromXml(pugi::xml_attribute attribute)
|
std::optional<IsoTime> OptionalTimeFromXml(pugi::xml_attribute attribute)
|
||||||
{
|
{
|
||||||
IsoTime result = {};
|
IsoTime result = IsoTime::Now();
|
||||||
if (!TimeFromXml(attribute, result))
|
if (!TimeFromXml(attribute, result))
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
return result;
|
return result;
|
||||||
|
|||||||
Reference in New Issue
Block a user