From 45b0df3e0b6bd658f8f30d7d713b409665573f69 Mon Sep 17 00:00:00 2001 From: Dan Paulat Date: Mon, 29 May 2023 00:47:47 -0500 Subject: [PATCH] Fix text product start time when first P-VTEC uses 000000T0000Z --- scwx-qt/source/scwx/qt/model/alert_model.cpp | 3 +- .../scwx/awips/text_product_message.hpp | 9 +- .../scwx/awips/text_product_message.cpp | 83 +++++++++++++++++++ 3 files changed, 90 insertions(+), 5 deletions(-) diff --git a/scwx-qt/source/scwx/qt/model/alert_model.cpp b/scwx-qt/source/scwx/qt/model/alert_model.cpp index 8ea4ccc1..793d67b4 100644 --- a/scwx-qt/source/scwx/qt/model/alert_model.cpp +++ b/scwx-qt/source/scwx/qt/model/alert_model.cpp @@ -421,8 +421,7 @@ AlertModelImpl::GetStartTime(const types::TextEventKey& key) if (messageList.size() > 0) { auto& firstMessage = messageList.front(); - auto firstSegment = firstMessage->segment(0); - return firstSegment->header_->vtecString_[0].pVtec_.event_begin(); + return firstMessage->segment_event_begin(0); } else { diff --git a/wxdata/include/scwx/awips/text_product_message.hpp b/wxdata/include/scwx/awips/text_product_message.hpp index ec90f82d..80ba4a16 100644 --- a/wxdata/include/scwx/awips/text_product_message.hpp +++ b/wxdata/include/scwx/awips/text_product_message.hpp @@ -91,11 +91,14 @@ public: std::shared_ptr wmo_header() const; std::vector mnd_header() const; std::vector overview_block() const; - size_t segment_count() const; + std::size_t segment_count() const; std::vector> segments() const; - std::shared_ptr segment(size_t s) const; + std::shared_ptr segment(std::size_t s) const; - size_t data_size() const; + std::chrono::system_clock::time_point + segment_event_begin(std::size_t s) const; + + std::size_t data_size() const; bool Parse(std::istream& is) override; diff --git a/wxdata/source/scwx/awips/text_product_message.cpp b/wxdata/source/scwx/awips/text_product_message.cpp index a27e593d..262ec01a 100644 --- a/wxdata/source/scwx/awips/text_product_message.cpp +++ b/wxdata/source/scwx/awips/text_product_message.cpp @@ -1,5 +1,6 @@ #include #include +#include #include #include @@ -15,6 +16,7 @@ namespace awips { static const std::string logPrefix_ = "scwx::awips::text_product_message"; +static const auto logger_ = scwx::util::Logger::Create(logPrefix_); // Issuance date/time takes one of the following forms: // * _xM__day_mon_
_year @@ -101,6 +103,87 @@ std::shared_ptr TextProductMessage::segment(size_t s) const return p->segments_[s]; } +std::chrono::system_clock::time_point +TextProductMessage::segment_event_begin(std::size_t s) const +{ + std::chrono::system_clock::time_point eventBegin {}; + + auto& header = segment(s)->header_; + if (header.has_value() && !header->vtecString_.empty()) + { + // Determine event begin from P-VTEC string + eventBegin = header->vtecString_[0].pVtec_.event_begin(); + + // 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 = + header->vtecString_[0].pVtec_.event_end(); + + auto endDays = floor(eventEnd); + year_month_day endDate {endDays}; + + // Determine WMO date/time + std::string wmoDateTime = wmo_header()->date_time(); + + bool wmoDateTimeValid = false; + unsigned long dayOfMonth = 0; + unsigned long beginHour = 0; + unsigned long beginMinute = 0; + + try + { + // WMO date time is in the format DDHHMM + dayOfMonth = 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 + if (eventBegin > eventEnd) + { + // If the current end month is January + if (endDate.month() == January) + { + // The begin month must be December of last year + eventBegin = sys_days {year {(endDate.year() - 1y).count()} / + December / day {dayOfMonth}} + + hours {beginHour} + minutes {beginMinute}; + } + else + { + // Back up one month + eventBegin = + sys_days {endDate.year() / + month {static_cast( + (endDate.month() - month {1}).count())} / + day {dayOfMonth}} + + hours {beginHour} + minutes {beginMinute}; + } + } + } + } + } + + return eventBegin; +} + size_t TextProductMessage::data_size() const { return 0;