summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorstuebinm2022-02-01 19:40:12 +0100
committerstuebinm2022-02-01 19:40:12 +0100
commitd6c0c640e64fceba6c280d76ee64d44831e9afef (patch)
tree199582ce5622c5a7593666a0e1799cdb68e96968
parent80a4af9f0a6f7e3bd2d8254f8bc8475ca73b8980 (diff)
handle (esp. iceportal) timestamps correctly
why use unix time if you can use unix time * 1000 while your margin of error will never be lower than a full minute?
-rw-r--r--src/iceportal.rs16
-rw-r--r--src/main.rs6
-rw-r--r--src/serde.rs21
-rw-r--r--src/types.rs86
4 files changed, 68 insertions, 61 deletions
diff --git a/src/iceportal.rs b/src/iceportal.rs
index a81d5d3..0d2076e 100644
--- a/src/iceportal.rs
+++ b/src/iceportal.rs
@@ -1,4 +1,4 @@
-use chrono::NaiveDateTime;
+use chrono::{DateTime, NaiveDateTime, Utc};
use serde::Deserialize;
use serde_json::Value;
@@ -48,10 +48,10 @@ struct Station {
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
struct Timetable {
- #[serde(deserialize_with = "option_naive_read_unixtime")]
- scheduled_arrival_time: Option<NaiveDateTime>,
- #[serde(deserialize_with = "option_naive_read_unixtime")]
- actual_arrival_time: Option<NaiveDateTime>
+ #[serde(deserialize_with = "option_naive_read_unixtime_db")]
+ scheduled_arrival_time: Option<DateTime<Utc>>,
+ #[serde(deserialize_with = "option_naive_read_unixtime_db")]
+ actual_arrival_time: Option<DateTime<Utc>>
}
impl IsStation for Stop {
@@ -59,12 +59,12 @@ impl IsStation for Stop {
&self.station.name
}
- fn scheduled_arrival (&self) -> Option<&chrono::NaiveDateTime> {
+ fn scheduled_arrival (&self) -> Option<&chrono::DateTime<Utc>> {
self.timetable.scheduled_arrival_time.as_ref()
}
- fn real_arrival (&self) -> Option<&chrono::NaiveDateTime> {
- self.timetable.actual_arrival_time.as_ref()
+ fn real_arrival (&self) -> Option<&chrono::DateTime<Utc>> {
+ self.timetable.scheduled_arrival_time.as_ref()
}
fn ds100 (&self) -> &str {
diff --git a/src/main.rs b/src/main.rs
index cb9ac6c..6bac375 100644
--- a/src/main.rs
+++ b/src/main.rs
@@ -159,9 +159,9 @@ fn main() -> Result<(), ureq::Error> {
Command::ICEPortal => {
match get_request::<TripInfo>("https://iceportal.de/api1/rs/tripInfo/trip") {
Ok(resp) => {
- println!("{}: Currently in {}", traveltext, resp.get_train_ref());
- println!("guessing last stop was: {:?}", resp.guess_last_station());
- println!("{}", resp.trip())
+ println!("{}: Currently in {}\n", traveltext, resp.get_train_ref().to_string().green());
+ println!("guessing last stop was: {:?}\n", resp.guess_last_station());
+ println!("Stops:\n{}", resp.trip())
}
Err(err) => {
if cli.debug {
diff --git a/src/serde.rs b/src/serde.rs
index 9049d64..486929a 100644
--- a/src/serde.rs
+++ b/src/serde.rs
@@ -1,22 +1,33 @@
-use chrono::NaiveDateTime;
+use chrono::{DateTime, NaiveDateTime, Utc};
use serde::{Deserialize, Deserializer};
-pub fn naive_read_unixtime<'de, D>(d: D) -> Result<NaiveDateTime, D::Error>
+pub fn naive_read_unixtime<'de, D>(d: D) -> Result<DateTime<Utc>, D::Error>
where
D: Deserializer<'de>,
{
let ts = <i64>::deserialize(d)?;
- Ok(NaiveDateTime::from_timestamp(ts, 0))
+ Ok(DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(ts, 0), Utc))
}
-pub fn option_naive_read_unixtime<'de, D>(d: D) -> Result<Option<NaiveDateTime>, D::Error>
+pub fn option_naive_read_unixtime<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
where
D: Deserializer<'de>,
{
match <i64>::deserialize(d) {
Ok(ts) =>
- Ok(Some(NaiveDateTime::from_timestamp(ts, 0))),
+ Ok(Some(DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(ts, 0), Utc))),
+ Err(_) => Ok(None)
+ }
+}
+
+pub fn option_naive_read_unixtime_db<'de, D>(d: D) -> Result<Option<DateTime<Utc>>, D::Error>
+where
+ D: Deserializer<'de>,
+{
+ match <i64>::deserialize(d) {
+ Ok(ts) =>
+ Ok(Some(DateTime::<Utc>::from_utc(NaiveDateTime::from_timestamp(ts/1000, 0), Utc))),
Err(_) => Ok(None)
}
}
diff --git a/src/types.rs b/src/types.rs
index 2d8d189..2e4d2e1 100644
--- a/src/types.rs
+++ b/src/types.rs
@@ -1,6 +1,6 @@
use serde::{Deserialize, Deserializer};
-use chrono::NaiveDateTime;
+use chrono::{DateTime, Local, NaiveDateTime, Utc};
use colored::*;
use crate::serde::*;
@@ -14,55 +14,53 @@ pub struct Station {
latitude: f64,
longitude: f64,
#[serde(deserialize_with = "naive_read_unixtime")]
- scheduled_time: NaiveDateTime,
+ scheduled_time: DateTime<Utc>,
#[serde(deserialize_with = "naive_read_unixtime")]
- real_time: NaiveDateTime,
+ real_time: DateTime<Utc>,
}
-
pub fn parse_optional_station<'de, D>(d: D) -> Result<Option<Station>, D::Error>
where
D: Deserializer<'de>,
{
let val = <serde_json::Value>::deserialize(d)?;
- match serde_json::from_value(val) {
- Ok(station) => Ok(Some(station)),
- Err(_) => Ok(None)
- }
+ match serde_json::from_value(val) {
+ Ok(station) => Ok(Some(station)),
+ Err(_) => Ok(None),
+ }
}
-
-
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Stop {
name: String,
#[serde(deserialize_with = "option_naive_read_unixtime")]
- scheduled_arrival: Option<NaiveDateTime>,
+ scheduled_arrival: Option<DateTime<Utc>>,
#[serde(deserialize_with = "option_naive_read_unixtime")]
- real_arrival: Option<NaiveDateTime>,
+ real_arrival: Option<DateTime<Utc>>,
#[serde(deserialize_with = "option_naive_read_unixtime")]
- scheduled_departure: Option<NaiveDateTime>,
+ scheduled_departure: Option<DateTime<Utc>>,
#[serde(deserialize_with = "option_naive_read_unixtime")]
- real_departure: Option<NaiveDateTime>,
+ real_departure: Option<DateTime<Utc>>,
}
-
-
pub trait IsStation {
- fn name (&self) -> &str;
- fn scheduled_arrival (&self) -> Option<&NaiveDateTime>;
- fn real_arrival (&self) -> Option<&NaiveDateTime>;
- fn ds100 (&self) -> &str;
+ fn name(&self) -> &str;
+ fn scheduled_arrival(&self) -> Option<&DateTime<Utc>>;
+ fn real_arrival(&self) -> Option<&DateTime<Utc>>;
+ fn ds100(&self) -> &str;
- fn to_fancy_string (&self) -> String {
+ fn to_fancy_string(&self) -> String {
format!(
"{} {} – {} ({})",
- self.real_arrival().map(|t| t.time().to_string()).unwrap_or("??:??:??".to_string()).blue(),
+ self.real_arrival() // chrono's API for timezones is expressive, but reads like c++ …
+ .map(|t| <DateTime<Local>>::from(*t).time().to_string())
+ .unwrap_or("??:??:??".to_string())
+ .blue(),
{
let delay = match (self.real_arrival(), self.scheduled_arrival()) {
(Some(a), Some(s)) => (a.time() - s.time()).num_minutes(),
- _ => 0
+ _ => 0,
};
let text = format!("({:+})", delay);
if delay > 0 {
@@ -78,40 +76,37 @@ pub trait IsStation {
}
impl IsStation for Station {
- fn name (&self) -> &str {
+ fn name(&self) -> &str {
&self.name
}
- fn scheduled_arrival (&self) -> Option<&NaiveDateTime> {
+ fn scheduled_arrival(&self) -> Option<&DateTime<Utc>> {
Some(&self.scheduled_time)
}
- fn real_arrival (&self) -> Option<&NaiveDateTime> {
+ fn real_arrival(&self) -> Option<&DateTime<Utc>> {
Some(&self.real_time)
}
- fn ds100 (&self) -> &str {
+ fn ds100(&self) -> &str {
&self.ds100
}
}
impl IsStation for Stop {
- fn name (&self) -> &str {
+ fn name(&self) -> &str {
&self.name
}
- fn scheduled_arrival (&self) -> Option<&NaiveDateTime> {
+ fn scheduled_arrival(&self) -> Option<&DateTime<Utc>> {
self.scheduled_arrival.as_ref()
}
- fn real_arrival (&self) -> Option<&NaiveDateTime> {
+ fn real_arrival(&self) -> Option<&DateTime<Utc>> {
self.real_arrival.as_ref()
}
- fn ds100 (&self) -> &str {
+ fn ds100(&self) -> &str {
"[??]"
}
}
-
-
-
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Train {
@@ -122,8 +117,6 @@ pub struct Train {
id: String,
}
-
-
#[derive(Deserialize, Debug)]
#[serde(rename_all = "camelCase")]
pub struct Status {
@@ -142,8 +135,7 @@ pub struct Ds100 {
inner: String,
}
-pub struct Trip<'a, S: IsStation> (pub &'a Vec<S>);
-
+pub struct Trip<'a, S: IsStation>(pub &'a Vec<S>);
impl std::fmt::Display for Train {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -155,16 +147,16 @@ impl std::fmt::Display for Train {
impl<S: IsStation> std::fmt::Display for Trip<'_, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
if self.0.len() != 0 {
- self.0.iter()
- .map(|stop| stop.to_fancy_string())
- // .intersperse(" ↓".to_string())
- .for_each(|l| writeln!(f, " {}\n ↓", l).unwrap());
+ self.0
+ .iter()
+ .map(|stop| stop.to_fancy_string())
+ // .intersperse(" ↓".to_string())
+ .for_each(|l| writeln!(f, " {}\n ↓", l).unwrap());
}
Ok(())
}
}
-
impl std::fmt::Display for Status {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self.checked_in {
@@ -179,14 +171,18 @@ impl std::fmt::Display for Status {
f,
"checked in to: {}.\n\n\
stops:\n {}\n ↓\n{} {}",
- self.train.as_ref().map(|t| t.to_string()).unwrap_or("".to_string()).green(),
+ self.train
+ .as_ref()
+ .map(|t| t.to_string())
+ .unwrap_or("".to_string())
+ .green(),
self.from_station.to_fancy_string(),
Trip(&self.intermediate_stops),
self.to_station
.as_ref()
.map(|s| s.to_fancy_string())
.unwrap_or_else(|| "🚄 Fahrt ins Blaue".blue().to_string())
- )
+ ),
}
}
}