Skip to content

Commit

Permalink
Fix forecast timestamps not corresponding to exact hours
Browse files Browse the repository at this point in the history
Refs: #32
  • Loading branch information
orontee committed Oct 20, 2023
1 parent 2941ae1 commit 4e33ed3
Show file tree
Hide file tree
Showing 12 changed files with 175 additions and 119 deletions.
5 changes: 5 additions & 0 deletions NEWS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

### Changed

- For whatever reason, on some devices, forecast timestamps did not
correspond to exact hours. Those timestamps are now rounded to the
nearest hour.
[#32](https://github.com/orontee/taranis/issues/32)

### Removed

## [1.4.0] - 2023-10-19
Expand Down
17 changes: 7 additions & 10 deletions src/alerts.cc
Original file line number Diff line number Diff line change
Expand Up @@ -47,16 +47,13 @@ void taranis::AlertsButton::open_dialog_maybe() {
if (not alert.event.empty()) {
alert_text << alert.event << std::endl << std::endl;
}
alert_text << alert.description << std::endl << std::endl;
if (alert.start_date) {
alert_text << GetLangText("Start") << " "
<< format_time(alert.start_date, true) << std::endl;
if (alert.end_date) {
alert_text << GetLangText("Duration") << " "
<< format_duration(alert.start_date, alert.end_date)
<< std::endl;
}
}
alert_text << alert.description << std::endl
<< std::endl
<< GetLangText("Start") << " "
<< format_full_date(alert.start_date) << std::endl
<< GetLangText("Duration") << " "
<< format_duration(alert.start_date, alert.end_date) << std::endl;

if (not alert.sender.empty()) {
alert_text << GetLangText("Source") << " " << alert.sender;
}
Expand Down
5 changes: 3 additions & 2 deletions src/app.cc
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
#include "config.h"
#include "errors.h"
#include "events.h"
#include "experimental/optional"
#include "inkview.h"
#include "model.h"
#include "units.h"
Expand Down Expand Up @@ -228,7 +229,7 @@ int App::handle_custom_event(int param_one, int param_two) {
}
return 1;
} else if (param_one == CustomEvent::model_updated) {
this->model->refresh_date = std::time(nullptr);
this->model->refresh_date = std::chrono::system_clock::now();
this->history->update_history_maybe();
this->show();
} else if (param_one == CustomEvent::warning_emitted) {
Expand All @@ -252,7 +253,7 @@ void App::clear_model_weather_conditions() {
this->model->hourly_forecast.clear();
this->model->daily_forecast.clear();
this->model->alerts.clear();
this->model->refresh_date = 0;
this->model->refresh_date = std::experimental::nullopt;
}

bool App::must_skip_data_refresh() const {
Expand Down
83 changes: 50 additions & 33 deletions src/convert.cc
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
#include "experimental/optional"
#include "model.h"

using namespace std::chrono_literals;

namespace taranis {

// NAN is written as null by jsoncpp, this will fix deserialization
Expand All @@ -26,9 +28,15 @@ Json::Value to_json(const Location &location) {

Json::Value to_json(const Condition &condition) {
Json::Value value;
value["date"] = static_cast<int64_t>(condition.date);
value["sunrise"] = static_cast<int64_t>(condition.sunrise);
value["sunset"] = static_cast<int64_t>(condition.sunset);
value["date"] = std::chrono::duration_cast<std::chrono::seconds>(
condition.date.time_since_epoch())
.count();
value["sunrise"] = std::chrono::duration_cast<std::chrono::seconds>(
condition.sunrise.time_since_epoch())
.count();
value["sunset"] = std::chrono::duration_cast<std::chrono::seconds>(
condition.sunset.time_since_epoch())
.count();
value["temperature"] = condition.temperature;
value["felt_temperature"] = condition.felt_temperature;
value["pressure"] = condition.pressure;
Expand All @@ -52,11 +60,21 @@ Json::Value to_json(const Condition &condition) {

Json::Value to_json(const DailyCondition &condition) {
Json::Value value;
value["date"] = static_cast<int64_t>(condition.date);
value["sunrise"] = static_cast<int64_t>(condition.sunrise);
value["sunset"] = static_cast<int64_t>(condition.sunset);
value["moonrise"] = static_cast<int64_t>(condition.moonrise);
value["moonset"] = static_cast<int64_t>(condition.moonset);
value["date"] = std::chrono::duration_cast<std::chrono::seconds>(
condition.date.time_since_epoch())
.count();
value["sunrise"] = std::chrono::duration_cast<std::chrono::seconds>(
condition.sunrise.time_since_epoch())
.count();
value["sunset"] = std::chrono::duration_cast<std::chrono::seconds>(
condition.sunset.time_since_epoch())
.count();
value["moonrise"] = std::chrono::duration_cast<std::chrono::seconds>(
condition.moonrise.time_since_epoch())
.count();
value["moonset"] = std::chrono::duration_cast<std::chrono::seconds>(
condition.moonset.time_since_epoch())
.count();
value["moon_phase"] = condition.moon_phase;
value["pressure"] = condition.pressure;
value["humidity"] = condition.humidity;
Expand Down Expand Up @@ -91,8 +109,12 @@ Json::Value to_json(const Alert &alert) {
Json::Value value;
value["sender"] = alert.sender;
value["event"] = alert.event;
value["start_date"] = static_cast<int64_t>(alert.start_date);
value["end_date"] = static_cast<int64_t>(alert.end_date);
value["start_date"] = std::chrono::duration_cast<std::chrono::seconds>(
alert.start_date.time_since_epoch())
.count();
value["end_date"] = std::chrono::duration_cast<std::chrono::seconds>(
alert.end_date.time_since_epoch())
.count();
value["description"] = alert.description;
return value;
}
Expand All @@ -109,7 +131,13 @@ Json::Value to_json(const Model &model) {
Json::Value value;
value["source"] = model.source;
value["unit_system"] = model.unit_system;
value["refresh_date"] = static_cast<int64_t>(model.refresh_date);
if (model.refresh_date == std::experimental::nullopt) {
value["refresh_date"] = Json::Value::null;
} else {
value["refresh_date"] = std::chrono::duration_cast<std::chrono::seconds>(
model.refresh_date->time_since_epoch())
.count();
}
value["location"] = to_json(model.location);

if (model.current_condition) {
Expand Down Expand Up @@ -141,12 +169,9 @@ void update_from_json(Location &location, const Json::Value &value) {
}

void update_from_json(Condition &condition, const Json::Value &value) {
condition.date =
static_cast<std::time_t>(value.get("date", 0).asLargestInt());
condition.sunrise =
static_cast<std::time_t>(value.get("sunrise", 0).asLargestInt());
condition.sunset =
static_cast<std::time_t>(value.get("sunset", 0).asLargestInt());
condition.date = TimePoint{value.get("date", 0).asInt64() * 1s};
condition.sunrise = TimePoint{value.get("sunrise", 0).asInt64() * 1s};
condition.sunset = TimePoint{value.get("sunset", 0).asInt64() * 1s};
condition.temperature = deserialize_possible_null(value["temperature"]);
condition.felt_temperature =
deserialize_possible_null(value["felt_temperature"]);
Expand All @@ -170,16 +195,11 @@ void update_from_json(Condition &condition, const Json::Value &value) {
}

void update_from_json(DailyCondition &condition, const Json::Value &value) {
condition.date =
static_cast<std::time_t>(value.get("date", 0).asLargestInt());
condition.sunrise =
static_cast<std::time_t>(value.get("sunrise", 0).asLargestInt());
condition.sunset =
static_cast<std::time_t>(value.get("sunset", 0).asLargestInt());
condition.moonrise =
static_cast<std::time_t>(value.get("moonrise", 0).asLargestInt());
condition.moonset =
static_cast<std::time_t>(value.get("moonset", 0).asLargestInt());
condition.date = TimePoint{value.get("date", 0).asInt64() * 1s};
condition.sunrise = TimePoint{value.get("sunrise", 0).asInt64() * 1s};
condition.sunset = TimePoint{value.get("sunset", 0).asInt64() * 1s};
condition.moonrise = TimePoint{value.get("moonrise", 0).asInt64() * 1s};
condition.moonset = TimePoint{value.get("moonset", 0).asInt64() * 1s};
condition.moon_phase = deserialize_possible_null(value["moon_phase"]);
condition.pressure = value.get("pressure", 0).asInt();
condition.humidity = value.get("humidity", 0).asInt();
Expand Down Expand Up @@ -223,10 +243,8 @@ void update_from_json(DailyCondition &condition, const Json::Value &value) {
void update_from_json(Alert &alert, const Json::Value &value) {
alert.sender = value.get("sender", "").asString();
alert.event = value.get("event", "").asString();
alert.start_date =
static_cast<std::time_t>(value.get("start_date", 0).asLargestInt());
alert.end_date =
static_cast<std::time_t>(value.get("end_date", 0).asLargestInt());
alert.start_date = TimePoint{value.get("start_date", 0).asInt64() * 1s};
alert.end_date = TimePoint{value.get("end_date", 0).asInt64() * 1s};
alert.description = value.get("description", "").asString();
}

Expand All @@ -242,8 +260,7 @@ void update_from_json(Model &model, const Json::Value &value) {
model.source = value.get("source", "OpenWeather").asString();
model.unit_system =
static_cast<UnitSystem>(value.get("unit_system", standard).asInt());
model.refresh_date =
static_cast<std::time_t>(value.get("refresh_date", 0).asLargestInt());
model.refresh_date = TimePoint{value.get("refresh_date", 0).asInt64() * 1s};

auto &location = model.location;
const auto &location_value = value["location"];
Expand Down
3 changes: 1 addition & 2 deletions src/dailyforecastbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -122,9 +122,8 @@ class DailyForecastBox : public Widget {
const auto &forecast = this->model->daily_forecast[row_index];

if (column_index == DailyForecastBox::WeekDayColumn) {
const auto calendar_time = std::localtime(&forecast.date);
column_content[row_index] = std::pair<std::string, std::string>{
format_day(calendar_time), format_short_date(calendar_time)};
format_day(forecast.date), format_short_date(forecast.date)};
} else if (column_index == DailyForecastBox::IconColumn) {
column_content[row_index] =
this->icons->get(forecast.weather_icon_name);
Expand Down
2 changes: 1 addition & 1 deletion src/hourlyforecastbox.h
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ class HourlyForecastBox : public Widget {

SetFont(tiny_font.get(), BLACK);

const auto time_text = format_time(forecast.date);
const auto time_text = format_time(forecast.date, true);
DrawString(bar_center_x - StringWidth(time_text.c_str()) / 2.0,
this->time_y, time_text.c_str());

Expand Down
3 changes: 2 additions & 1 deletion src/menu.cc
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
#include "menu.h"

#include "experimental/optional"
#include "inkview.h"
#include "util.h"

Expand Down Expand Up @@ -164,7 +165,7 @@ void MenuButton::open_menu() {
if (not this->menu_handler) {
return;
}
if (this->model->refresh_date == 0) {
if (this->model->refresh_date == std::experimental::nullopt) {
// don't popup menu while refreshing due to display update bug
// after menu dialog is hidden... See issue #51
return;
Expand Down
27 changes: 15 additions & 12 deletions src/model.h
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#pragma once

#include <algorithm>
#include <chrono>
#include <cmath>
#include <ctime>
#include <experimental/optional>
Expand All @@ -12,6 +13,8 @@ namespace std {
template <class T> using optional = std::experimental::optional<T>;
}

using TimePoint = std::chrono::time_point<std::chrono::system_clock>;

namespace taranis {

enum UnitSystem { standard = 0, metric = 1, imperial = 2 };
Expand Down Expand Up @@ -114,9 +117,9 @@ enum Weather {

// Can contain current weather data or hourly forecast
struct Condition {
std::time_t date;
std::time_t sunrise;
std::time_t sunset;
TimePoint date;
TimePoint sunrise;
TimePoint sunset;
double temperature;
double felt_temperature;
int pressure;
Expand All @@ -138,11 +141,11 @@ struct Condition {

// Can contain daily forecast
struct DailyCondition {
std::time_t date;
std::time_t sunrise;
std::time_t sunset;
std::time_t moonrise;
std::time_t moonset;
TimePoint date;
TimePoint sunrise;
TimePoint sunset;
TimePoint moonrise;
TimePoint moonset;
double moon_phase;
int pressure;
int humidity;
Expand Down Expand Up @@ -174,8 +177,8 @@ struct DailyCondition {
struct Alert {
std::string sender;
std::string event;
std::time_t start_date;
std::time_t end_date;
TimePoint start_date;
TimePoint end_date;
std::string description;
};

Expand All @@ -187,10 +190,10 @@ struct HistoryItem {
struct Model {
std::string source{"OpenWeather"};
UnitSystem unit_system{standard};
std::time_t refresh_date;
std::optional<TimePoint> refresh_date;
Location location;

std::optional<Condition> current_condition;
std::optional<Condition> current_condition = std::experimental::nullopt;
std::vector<Condition> hourly_forecast;
std::vector<DailyCondition> daily_forecast;

Expand Down
36 changes: 16 additions & 20 deletions src/service.cc
Original file line number Diff line number Diff line change
@@ -1,11 +1,14 @@
#include "service.h"

#include <boost/log/trivial.hpp>
#include <chrono>

#include "errors.h"
#include "inkview.h"
#include "util.h"

using namespace std::chrono_literals;

namespace taranis {

namespace openweather {
Expand Down Expand Up @@ -173,11 +176,9 @@ Json::Value Service::send_get_request(const std::string &url) {
Condition Service::extract_condition(const Json::Value &value) {
BOOST_LOG_TRIVIAL(debug) << "Extracting weather condition from JSON value";

const auto date = static_cast<time_t>(value.get("dt", 0).asLargestInt());
const auto sunrise =
static_cast<time_t>(value.get("sunrise", 0).asLargestInt());
const auto sunset =
static_cast<time_t>(value.get("sunset", 0).asLargestInt());
const TimePoint date{value.get("dt", 0).asInt64() * 1s};
const TimePoint sunrise{value.get("sunrise", 0).asInt64() * 1s};
const TimePoint sunset{value.get("sunset", 0).asInt64() * 1s};
const auto temperature = value.get("temp", NAN).asDouble();
const auto felt_temperature = value.get("feels_like", NAN).asDouble();
const auto pressure = value.get("pressure", 0).asInt();
Expand Down Expand Up @@ -234,15 +235,11 @@ Condition Service::extract_condition(const Json::Value &value) {
DailyCondition Service::extract_daily_condition(const Json::Value &value) {
BOOST_LOG_TRIVIAL(debug) << "Extracting daily condition from JSON value";

const auto date = static_cast<time_t>(value.get("dt", 0).asLargestInt());
const auto sunrise =
static_cast<time_t>(value.get("sunrise", 0).asLargestInt());
const auto sunset =
static_cast<time_t>(value.get("sunset", 0).asLargestInt());
const auto moonrise =
static_cast<time_t>(value.get("moonrise", 0).asLargestInt());
const auto moonset =
static_cast<time_t>(value.get("moonset", 0).asLargestInt());
const TimePoint date{value.get("dt", 0).asInt64() * 1s};
const TimePoint sunrise{value.get("sunrise", 0).asInt64() * 1s};
const TimePoint sunset{value.get("sunset", 0).asInt64() * 1s};
const TimePoint moonrise{value.get("moonrise", 0).asInt64() * 1s};
const TimePoint moonset{value.get("moonset", 0).asInt64() * 1s};
const auto moon_phase = value.get("moon_phase", NAN).asDouble();
const auto pressure = value.get("pressure", 0).asInt();
const auto humidity = value.get("humidity", 0).asInt();
Expand Down Expand Up @@ -304,12 +301,11 @@ std::vector<Alert> Service::extract_alerts(const Json::Value &value) {
std::vector<Alert> alerts;

for (auto &alert_value : value) {
const Alert alert{
alert_value.get("sender_name", "").asString(),
alert_value.get("event", "").asString(),
static_cast<time_t>(alert_value.get("start", 0).asLargestInt()),
static_cast<time_t>(alert_value.get("end", 0).asLargestInt()),
alert_value.get("description", "").asString()};
const Alert alert{alert_value.get("sender_name", "").asString(),
alert_value.get("event", "").asString(),
TimePoint{alert_value.get("start", 0).asInt64() * 1s},
TimePoint{alert_value.get("end", 0).asInt64() * 1s},
alert_value.get("description", "").asString()};
alerts.push_back(alert);
}
return alerts;
Expand Down
Loading

0 comments on commit 4e33ed3

Please sign in to comment.