Skip to content

Commit 26d0260

Browse files
committed
fixed a timezone related issue
- fixed timezone related issue with calculating max24 value - refoactored code - introduced a macro since rust doesn't truely suppor currying
1 parent 45ae4ba commit 26d0260

File tree

4 files changed

+139
-126
lines changed

4 files changed

+139
-126
lines changed

Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "pi-inky-weather-epd"
3-
version = "0.5.4"
3+
version = "0.5.5"
44
edition = "2021"
55
authors = ["MT"]
66
description = "A weather display application for the Raspberry Pi using an EPD (e-paper display)."

src/dashboard/context.rs

Lines changed: 124 additions & 110 deletions
Original file line numberDiff line numberDiff line change
@@ -191,7 +191,7 @@ impl ContextBuilder {
191191
}
192192

193193
pub fn with_daily_forecast_data(&mut self, daily_forecast_data: Vec<DailyEntry>) -> &mut Self {
194-
// The date returned by Bom api is x:14 utc, which translates to x+10:00 AEST time,
194+
// The date returned by Bom api is UTC, for example x:14 UTC, which translates to x:14+10:00 AEST time,
195195
// so we have to do some conversion
196196
let local_date_truncated = Local::now()
197197
.with_hour(0)
@@ -310,11 +310,12 @@ impl ContextBuilder {
310310
}
311311
}
312312

