diff --git a/src/config.rs b/src/config.rs index 176f1f6..dd1cfb9 100644 --- a/src/config.rs +++ b/src/config.rs @@ -91,7 +91,9 @@ impl DateTimeConfigBuilder { pub fn build(self) -> DateTimeConfig { DateTimeConfig { timestamp_unit: self.timestamp_unit.unwrap_or_default(), - time_config: self.time_config.unwrap_or_default(), + time_config: self + .time_config + .unwrap_or_else(|| TimeConfigBuilder::new().timestamp_unit(TimestampUnit::Second).build()), } } } @@ -105,6 +107,7 @@ impl DateTimeConfig { #[derive(Debug, Clone, Default, PartialEq)] pub struct TimeConfig { pub microseconds_precision_overflow_behavior: MicrosecondsPrecisionOverflowBehavior, + pub timestamp_unit: TimestampUnit, pub unix_timestamp_offset: Option, } @@ -117,6 +120,7 @@ impl TimeConfig { #[derive(Debug, Clone, Default)] pub struct TimeConfigBuilder { microseconds_precision_overflow_behavior: Option, + timestamp_unit: Option, unix_timestamp_offset: Option, } @@ -131,6 +135,10 @@ impl TimeConfigBuilder { self.microseconds_precision_overflow_behavior = Some(microseconds_precision_overflow_behavior); self } + pub fn timestamp_unit(mut self, timestamp_unit: TimestampUnit) -> Self { + self.timestamp_unit = Some(timestamp_unit); + self + } pub fn unix_timestamp_offset(mut self, unix_timestamp_offset: Option) -> Self { self.unix_timestamp_offset = unix_timestamp_offset; self @@ -138,6 +146,7 @@ impl TimeConfigBuilder { pub fn build(self) -> TimeConfig { TimeConfig { microseconds_precision_overflow_behavior: self.microseconds_precision_overflow_behavior.unwrap_or_default(), + timestamp_unit: self.timestamp_unit.unwrap_or_default(), unix_timestamp_offset: self.unix_timestamp_offset, } } diff --git a/src/date.rs b/src/date.rs index c7bb2fd..8fc5d50 100644 --- a/src/date.rs +++ b/src/date.rs @@ -221,7 +221,7 @@ impl Date { /// assert_eq!(d.to_string(), "2022-06-07"); /// ``` pub fn from_timestamp(timestamp: i64, require_exact: bool, config: &DateConfig) -> Result { - let (seconds, microseconds) = timestamp_to_seconds_micros(timestamp, config.timestamp_unit)?; + let (seconds, microseconds) = timestamp_to_seconds_micros(timestamp, config.timestamp_unit, MS_WATERSHED)?; let (d, remaining_seconds) = Self::from_timestamp_calc(seconds)?; if require_exact && (remaining_seconds != 0 || microseconds != 0) { return Err(ParseError::DateNotExact); diff --git a/src/datetime.rs b/src/datetime.rs index 0c30d8e..ba95c3b 100644 --- a/src/datetime.rs +++ b/src/datetime.rs @@ -439,7 +439,8 @@ impl DateTime { timestamp_microsecond: u32, config: &DateTimeConfig, ) -> Result { - let (mut second, extra_microsecond) = timestamp_to_seconds_micros(timestamp, config.timestamp_unit)?; + let (mut second, extra_microsecond) = + timestamp_to_seconds_micros(timestamp, config.timestamp_unit, MS_WATERSHED)?; let mut total_microsecond = timestamp_microsecond .checked_add(extra_microsecond) .ok_or(ParseError::TimeTooLarge)?; diff --git a/src/time.rs b/src/time.rs index 13305b0..95885b8 100644 --- a/src/time.rs +++ b/src/time.rs @@ -4,8 +4,11 @@ use std::fmt; use std::str::FromStr; use crate::config::TimeConfigBuilder; +use crate::util::timestamp_to_seconds_micros; use crate::{get_digit, get_digit_unchecked, ConfigError, ParseError, TimeConfig}; +pub(crate) const TIME_MS_WATERSHED: i64 = 86_400; + /// A Time /// /// Allowed formats: @@ -259,7 +262,7 @@ impl Time { /// /// # Arguments /// - /// * `timestamp_second` - timestamp in seconds + /// * `timestamp` - timestamp in seconds /// * `timestamp_microsecond` - microseconds fraction of a second timestamp /// * `config` - the `TimeConfig` to use /// @@ -274,17 +277,21 @@ impl Time { /// assert_eq!(d.to_string(), "01:02:20.000123"); /// ``` pub fn from_timestamp_with_config( - timestamp_second: u32, + timestamp: u32, timestamp_microsecond: u32, config: &TimeConfig, ) -> Result { - let mut second = timestamp_second; - let mut microsecond = timestamp_microsecond; - if microsecond >= 1_000_000 { + let (mut second, extra_microsecond) = + timestamp_to_seconds_micros(timestamp.into(), config.timestamp_unit, TIME_MS_WATERSHED)?; + let mut total_microsecond = timestamp_microsecond + .checked_add(extra_microsecond) + .ok_or(ParseError::TimeTooLarge)?; + + if total_microsecond >= 1_000_000 { second = second - .checked_add(microsecond / 1_000_000) + .checked_add(total_microsecond as i64 / 1_000_000) .ok_or(ParseError::TimeTooLarge)?; - microsecond %= 1_000_000; + total_microsecond %= 1_000_000; } if second >= 86_400 { return Err(ParseError::TimeTooLarge); @@ -293,7 +300,7 @@ impl Time { hour: (second / 3600) as u8, minute: ((second % 3600) / 60) as u8, second: (second % 60) as u8, - microsecond, + microsecond: total_microsecond, tz_offset: config.unix_timestamp_offset, }) } diff --git a/src/util.rs b/src/util.rs index 556d2e3..a86ad59 100644 --- a/src/util.rs +++ b/src/util.rs @@ -1,9 +1,8 @@ -use crate::date::MS_WATERSHED; use crate::{ParseError, TimestampUnit}; -pub(crate) fn timestamp_watershed(timestamp: i64) -> Result<(i64, u32), ParseError> { +pub(crate) fn timestamp_watershed(timestamp: i64, infer_threshold: i64) -> Result<(i64, u32), ParseError> { let ts_abs = timestamp.checked_abs().ok_or(ParseError::DateTooSmall)?; - if ts_abs <= MS_WATERSHED { + if ts_abs <= infer_threshold { return Ok((timestamp, 0)); } let mut seconds = timestamp / 1_000; @@ -15,7 +14,11 @@ pub(crate) fn timestamp_watershed(timestamp: i64) -> Result<(i64, u32), ParseErr Ok((seconds, microseconds as u32)) } -pub fn timestamp_to_seconds_micros(timestamp: i64, unit: TimestampUnit) -> Result<(i64, u32), ParseError> { +pub fn timestamp_to_seconds_micros( + timestamp: i64, + unit: TimestampUnit, + infer_threshold: i64, +) -> Result<(i64, u32), ParseError> { match unit { TimestampUnit::Second => Ok((timestamp, 0)), TimestampUnit::Millisecond => { @@ -27,6 +30,6 @@ pub fn timestamp_to_seconds_micros(timestamp: i64, unit: TimestampUnit) -> Resul } Ok((seconds, microseconds as u32)) } - TimestampUnit::Infer => timestamp_watershed(timestamp), + TimestampUnit::Infer => timestamp_watershed(timestamp, infer_threshold), } } diff --git a/tests/main.rs b/tests/main.rs index ec17c74..2c05e86 100644 --- a/tests/main.rs +++ b/tests/main.rs @@ -1543,6 +1543,7 @@ fn test_time_config_builder() { TimeConfigBuilder::new().build(), TimeConfig { microseconds_precision_overflow_behavior: MicrosecondsPrecisionOverflowBehavior::Error, + timestamp_unit: TimestampUnit::Infer, unix_timestamp_offset: None, } ); @@ -1568,6 +1569,7 @@ fn test_datetimetime_config_builder() { timestamp_unit: TimestampUnit::Infer, time_config: TimeConfig { microseconds_precision_overflow_behavior: MicrosecondsPrecisionOverflowBehavior::Error, + timestamp_unit: TimestampUnit::Second, unix_timestamp_offset: None, } }