time library support #70
@ -17,6 +17,8 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
## Added
|
## Added
|
||||||
|
|
||||||
- `From<$EcssEnum$TY> from $TY` for the ECSS enum type definitions.
|
- `From<$EcssEnum$TY> from $TY` for the ECSS enum type definitions.
|
||||||
|
- Added basic support conversions to the `time` library. Introduce new `chrono` and `timelib`
|
||||||
|
feature gate
|
||||||
|
|
||||||
# [v0.11.0-rc.0] 2024-03-04
|
# [v0.11.0-rc.0] 2024-03-04
|
||||||
|
|
||||||
|
@ -34,9 +34,15 @@ optional = true
|
|||||||
default-features = false
|
default-features = false
|
||||||
features = ["derive"]
|
features = ["derive"]
|
||||||
|
|
||||||
|
[dependencies.time]
|
||||||
|
version = "0.3"
|
||||||
|
default-features = false
|
||||||
|
optional = true
|
||||||
|
|
||||||
[dependencies.chrono]
|
[dependencies.chrono]
|
||||||
version = "0.4"
|
version = "0.4"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
optional = true
|
||||||
|
|
||||||
[dependencies.num-traits]
|
[dependencies.num-traits]
|
||||||
version = "0.2"
|
version = "0.2"
|
||||||
@ -50,6 +56,8 @@ default = ["std"]
|
|||||||
std = ["chrono/std", "chrono/clock", "alloc", "thiserror"]
|
std = ["chrono/std", "chrono/clock", "alloc", "thiserror"]
|
||||||
serde = ["dep:serde", "chrono/serde"]
|
serde = ["dep:serde", "chrono/serde"]
|
||||||
alloc = ["postcard/alloc", "chrono/alloc"]
|
alloc = ["postcard/alloc", "chrono/alloc"]
|
||||||
|
chrono = ["dep:chrono"]
|
||||||
|
timelib = ["dep:time"]
|
||||||
|
|
||||||
[package.metadata.docs.rs]
|
[package.metadata.docs.rs]
|
||||||
all-features = true
|
all-features = true
|
||||||
|
@ -43,6 +43,8 @@ deserializing them with an appropriate `serde` provider like
|
|||||||
## Optional Features
|
## Optional Features
|
||||||
|
|
||||||
- [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s
|
- [`serde`](https://serde.rs/): Adds `serde` support for most types by adding `Serialize` and `Deserialize` `derive`s
|
||||||
|
- [`chrono`](https://crates.io/crates/chrono): Add basic support for the `chrono` time library.
|
||||||
|
- [`timelib`](https://crates.io/crates/time): Add basic support for the `time` time library.
|
||||||
|
|
||||||
# Examples
|
# Examples
|
||||||
|
|
||||||
|
@ -8,6 +8,7 @@ use super::*;
|
|||||||
use crate::private::Sealed;
|
use crate::private::Sealed;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use alloc::boxed::Box;
|
use alloc::boxed::Box;
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
#[cfg(feature = "alloc")]
|
#[cfg(feature = "alloc")]
|
||||||
use core::any::Any;
|
use core::any::Any;
|
||||||
@ -588,6 +589,20 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
|
|||||||
self.calc_unix_seconds(unix_days_seconds, ms_of_day);
|
self.calc_unix_seconds(unix_days_seconds, ms_of_day);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn calc_ns_since_last_second(&self) -> u32 {
|
||||||
|
let mut ns_since_last_sec = (self.ms_of_day % 1000) * 10_u32.pow(6);
|
||||||
|
match self.submillis_precision() {
|
||||||
|
SubmillisPrecision::Microseconds => {
|
||||||
|
ns_since_last_sec += self.submillis() * 1000;
|
||||||
|
}
|
||||||
|
SubmillisPrecision::Picoseconds => {
|
||||||
|
ns_since_last_sec += self.submillis() / 1000;
|
||||||
|
}
|
||||||
|
_ => (),
|
||||||
|
}
|
||||||
|
ns_since_last_sec
|
||||||
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn calc_unix_seconds(&mut self, mut unix_days_seconds: i64, ms_of_day: u32) {
|
fn calc_unix_seconds(&mut self, mut unix_days_seconds: i64, ms_of_day: u32) {
|
||||||
let seconds_of_day = (ms_of_day / 1000) as i64;
|
let seconds_of_day = (ms_of_day / 1000) as i64;
|
||||||
@ -599,7 +614,11 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
|
|||||||
self.unix_stamp = UnixTimestamp::const_new(unix_days_seconds, (ms_of_day % 1000) as u16);
|
self.unix_stamp = UnixTimestamp::const_new(unix_days_seconds, (ms_of_day % 1000) as u16);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn calc_date_time(&self, ns_since_last_second: u32) -> Option<DateTime<Utc>> {
|
#[cfg(feature = "chrono")]
|
||||||
|
fn calc_chrono_date_time(
|
||||||
|
&self,
|
||||||
|
ns_since_last_second: u32,
|
||||||
|
) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||||
assert!(
|
assert!(
|
||||||
ns_since_last_second < 10_u32.pow(9),
|
ns_since_last_second < 10_u32.pow(9),
|
||||||
"Invalid MS since last second"
|
"Invalid MS since last second"
|
||||||
@ -612,6 +631,17 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
|
|||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timelib")]
|
||||||
|
fn calc_timelib_date_time(
|
||||||
|
&self,
|
||||||
|
ns_since_last_second: u32,
|
||||||
|
) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
|
||||||
|
Ok(
|
||||||
|
time::OffsetDateTime::from_unix_timestamp(self.unix_stamp.unix_seconds)?
|
||||||
|
+ time::Duration::nanoseconds(ns_since_last_second.into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
fn length_check(&self, buf: &[u8], len_as_bytes: usize) -> Result<(), TimestampError> {
|
fn length_check(&self, buf: &[u8], len_as_bytes: usize) -> Result<(), TimestampError> {
|
||||||
if buf.len() < len_as_bytes {
|
if buf.len() < len_as_bytes {
|
||||||
return Err(TimestampError::ByteConversion(
|
return Err(TimestampError::ByteConversion(
|
||||||
@ -1155,18 +1185,16 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CcsdsTimeProvider for TimeProvider<Pro
|
|||||||
self.unix_stamp
|
self.unix_stamp
|
||||||
}
|
}
|
||||||
|
|
||||||
fn date_time(&self) -> Option<DateTime<Utc>> {
|
#[cfg(feature = "chrono")]
|
||||||
let mut ns_since_last_sec = (self.ms_of_day % 1000) * 10_u32.pow(6);
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
||||||
match self.submillis_precision() {
|
fn chrono_date_time(&self) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||||
SubmillisPrecision::Microseconds => {
|
self.calc_chrono_date_time(self.calc_ns_since_last_second())
|
||||||
ns_since_last_sec += self.submillis() * 1000;
|
}
|
||||||
}
|
|
||||||
SubmillisPrecision::Picoseconds => {
|
#[cfg(feature = "timelib")]
|
||||||
ns_since_last_sec += self.submillis() / 1000;
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
|
||||||
}
|
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
|
||||||
_ => (),
|
self.calc_timelib_date_time(self.calc_ns_since_last_second())
|
||||||
}
|
|
||||||
self.calc_date_time(ns_since_last_sec)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1329,7 +1357,7 @@ mod tests {
|
|||||||
time_stamper.p_field(),
|
time_stamper.p_field(),
|
||||||
(1, [(CcsdsTimeCodes::Cds as u8) << 4, 0])
|
(1, [(CcsdsTimeCodes::Cds as u8) << 4, 0])
|
||||||
);
|
);
|
||||||
let date_time = time_stamper.date_time().unwrap();
|
let date_time = time_stamper.chrono_date_time().unwrap();
|
||||||
assert_eq!(date_time.year(), 1958);
|
assert_eq!(date_time.year(), 1958);
|
||||||
assert_eq!(date_time.month(), 1);
|
assert_eq!(date_time.month(), 1);
|
||||||
assert_eq!(date_time.day(), 1);
|
assert_eq!(date_time.day(), 1);
|
||||||
@ -1346,7 +1374,7 @@ mod tests {
|
|||||||
time_stamper.submillis_precision(),
|
time_stamper.submillis_precision(),
|
||||||
SubmillisPrecision::Absent
|
SubmillisPrecision::Absent
|
||||||
);
|
);
|
||||||
let date_time = time_stamper.date_time().unwrap();
|
let date_time = time_stamper.chrono_date_time().unwrap();
|
||||||
assert_eq!(date_time.year(), 1970);
|
assert_eq!(date_time.year(), 1970);
|
||||||
assert_eq!(date_time.month(), 1);
|
assert_eq!(date_time.month(), 1);
|
||||||
assert_eq!(date_time.day(), 1);
|
assert_eq!(date_time.day(), 1);
|
||||||
@ -1539,7 +1567,7 @@ mod tests {
|
|||||||
timestamp_now: TimeProvider<T>,
|
timestamp_now: TimeProvider<T>,
|
||||||
compare_stamp: DateTime<Utc>,
|
compare_stamp: DateTime<Utc>,
|
||||||
) {
|
) {
|
||||||
let dt = timestamp_now.date_time().unwrap();
|
let dt = timestamp_now.chrono_date_time().unwrap();
|
||||||
if compare_stamp.year() > dt.year() {
|
if compare_stamp.year() > dt.year() {
|
||||||
assert_eq!(compare_stamp.year() - dt.year(), 1);
|
assert_eq!(compare_stamp.year() - dt.year(), 1);
|
||||||
} else {
|
} else {
|
||||||
@ -1736,7 +1764,7 @@ mod tests {
|
|||||||
time_provider.ms_of_day,
|
time_provider.ms_of_day,
|
||||||
30 * 1000 + 49 * 60 * 1000 + 16 * 60 * 60 * 1000 + subsec_millis
|
30 * 1000 + 49 * 60 * 1000 + 16 * 60 * 60 * 1000 + subsec_millis
|
||||||
);
|
);
|
||||||
assert_eq!(time_provider.date_time().unwrap(), datetime_utc);
|
assert_eq!(time_provider.chrono_date_time().unwrap(), datetime_utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1790,7 +1818,7 @@ mod tests {
|
|||||||
SubmillisPrecision::Microseconds
|
SubmillisPrecision::Microseconds
|
||||||
);
|
);
|
||||||
assert_eq!(time_provider.submillis(), 500);
|
assert_eq!(time_provider.submillis(), 500);
|
||||||
assert_eq!(time_provider.date_time().unwrap(), datetime_utc);
|
assert_eq!(time_provider.chrono_date_time().unwrap(), datetime_utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1844,7 +1872,7 @@ mod tests {
|
|||||||
SubmillisPrecision::Picoseconds
|
SubmillisPrecision::Picoseconds
|
||||||
);
|
);
|
||||||
assert_eq!(time_provider.submillis(), submilli_nanos * 1000);
|
assert_eq!(time_provider.submillis(), submilli_nanos * 1000);
|
||||||
assert_eq!(time_provider.date_time().unwrap(), datetime_utc);
|
assert_eq!(time_provider.chrono_date_time().unwrap(), datetime_utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1917,7 +1945,7 @@ mod tests {
|
|||||||
time_provider.ms_of_day,
|
time_provider.ms_of_day,
|
||||||
30 * 1000 + 49 * 60 * 1000 + 16 * 60 * 60 * 1000 + subsec_millis
|
30 * 1000 + 49 * 60 * 1000 + 16 * 60 * 60 * 1000 + subsec_millis
|
||||||
);
|
);
|
||||||
let dt_back = time_provider.date_time().unwrap();
|
let dt_back = time_provider.chrono_date_time().unwrap();
|
||||||
assert_eq!(datetime_utc, dt_back);
|
assert_eq!(datetime_utc, dt_back);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -3,7 +3,10 @@
|
|||||||
//!
|
//!
|
||||||
//! The core data structure to do this is the [TimeProviderCcsdsEpoch] struct.
|
//! The core data structure to do this is the [TimeProviderCcsdsEpoch] struct.
|
||||||
use super::*;
|
use super::*;
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
|
|
||||||
use core::fmt::Debug;
|
use core::fmt::Debug;
|
||||||
use core::ops::{Add, AddAssign};
|
use core::ops::{Add, AddAssign};
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
@ -313,8 +316,10 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn from_date_time(
|
#[cfg(feature = "chrono")]
|
||||||
dt: &DateTime<Utc>,
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
||||||
|
pub fn from_chrono_date_time(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
res: FractionalResolution,
|
res: FractionalResolution,
|
||||||
leap_seconds: u32,
|
leap_seconds: u32,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, TimestampError> {
|
||||||
@ -688,18 +693,32 @@ impl CcsdsTimeProvider for TimeProviderCcsdsEpoch {
|
|||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
fn date_time(&self) -> Option<DateTime<Utc>> {
|
#[cfg(feature = "chrono")]
|
||||||
let unix_seconds = self.unix_seconds();
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
||||||
let ns = if let Some(fractional_part) = self.fractions {
|
fn chrono_date_time(&self) -> Option<chrono::DateTime<chrono::Utc>> {
|
||||||
convert_fractional_part_to_ns(fractional_part)
|
let mut ns = 0;
|
||||||
} else {
|
if let Some(fractional_part) = self.fractions {
|
||||||
0
|
ns = convert_fractional_part_to_ns(fractional_part);
|
||||||
};
|
}
|
||||||
if let LocalResult::Single(res) = Utc.timestamp_opt(unix_seconds, ns as u32) {
|
if let LocalResult::Single(res) = chrono::Utc.timestamp_opt(self.unix_seconds(), ns as u32)
|
||||||
|
{
|
||||||
return Some(res);
|
return Some(res);
|
||||||
}
|
}
|
||||||
None
|
None
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "timelib")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
|
||||||
|
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
|
||||||
|
let mut ns = 0;
|
||||||
|
if let Some(fractional_part) = self.fractions {
|
||||||
|
ns = convert_fractional_part_to_ns(fractional_part);
|
||||||
|
}
|
||||||
|
Ok(
|
||||||
|
time::OffsetDateTime::from_unix_timestamp(self.unix_seconds())?
|
||||||
|
+ time::Duration::nanoseconds(ns as i64),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn get_provider_values_after_duration_addition(
|
fn get_provider_values_after_duration_addition(
|
||||||
@ -820,7 +839,7 @@ mod tests {
|
|||||||
assert_eq!(counter.1, 0);
|
assert_eq!(counter.1, 0);
|
||||||
let fractions = zero_cuc.width_fractions_pair();
|
let fractions = zero_cuc.width_fractions_pair();
|
||||||
assert!(fractions.is_none());
|
assert!(fractions.is_none());
|
||||||
let dt = zero_cuc.date_time();
|
let dt = zero_cuc.chrono_date_time();
|
||||||
assert!(dt.is_some());
|
assert!(dt.is_some());
|
||||||
let dt = dt.unwrap();
|
let dt = dt.unwrap();
|
||||||
assert_eq!(dt.year(), 1957);
|
assert_eq!(dt.year(), 1957);
|
||||||
@ -865,7 +884,7 @@ mod tests {
|
|||||||
let cuc_now = TimeProviderCcsdsEpoch::from_now(FractionalResolution::SixtyNs, LEAP_SECONDS);
|
let cuc_now = TimeProviderCcsdsEpoch::from_now(FractionalResolution::SixtyNs, LEAP_SECONDS);
|
||||||
assert!(cuc_now.is_ok());
|
assert!(cuc_now.is_ok());
|
||||||
let cuc_now = cuc_now.unwrap();
|
let cuc_now = cuc_now.unwrap();
|
||||||
let dt_opt = cuc_now.date_time();
|
let dt_opt = cuc_now.chrono_date_time();
|
||||||
assert!(dt_opt.is_some());
|
assert!(dt_opt.is_some());
|
||||||
let dt = dt_opt.unwrap();
|
let dt = dt_opt.unwrap();
|
||||||
let diff = dt - now;
|
let diff = dt - now;
|
||||||
@ -1298,7 +1317,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_from_dt() {
|
fn test_from_dt() {
|
||||||
let dt = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
|
let dt = Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
|
||||||
let cuc = TimeProviderCcsdsEpoch::from_date_time(
|
let cuc = TimeProviderCcsdsEpoch::from_chrono_date_time(
|
||||||
&dt,
|
&dt,
|
||||||
FractionalResolution::Seconds,
|
FractionalResolution::Seconds,
|
||||||
LEAP_SECONDS,
|
LEAP_SECONDS,
|
||||||
|
@ -236,7 +236,13 @@ pub trait CcsdsTimeProvider {
|
|||||||
UnixTimestamp::const_new(self.unix_seconds(), self.subsecond_millis())
|
UnixTimestamp::const_new(self.unix_seconds(), self.subsecond_millis())
|
||||||
}
|
}
|
||||||
|
|
||||||
fn date_time(&self) -> Option<DateTime<Utc>>;
|
#[cfg(feature = "chrono")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
||||||
|
fn chrono_date_time(&self) -> Option<chrono::DateTime<chrono::Utc>>;
|
||||||
|
|
||||||
|
#[cfg(feature = "timelib")]
|
||||||
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
|
||||||
|
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange>;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// UNIX timestamp: Elapsed seconds since 1970-01-01T00:00:00+00:00.
|
/// UNIX timestamp: Elapsed seconds since 1970-01-01T00:00:00+00:00.
|
||||||
|
Loading…
Reference in New Issue
Block a user