mirror of
https://github.com/ciphervance/supercell-wx.git
synced 2025-10-30 13:30:06 +00:00
Adding robust date calculation to WMO header
This commit is contained in:
parent
c00016cb69
commit
7e9895e002
3 changed files with 178 additions and 72 deletions
|
|
@ -1,5 +1,6 @@
|
|||
#pragma once
|
||||
|
||||
#include <chrono>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
|
|
@ -45,8 +46,41 @@ public:
|
|||
std::string product_category() const;
|
||||
std::string product_designator() const;
|
||||
|
||||
/**
|
||||
* @brief Get the WMO date/time
|
||||
*
|
||||
* Gets the WMO date/time. Uses the optional date hint provided via
|
||||
* SetDateHint(std::chrono::year_month). If the date hint has not been
|
||||
* provided, the endTimeHint parameter is required.
|
||||
*
|
||||
* @param [in] endTimeHint The optional end time bounds to provide. This is
|
||||
* ignored if a date hint has been provided to determine an absolute date.
|
||||
*/
|
||||
std::chrono::sys_time<std::chrono::minutes> GetDateTime(
|
||||
std::optional<std::chrono::system_clock::time_point> endTimeHint =
|
||||
std::nullopt);
|
||||
|
||||
/**
|
||||
* @brief Parse a WMO header
|
||||
*
|
||||
* @param [in] is The input stream to parse
|
||||
*/
|
||||
bool Parse(std::istream& is);
|
||||
|
||||
/**
|
||||
* @brief Provide a date hint for the WMO parser
|
||||
*
|
||||
* The WMO header contains a date/time in the format DDMMSS. The year and
|
||||
* month must be derived using another source. The date hint provides the
|
||||
* additional context required to determine the absolute product time.
|
||||
*
|
||||
* This function will update any absolute date/time already calculated, or
|
||||
* affect the calculation of a subsequent absolute date/time.
|
||||
*
|
||||
* @param [in] dateHint The date hint to provide the WMO header parser
|
||||
*/
|
||||
void SetDateHint(std::chrono::year_month dateHint);
|
||||
|
||||
private:
|
||||
std::unique_ptr<WmoHeaderImpl> p;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -119,71 +119,11 @@ std::chrono::system_clock::time_point Segment::event_begin() const
|
|||
// If event begin is 000000T0000Z
|
||||
if (eventBegin == std::chrono::system_clock::time_point {})
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
// Determine event end from P-VTEC string
|
||||
system_clock::time_point eventEnd =
|
||||
std::chrono::system_clock::time_point eventEnd =
|
||||
header_->vtecString_[0].pVtec_.event_end();
|
||||
|
||||
auto endDays = floor<days>(eventEnd);
|
||||
year_month_day endDate {endDays};
|
||||
|
||||
// Determine WMO date/time
|
||||
std::string wmoDateTime = wmoHeader_->date_time();
|
||||
|
||||
bool wmoDateTimeValid = false;
|
||||
unsigned int dayOfMonth = 0;
|
||||
unsigned long beginHour = 0;
|
||||
unsigned long beginMinute = 0;
|
||||
|
||||
try
|
||||
{
|
||||
// WMO date time is in the format DDHHMM
|
||||
dayOfMonth =
|
||||
static_cast<unsigned int>(std::stoul(wmoDateTime.substr(0, 2)));
|
||||
beginHour = std::stoul(wmoDateTime.substr(2, 2));
|
||||
beginMinute = std::stoul(wmoDateTime.substr(4, 2));
|
||||
wmoDateTimeValid = true;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
logger_->warn("Malformed WMO date/time: {}", wmoDateTime);
|
||||
}
|
||||
|
||||
if (wmoDateTimeValid)
|
||||
{
|
||||
// Combine end date year and month with WMO date time
|
||||
eventBegin =
|
||||
sys_days {endDate.year() / endDate.month() / day {dayOfMonth}} +
|
||||
hours {beginHour} + minutes {beginMinute};
|
||||
|
||||
// If the begin date is after the end date, assume the start time
|
||||
// was the previous month (give a 1 day grace period for expiring
|
||||
// events in the past)
|
||||
if (eventBegin > eventEnd + 24h)
|
||||
{
|
||||
// If the current end month is January
|
||||
if (endDate.month() == January)
|
||||
{
|
||||
// The begin month must be December of last year
|
||||
eventBegin =
|
||||
sys_days {
|
||||
year {static_cast<int>((endDate.year() - 1y).count())} /
|
||||
December / day {dayOfMonth}} +
|
||||
hours {beginHour} + minutes {beginMinute};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Back up one month
|
||||
eventBegin =
|
||||
sys_days {endDate.year() /
|
||||
month {static_cast<unsigned int>(
|
||||
(endDate.month() - month {1}).count())} /
|
||||
day {dayOfMonth}} +
|
||||
hours {beginHour} + minutes {beginMinute};
|
||||
}
|
||||
}
|
||||
}
|
||||
eventBegin = wmoHeader_->GetDateTime(eventEnd);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -42,17 +42,26 @@ public:
|
|||
WmoHeaderImpl(const WmoHeaderImpl&&) = delete;
|
||||
WmoHeaderImpl& operator=(const WmoHeaderImpl&&) = delete;
|
||||
|
||||
void CalculateAbsoluteDateTime();
|
||||
bool ParseDateTime(unsigned int& dayOfMonth,
|
||||
unsigned long& hour,
|
||||
unsigned long& minute);
|
||||
|
||||
bool operator==(const WmoHeaderImpl& o) const;
|
||||
|
||||
std::string sequenceNumber_;
|
||||
std::string dataType_;
|
||||
std::string geographicDesignator_;
|
||||
std::string bulletinId_;
|
||||
std::string icao_;
|
||||
std::string dateTime_;
|
||||
std::string bbbIndicator_;
|
||||
std::string productCategory_;
|
||||
std::string productDesignator_;
|
||||
std::string sequenceNumber_ {};
|
||||
std::string dataType_ {};
|
||||
std::string geographicDesignator_ {};
|
||||
std::string bulletinId_ {};
|
||||
std::string icao_ {};
|
||||
std::string dateTime_ {};
|
||||
std::string bbbIndicator_ {};
|
||||
std::string productCategory_ {};
|
||||
std::string productDesignator_ {};
|
||||
|
||||
std::optional<std::chrono::year_month> dateHint_ {};
|
||||
std::optional<std::chrono::sys_time<std::chrono::minutes>>
|
||||
absoluteDateTime_ {};
|
||||
};
|
||||
|
||||
WmoHeader::WmoHeader() : p(std::make_unique<WmoHeaderImpl>()) {}
|
||||
|
|
@ -124,6 +133,71 @@ std::string WmoHeader::product_designator() const
|
|||
return p->productDesignator_;
|
||||
}
|
||||
|
||||
std::chrono::sys_time<std::chrono::minutes> WmoHeader::GetDateTime(
|
||||
std::optional<std::chrono::system_clock::time_point> endTimeHint)
|
||||
{
|
||||
std::chrono::sys_time<std::chrono::minutes> wmoDateTime {};
|
||||
|
||||
if (p->absoluteDateTime_.has_value())
|
||||
{
|
||||
wmoDateTime = p->absoluteDateTime_.value();
|
||||
}
|
||||
else if (endTimeHint.has_value())
|
||||
{
|
||||
bool dateTimeValid = false;
|
||||
unsigned int dayOfMonth = 0;
|
||||
unsigned long hour = 0;
|
||||
unsigned long minute = 0;
|
||||
|
||||
dateTimeValid = p->ParseDateTime(dayOfMonth, hour, minute);
|
||||
|
||||
if (dateTimeValid)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
|
||||
auto endDays = floor<days>(endTimeHint.value());
|
||||
year_month_day endDate {endDays};
|
||||
|
||||
// Combine end date year and month with WMO date time
|
||||
wmoDateTime =
|
||||
sys_days {endDate.year() / endDate.month() / day {dayOfMonth}} +
|
||||
hours {hour} + minutes {minute};
|
||||
|
||||
// If the begin date is after the end date, assume the start time
|
||||
// was the previous month (give a 1 day grace period for expiring
|
||||
// events in the past)
|
||||
if (wmoDateTime > endTimeHint.value() + 24h)
|
||||
{
|
||||
// If the current end month is January
|
||||
if (endDate.month() == January)
|
||||
{
|
||||
year_month x = year {2024} / December;
|
||||
sys_days y;
|
||||
|
||||
// The begin month must be December of last year
|
||||
wmoDateTime =
|
||||
sys_days {
|
||||
year {static_cast<int>((endDate.year() - 1y).count())} /
|
||||
December / day {dayOfMonth}} +
|
||||
hours {hour} + minutes {minute};
|
||||
}
|
||||
else
|
||||
{
|
||||
// Back up one month
|
||||
wmoDateTime =
|
||||
sys_days {endDate.year() /
|
||||
month {static_cast<unsigned int>(
|
||||
(endDate.month() - month {1}).count())} /
|
||||
day {dayOfMonth}} +
|
||||
hours {hour} + minutes {minute};
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return wmoDateTime;
|
||||
}
|
||||
|
||||
bool WmoHeader::Parse(std::istream& is)
|
||||
{
|
||||
bool headerValid = true;
|
||||
|
|
@ -224,6 +298,8 @@ bool WmoHeader::Parse(std::istream& is)
|
|||
p->icao_ = wmoTokenList[1];
|
||||
p->dateTime_ = wmoTokenList[2];
|
||||
|
||||
p->CalculateAbsoluteDateTime();
|
||||
|
||||
if (wmoTokenList.size() == 4)
|
||||
{
|
||||
p->bbbIndicator_ = wmoTokenList[3];
|
||||
|
|
@ -255,4 +331,60 @@ bool WmoHeader::Parse(std::istream& is)
|
|||
return headerValid;
|
||||
}
|
||||
|
||||
void WmoHeader::SetDateHint(std::chrono::year_month dateHint)
|
||||
{
|
||||
p->dateHint_ = dateHint;
|
||||
p->CalculateAbsoluteDateTime();
|
||||
}
|
||||
|
||||
bool WmoHeaderImpl::ParseDateTime(unsigned int& dayOfMonth,
|
||||
unsigned long& hour,
|
||||
unsigned long& minute)
|
||||
{
|
||||
bool dateTimeValid = false;
|
||||
|
||||
try
|
||||
{
|
||||
// WMO date time is in the format DDHHMM
|
||||
dayOfMonth =
|
||||
static_cast<unsigned int>(std::stoul(dateTime_.substr(0, 2)));
|
||||
hour = std::stoul(dateTime_.substr(2, 2));
|
||||
minute = std::stoul(dateTime_.substr(4, 2));
|
||||
dateTimeValid = true;
|
||||
}
|
||||
catch (const std::exception&)
|
||||
{
|
||||
logger_->warn("Malformed WMO date/time: {}", dateTime_);
|
||||
}
|
||||
|
||||
return dateTimeValid;
|
||||
}
|
||||
|
||||
void WmoHeaderImpl::CalculateAbsoluteDateTime()
|
||||
{
|
||||
bool dateTimeValid = false;
|
||||
|
||||
if (dateHint_.has_value() && !dateTime_.empty())
|
||||
{
|
||||
unsigned int dayOfMonth = 0;
|
||||
unsigned long hour = 0;
|
||||
unsigned long minute = 0;
|
||||
|
||||
dateTimeValid = ParseDateTime(dayOfMonth, hour, minute);
|
||||
|
||||
if (dateTimeValid)
|
||||
{
|
||||
using namespace std::chrono;
|
||||
absoluteDateTime_ = sys_days {dateHint_->year() / dateHint_->month() /
|
||||
day {dayOfMonth}} +
|
||||
hours {hour} + minutes {minute};
|
||||
}
|
||||
}
|
||||
|
||||
if (!dateTimeValid)
|
||||
{
|
||||
absoluteDateTime_.reset();
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace scwx::awips
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue