From af28994bb5e3128e3a25386b8f609428b97e057c Mon Sep 17 00:00:00 2001 From: Wil Boayue Date: Mon, 28 Aug 2023 16:56:30 -0700 Subject: [PATCH 1/3] issue-101. allow optional connection time --- examples/contract_details.rs | 2 +- examples/stream_bars.rs | 2 +- src/client.rs | 42 ++++++++++++++++++++++------------- src/client/tests.rs | 2 +- src/market_data/historical.rs | 8 ++++++- 5 files changed, 37 insertions(+), 19 deletions(-) diff --git a/examples/contract_details.rs b/examples/contract_details.rs index 02040753..41314929 100644 --- a/examples/contract_details.rs +++ b/examples/contract_details.rs @@ -5,7 +5,7 @@ fn main() -> anyhow::Result<()> { let client = Client::connect("127.0.0.1:4002", 100)?; println!("server_version: {}", client.server_version()); - println!("connection_time: {}", client.connection_time()); + println!("connection_time: {:?}", client.connection_time()); println!("managed_accounts: {}", client.managed_accounts()); println!("next_order_id: {}", client.next_order_id()); diff --git a/examples/stream_bars.rs b/examples/stream_bars.rs index 9bb87094..5d72a97d 100644 --- a/examples/stream_bars.rs +++ b/examples/stream_bars.rs @@ -28,7 +28,7 @@ fn main() -> anyhow::Result<()> { let client = Client::connect("127.0.0.1:4002", 100).unwrap(); println!("server_version: {}", client.server_version()); - println!("server_time: {}", client.connection_time()); + println!("server_time: {:?}", client.connection_time()); println!("managed_accounts: {}", client.managed_accounts()); println!("next_order_id: {}", client.next_order_id()); diff --git a/src/client.rs b/src/client.rs index 7618dfe1..41f29858 100644 --- a/src/client.rs +++ b/src/client.rs @@ -35,8 +35,8 @@ pub struct Client { pub(crate) server_version: i32, /// IB Server time // pub server_time: OffsetDateTime, - pub(crate) connection_time: OffsetDateTime, - pub(crate) time_zone: &'static Tz, + pub(crate) connection_time: Option, + pub(crate) time_zone: Option<&'static Tz>, managed_accounts: String, client_id: i32, // ID of client. @@ -62,7 +62,7 @@ impl Client { /// let client = Client::connect("127.0.0.1:4002", 100).expect("connection failed"); /// /// println!("server_version: {}", client.server_version()); - /// println!("connection_time: {}", client.connection_time()); + /// println!("connection_time: {:?}", client.connection_time()); /// println!("managed_accounts: {}", client.managed_accounts()); /// println!("next_order_id: {}", client.next_order_id()); /// ``` @@ -74,8 +74,8 @@ impl Client { fn do_connect(client_id: i32, message_bus: RefCell>) -> Result { let mut client = Client { server_version: 0, - connection_time: OffsetDateTime::now_utc(), - time_zone: time_tz::timezones::db::UTC, + connection_time: None, + time_zone: None, managed_accounts: String::from(""), message_bus, client_id, @@ -200,8 +200,8 @@ impl Client { } /// The time of the server when the client connected - pub fn connection_time(&self) -> &OffsetDateTime { - &self.connection_time + pub fn connection_time(&self) -> Option { + self.connection_time.clone() } /// Returns the managed accounts. @@ -889,8 +889,8 @@ impl Client { pub(crate) fn stubbed(message_bus: RefCell>, server_version: i32) -> Client { Client { server_version: server_version, - connection_time: OffsetDateTime::now_utc(), - time_zone: time_tz::timezones::db::UTC, + connection_time: None, + time_zone: None, managed_accounts: String::from(""), message_bus, client_id: 100, @@ -964,17 +964,29 @@ impl Debug for Client { } // Parses following format: 20230405 22:20:39 PST -fn parse_connection_time(connection_time: &str) -> (OffsetDateTime, &'static Tz) { +fn parse_connection_time(connection_time: &str) -> (Option, Option<&'static Tz>) { let parts: Vec<&str> = connection_time.split(' ').collect(); let zones = timezones::find_by_name(parts[2]); let format = format_description!("[year][month][day] [hour]:[minute]:[second]"); - let date = time::PrimitiveDateTime::parse(format!("{} {}", parts[0], parts[1]).as_str(), format).unwrap(); - let timezone = zones[0]; - match date.assume_timezone(timezone) { - OffsetResult::Some(date) => (date, timezone), - _ => (OffsetDateTime::now_utc(), time_tz::timezones::db::UTC), + let date_str = format!("{} {}", parts[0], parts[1]); + let date = time::PrimitiveDateTime::parse(date_str.as_str(), format); + match date { + Ok(connected_at) => { + let timezone = zones[0]; + match connected_at.assume_timezone(timezone) { + OffsetResult::Some(date) => (Some(date), Some(timezone)), + _ => { + error!("error setting timezone"); + (None, None) + } + } + } + Err(err) => { + error!("could not parse connection time from {date_str}: {err}"); + return (None, None); + } } } diff --git a/src/client/tests.rs b/src/client/tests.rs index 28bb62db..2c4138e3 100644 --- a/src/client/tests.rs +++ b/src/client/tests.rs @@ -10,6 +10,6 @@ fn test_parse_connection_time() { let la = timezones::db::america::LOS_ANGELES; if let OffsetResult::Some(other) = datetime!(2023-04-05 22:20:39).assume_timezone(la) { - assert_eq!(connection_time, other); + assert_eq!(connection_time, Some(other)); } } diff --git a/src/market_data/historical.rs b/src/market_data/historical.rs index 836a7c60..a5efe360 100644 --- a/src/market_data/historical.rs +++ b/src/market_data/historical.rs @@ -3,6 +3,7 @@ use std::fmt::Debug; use log::error; use time::{Date, OffsetDateTime}; +use time_tz::Tz; use crate::client::transport::ResponseIterator; use crate::contracts::Contract; @@ -360,8 +361,13 @@ pub(crate) fn historical_data( let mut messages = client.send_request(request_id, request)?; if let Some(mut message) = messages.next() { + let time_zone = if let Some(tz) = client.time_zone { + tz + } else { + time_tz::timezones::db::UTC + }; match message.message_type() { - IncomingMessages::HistoricalData => decoders::decode_historical_data(client.server_version, client.time_zone, &mut message), + IncomingMessages::HistoricalData => decoders::decode_historical_data(client.server_version, time_zone, &mut message), IncomingMessages::Error => Err(Error::Simple(message.peek_string(4))), _ => Err(Error::Simple(format!("unexpected message: {:?}", message.message_type()))), } From a603a832488a69d02e315ac9158badaf3711e4e9 Mon Sep 17 00:00:00 2001 From: Wil Boayue Date: Mon, 28 Aug 2023 17:08:15 -0700 Subject: [PATCH 2/3] allow missing time only --- src/client.rs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/src/client.rs b/src/client.rs index 41f29858..e6168ec2 100644 --- a/src/client.rs +++ b/src/client.rs @@ -968,24 +968,27 @@ fn parse_connection_time(connection_time: &str) -> (Option, Opti let parts: Vec<&str> = connection_time.split(' ').collect(); let zones = timezones::find_by_name(parts[2]); + if zones.is_empty() { + error!("time zone not found for {}", parts[2]); + return (None, None); + } + + let timezone = zones[0]; let format = format_description!("[year][month][day] [hour]:[minute]:[second]"); let date_str = format!("{} {}", parts[0], parts[1]); let date = time::PrimitiveDateTime::parse(date_str.as_str(), format); match date { - Ok(connected_at) => { - let timezone = zones[0]; - match connected_at.assume_timezone(timezone) { - OffsetResult::Some(date) => (Some(date), Some(timezone)), - _ => { - error!("error setting timezone"); - (None, None) - } + Ok(connected_at) => match connected_at.assume_timezone(timezone) { + OffsetResult::Some(date) => (Some(date), Some(timezone)), + _ => { + error!("error setting timezone"); + (None, Some(timezone)) } - } + }, Err(err) => { error!("could not parse connection time from {date_str}: {err}"); - return (None, None); + return (None, Some(timezone)); } } } From fa2cbc60a9d7b8e5e4c5f07fc1c8eb63e6e4241f Mon Sep 17 00:00:00 2001 From: Wil Boayue Date: Mon, 28 Aug 2023 20:16:23 -0700 Subject: [PATCH 3/3] added warning --- src/market_data/historical.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/market_data/historical.rs b/src/market_data/historical.rs index a5efe360..58608506 100644 --- a/src/market_data/historical.rs +++ b/src/market_data/historical.rs @@ -1,7 +1,7 @@ use std::collections::VecDeque; use std::fmt::Debug; -use log::error; +use log::{error, warn}; use time::{Date, OffsetDateTime}; use time_tz::Tz; @@ -364,6 +364,7 @@ pub(crate) fn historical_data( let time_zone = if let Some(tz) = client.time_zone { tz } else { + warn!("server timezone unknown. assuming UTC, but that may be incorrect!"); time_tz::timezones::db::UTC }; match message.message_type() {