313+
// Extrusion Pattern: force everything through one function until it resembles spaghetti
313314
pub fn with_hourly_forecast_data(
314315
&mut self,
315316
hourly_forecast_data: Vec<HourlyForecast>,
316317
) -> &mut Self {
317-
let (forecast_window_start, forecast_window_end) = match Self::find_forecast_window(
318+
let (utc_forecast_window_start, utc_forecast_window_end) = match Self::find_forecast_window(
318319
&hourly_forecast_data,
319320
) {
320321
Some((start, end)) => (start, end),
@@ -325,8 +326,30 @@ impl ContextBuilder {
325326
}
326327
};
327328

328-
println!("24h forecast window start : {:?}", forecast_window_start);
329-
println!("24h forecast window end : {:?}", forecast_window_end);
329+
println!(
330+
"24h UTC forecast window: start = {:?}, end = {:?}",
331+
utc_forecast_window_start, utc_forecast_window_end
332+
);
333+
334+
let local_forecast_window_start: DateTime<Local> =
335+
utc_forecast_window_start.with_timezone(&Local);
336+
let local_forecast_window_end: DateTime<Local> =
337+
utc_forecast_window_end.with_timezone(&Local);
338+
let day_end = local_forecast_window_start
339+
.with_hour(0)
340+
.unwrap()
341+
.with_minute(0)
342+
.unwrap()
343+
.with_second(0)
344+
.unwrap()
345+
+ chrono::Duration::days(1);
346+
347+
println!(
348+
"Local forecast window: start = {:?}, end = {:?}",
349+
local_forecast_window_start, local_forecast_window_end
350+
);
351+
352+
// println!("Day end: {:?}", day_end);
330353

331354
let mut graph = HourlyForecastGraph {
332355
x_axis_always_at_min: CONFIG.render_options.x_axis_always_at_min,
@@ -337,53 +360,22 @@ impl ContextBuilder {
337360
Self::populate_graph_data(
338361
self,
339362
&hourly_forecast_data,
340-
forecast_window_start,
341-
forecast_window_end,
363+
local_forecast_window_start,
364+
local_forecast_window_end,
342365
&mut graph,
343366
);
344367

345368
let svg_result = graph.draw_graph().unwrap();
346369
let (temp_curve_data, feel_like_curve_data, rain_curve_data) =
347370
Self::extract_curve_data(&svg_result);
348-
349-
let day_start = forecast_window_start
350-
.with_hour(0)
351-
.unwrap()
352-
.with_minute(0)
353-
.unwrap()
354-
.with_second(0)
355-
.unwrap();
356-
let day_end = day_start + chrono::Duration::days(1);
357-
358-
// println!("day_start: {:?}", day_start);
359-
// println!("day_end: {:?}", day_end);
360-
361371
self.context.graph_height = graph.height.to_string();
362372
self.context.graph_width = graph.width.to_string();
363373
self.context.actual_temp_curve_data = temp_curve_data;
364374
self.context.feel_like_curve_data = feel_like_curve_data;
365375
self.context.rain_curve_data = rain_curve_data;
366376

367-
Self::set_max_values(
368-
self,
369-
&hourly_forecast_data,
370-
forecast_window_start,
371-
day_end,
372-
forecast_window_end,
373-
);
374-
375-
self.context.total_rain_today = (get_total_between_dates(
376-
&hourly_forecast_data,
377-
&forecast_window_start,
378-
&forecast_window_end,
379-
|item: &HourlyForecast| item.rain.calculate_median_rain(),
380-
|item| &item.time,
381-
))
382-
.to_string();
383-
384-
let forecast_window_start_local = forecast_window_start.with_timezone(&chrono::Local);
385377
let axis_data_path =
386-
graph.create_axis_with_labels(forecast_window_start_local.hour() as f32);
378+
graph.create_axis_with_labels(local_forecast_window_start.hour() as f32);
387379

388380
self.context.x_axis_path = axis_data_path.x_axis_path;
389381
self.context.y_left_axis_path = axis_data_path.y_left_axis_path;
@@ -394,6 +386,24 @@ impl ContextBuilder {
394386
self.context.x_axis_guideline_path = axis_data_path.x_axis_guideline_path;
395387

396388
self.context.uv_gradient = graph.draw_uv_gradient_over_time();
389+
390+
Self::set_max_values_for_table(
391+
self,
392+
&hourly_forecast_data,
393+
local_forecast_window_start,
394+
day_end,
395+
local_forecast_window_end,
396+
);
397+
398+
self.context.total_rain_today = (get_total_between_dates(
399+
&hourly_forecast_data,
400+
&local_forecast_window_start,
401+
&local_forecast_window_end,
402+
|item: &HourlyForecast| item.rain.calculate_median_rain(),
403+
|item| item.time.with_timezone(&Local),
404+
))
405+
.to_string();
406+
397407
self
398408
}
399409

@@ -407,7 +417,7 @@ impl ContextBuilder {
407417
.unwrap()
408418
.with_nanosecond(0)
409419
.unwrap();
410-
println!("Current UTC date : {:?}", current_date);
420+
println!("Current time (UTC, to the hour) : {:?}", current_date);
411421

412422
let first_date = hourly_forecast_data.iter().find_map(|forecast| {
413423
if forecast.time >= current_date {
@@ -425,11 +435,25 @@ impl ContextBuilder {
425435
}
426436
}
427437

438+
fn extract_curve_data(svg_result: &[GraphDataPath]) -> (String, String, String) {
439+
svg_result.iter().fold(
440+
(String::new(), String::new(), String::new()),
441+
|(mut temp_acc, mut feel_like_acc, mut rain_acc), path| {
442+
match path {
443+
GraphDataPath::Temp(data) => temp_acc.push_str(data),
444+
GraphDataPath::TempFeelLike(data) => feel_like_acc.push_str(data),
445+
GraphDataPath::Rain(data) => rain_acc.push_str(data),
446+
}
447+
(temp_acc, feel_like_acc, rain_acc)
448+
},
449+
)
450+
}
451+
428452
fn populate_graph_data(
429453
&mut self,
430454
hourly_forecast_data: &[HourlyForecast],
431-
forecast_window_start: chrono::DateTime<Utc>,
432-
forecast_window_end: chrono::DateTime<Utc>,
455+
forecast_window_start: chrono::DateTime<Local>,
456+
forecast_window_end: chrono::DateTime<Local>,
433457
graph: &mut HourlyForecastGraph,
434458
) {
435459
let mut x = 0;
@@ -441,6 +465,7 @@ impl ContextBuilder {
441465
.for_each(|forecast| {
442466
if x == 0 {
443467
self.with_current_hour_data(forecast);
468+
self.set_now_values_for_table(forecast)
444469
} else if x >= 24 {
445470
eprintln!(
446471
"Warning: More than 24 hours of hourly forecast data, this should not happen"
@@ -466,105 +491,94 @@ impl ContextBuilder {
466491
self.context.current_hour_actual_temp = current_hour.temp.to_string();
467492
self.context.current_hour_weather_icon = current_hour.get_icon_path();
468493
self.context.current_hour_feels_like = current_hour.temp_feels_like.to_string();
469-
self.context.current_hour_wind_speed = current_hour.wind.get_speed().to_string();
470-
self.context.current_hour_wind_icon = current_hour.wind.get_icon_path();
471-
self.context.current_hour_uv_index = current_hour.uv.0.to_string();
472-
self.context.current_hour_uv_index_icon = current_hour.uv.get_icon_path();
473-
self.context.current_hour_relative_humidity = current_hour.relative_humidity.0.to_string();
474-
self.context.current_hour_relative_humidity_icon =
475-
current_hour.relative_humidity.get_icon_path();
476494
self.context.current_day_date = chrono::Local::now().format("%A, %d %B").to_string();
477495
self.context.current_hour_rain_amount =
478496
current_hour.rain.calculate_median_rain().to_string();
479497
self.context.current_hour_rain_measure_icon = current_hour.rain.amount.get_icon_path();
498+
480499
self
481500
}
482501

483-
fn extract_curve_data(svg_result: &[GraphDataPath]) -> (String, String, String) {
484-
svg_result.iter().fold(
485-
(String::new(), String::new(), String::new()),
486-
|(mut temp_acc, mut feel_like_acc, mut rain_acc), path| {
487-
match path {
488-
GraphDataPath::Temp(data) => temp_acc.push_str(data),
489-
GraphDataPath::TempFeelLike(data) => feel_like_acc.push_str(data),
490-
GraphDataPath::Rain(data) => rain_acc.push_str(data),
491-
}
492-
(temp_acc, feel_like_acc, rain_acc)
493-
},
494-
)
502+
fn set_now_values_for_table(&mut self, current_hour: &HourlyForecast) {
503+
self.context.current_hour_wind_speed = current_hour.wind.get_speed().to_string();
504+
self.context.current_hour_wind_icon = current_hour.wind.get_icon_path();
505+
self.context.current_hour_uv_index = current_hour.uv.0.to_string();
506+
self.context.current_hour_uv_index_icon = current_hour.uv.get_icon_path();
507+
self.context.current_hour_relative_humidity = current_hour.relative_humidity.0.to_string();
508+
self.context.current_hour_relative_humidity_icon =
509+
current_hour.relative_humidity.get_icon_path();
495510
}
496511

497-
fn set_max_values(
512+
fn set_max_values_for_table(
498513
&mut self,
499514
hourly_forecast_data: &[HourlyForecast],
500-
forecast_window_start: chrono::DateTime<Utc>,
501-
day_end: chrono::DateTime<Utc>,
502-
forecast_window_end: chrono::DateTime<Utc>,
515+
forecast_window_start: chrono::DateTime<Local>,
516+
day_end: chrono::DateTime<Local>,
517+
forecast_window_end: chrono::DateTime<Local>,
503518
) {
504-
let max_wind_today = find_max_item_between_dates(
505-
hourly_forecast_data,
506-
&forecast_window_start,
507-
&day_end,
508-
|item| item.wind.get_speed(),
509-
|item| &item.time,
519+
println!("### Calculating table Max24h...");
520+
let today_duration = day_end
521+
.signed_duration_since(forecast_window_start)
522+
.num_hours();
523+
println!(
524+
"Today's Forecast Window: start = {:?}, end = {:?}, duration = {} hours",
525+
forecast_window_start, day_end, today_duration
510526
);
511527

512-
let max_wind_tomorrow = find_max_item_between_dates(
513-
hourly_forecast_data,
514-
&day_end,
515-
&forecast_window_end,
516-
|item| item.wind.get_speed(),
517-
|item| &item.time,
528+
let tomorrow_duration = forecast_window_end
529+
.signed_duration_since(day_end)
530+
.num_hours();
531+
println!(
532+
"Tomorrow's Forecast Window: start = {:?}, end = {:?}, duration = {} hours",
533+
day_end, forecast_window_end, tomorrow_duration
518534
);
519535

536+
macro_rules! max_in_today_and_tomorrow {
537+
($get_value:expr) => {{
538+
let get_time = |item: &HourlyForecast| item.time.with_timezone(&Local);
539+
let max_today = find_max_item_between_dates(
540+
hourly_forecast_data,
541+
&forecast_window_start,
542+
&day_end,
543+
$get_value,
544+
get_time,
545+
);
546+
let max_tomorrow = find_max_item_between_dates(
547+
hourly_forecast_data,
548+
&day_end,
549+
&forecast_window_end,
550+
$get_value,
551+
get_time,
552+
);
553+
(max_today, max_tomorrow)
554+
}};
555+
}
556+
557+
let (max_wind_today, max_wind_tomorrow) =
558+
max_in_today_and_tomorrow!(|item| item.wind.get_speed());
559+
520560
if max_wind_today > max_wind_tomorrow {
521561
self.context.max_gust_speed = max_wind_today.to_string();
522562
} else {
523563
self.context.max_gust_speed = max_wind_tomorrow.to_string();
524564
self.context.max_gust_speed_font_style = FontStyle::Italic.to_string();
525565
}
526566

527-
let max_uv_index = find_max_item_between_dates(
528-
hourly_forecast_data,
529-
&forecast_window_start,
530-
&day_end,
531-
|item| item.uv,
532-
|item| &item.time,
533-
);
567+
let (max_uv_index_today, max_uv_index_tomorrow) =
568+
max_in_today_and_tomorrow!(|item| item.uv);
534569

535-
let max_uv_index_tomorrow = find_max_item_between_dates(
536-
hourly_forecast_data,
537-
&day_end,
538-
&forecast_window_end,
539-
|item| item.uv,
540-
|item| &item.time,
541-
);
542-
543-
if max_uv_index > max_uv_index_tomorrow {
544-
self.context.max_uv_index = max_uv_index.0.to_string();
570+
if max_uv_index_today > max_uv_index_tomorrow {
571+
self.context.max_uv_index = max_uv_index_today.0.to_string();
545572
} else {
546573
self.context.max_uv_index = max_uv_index_tomorrow.0.to_string();
547574
self.context.max_uv_index_font_style = FontStyle::Italic.to_string();
548575
}
549576

550-
let max_relative_humidity = find_max_item_between_dates(
551-
hourly_forecast_data,
552-
&forecast_window_start,
553-
&day_end,
554-
|item| item.relative_humidity,
555-
|item| &item.time,
556-
);
557-
558-
let max_relative_humidity_tomorrow = find_max_item_between_dates(
559-
hourly_forecast_data,
560-
&day_end,
561-
&forecast_window_end,
562-
|item| item.relative_humidity,
563-
|item| &item.time,
564-
);
577+
let (max_relative_humidity_today, max_relative_humidity_tomorrow) =
578+
max_in_today_and_tomorrow!(|item| item.relative_humidity);
565579

566-
if max_relative_humidity > max_relative_humidity_tomorrow {
567-
self.context.max_relative_humidity = max_relative_humidity.0.to_string();
580+
if max_relative_humidity_today > max_relative_humidity_tomorrow {
581+
self.context.max_relative_humidity = max_relative_humidity_today.0.to_string();
568582
} else {
569583
self.context.max_relative_humidity = max_relative_humidity_tomorrow.0.to_string();
570584
self.context.max_relative_humidity_font_style = FontStyle::Italic.to_string();

0 commit comments

Comments
 (0)