UnixTimestamp now has nanosecond precision
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
This commit is contained in:
parent
aae246dca6
commit
05b6503851
14
CHANGELOG.md
14
CHANGELOG.md
@ -8,6 +8,9 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
|
|
||||||
# [unreleased]
|
# [unreleased]
|
||||||
|
|
||||||
|
Major API changes for the time API. If you are using the time API, it is strongly recommended
|
||||||
|
to check all the API changes.
|
||||||
|
|
||||||
## Fixed
|
## Fixed
|
||||||
|
|
||||||
- CUC timestamp was fixed to include leap second corrections because it is based on the TAI
|
- CUC timestamp was fixed to include leap second corrections because it is based on the TAI
|
||||||
@ -20,6 +23,17 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
|
|||||||
- Added basic support conversions to the `time` library. Introduce new `chrono` and `timelib`
|
- Added basic support conversions to the `time` library. Introduce new `chrono` and `timelib`
|
||||||
feature gate
|
feature gate
|
||||||
|
|
||||||
|
## Changed
|
||||||
|
|
||||||
|
- `UnixTimestamp::new` renamed to `UnixTimestamp::new_checked`.
|
||||||
|
- `UnixTimestamp` now has a nanosecond subsecond precision. The `new` constructor now expects
|
||||||
|
nanoseconds as the second argument.
|
||||||
|
- Added new `UnixTimestamp::new_subsec_millis` and `UnixTimestamp::new_subsec_millis_checked` API
|
||||||
|
to still allow creating a timestamp with only millisecond subsecond resolution.
|
||||||
|
- `CcsdsTimeProvider` now has a new `subsecond_nanos` method in addition to a default
|
||||||
|
implementation for the `subsecond_millis` method.
|
||||||
|
- `CcsdsTimeProvider::date_time` renamed to `CcsdsTimeProvider::chrono_date_time`
|
||||||
|
|
||||||
# [v0.11.0-rc.0] 2024-03-04
|
# [v0.11.0-rc.0] 2024-03-04
|
||||||
|
|
||||||
## Added
|
## Added
|
||||||
|
@ -48,8 +48,9 @@ optional = true
|
|||||||
version = "0.2"
|
version = "0.2"
|
||||||
default-features = false
|
default-features = false
|
||||||
|
|
||||||
[dev-dependencies.postcard]
|
[dev-dependencies]
|
||||||
version = "1"
|
postcard = "1"
|
||||||
|
chrono = "0.4"
|
||||||
|
|
||||||
[features]
|
[features]
|
||||||
default = ["std"]
|
default = ["std"]
|
||||||
|
336
src/time/cds.rs
336
src/time/cds.rs
@ -4,20 +4,40 @@
|
|||||||
//! The core data structure to do this is the [TimeProvider] struct and the
|
//! The core data structure to do this is the [TimeProvider] struct and the
|
||||||
//! [get_dyn_time_provider_from_bytes] function to retrieve correct instances of the
|
//! [get_dyn_time_provider_from_bytes] function to retrieve correct instances of the
|
||||||
//! struct from a bytestream.
|
//! struct from a bytestream.
|
||||||
use super::*;
|
|
||||||
use crate::private::Sealed;
|
use crate::private::Sealed;
|
||||||
#[cfg(feature = "alloc")]
|
use crate::ByteConversionError;
|
||||||
use alloc::boxed::Box;
|
|
||||||
#[cfg(feature = "chrono")]
|
|
||||||
use chrono::Datelike;
|
|
||||||
#[cfg(feature = "alloc")]
|
|
||||||
use core::any::Any;
|
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use core::fmt::Debug;
|
use core::fmt::{Debug, Display, Formatter};
|
||||||
use core::ops::{Add, AddAssign};
|
use core::ops::{Add, AddAssign};
|
||||||
use core::time::Duration;
|
use core::time::Duration;
|
||||||
|
|
||||||
use delegate::delegate;
|
use delegate::delegate;
|
||||||
|
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use super::StdTimestampError;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::error::Error;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::time::{SystemTime, SystemTimeError};
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
|
use chrono::Datelike;
|
||||||
|
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use super::ccsds_time_code_from_p_field;
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use alloc::boxed::Box;
|
||||||
|
#[cfg(feature = "alloc")]
|
||||||
|
use core::any::Any;
|
||||||
|
|
||||||
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Serialize, Deserialize};
|
||||||
|
|
||||||
|
use super::{
|
||||||
|
ccsds_to_unix_days, unix_to_ccsds_days, CcsdsTimeCodes, CcsdsTimeProvider, TimeReader,
|
||||||
|
TimeWriter, TimestampError, UnixTimestamp, MS_PER_DAY, SECONDS_PER_DAY,
|
||||||
|
};
|
||||||
|
|
||||||
/// Base value for the preamble field for a time field parser to determine the time field type.
|
/// Base value for the preamble field for a time field parser to determine the time field type.
|
||||||
pub const P_FIELD_BASE: u8 = (CcsdsTimeCodes::Cds as u8) << 4;
|
pub const P_FIELD_BASE: u8 = (CcsdsTimeCodes::Cds as u8) << 4;
|
||||||
pub const MIN_CDS_FIELD_LEN: usize = 7;
|
pub const MIN_CDS_FIELD_LEN: usize = 7;
|
||||||
@ -165,7 +185,6 @@ pub struct TimeProvider<DaysLen: ProvidesDaysLength = DaysLen16Bits> {
|
|||||||
pfield: u8,
|
pfield: u8,
|
||||||
ccsds_days: DaysLen::FieldType,
|
ccsds_days: DaysLen::FieldType,
|
||||||
ms_of_day: u32,
|
ms_of_day: u32,
|
||||||
// submillis_precision: SubmillisPrecision,
|
|
||||||
submillis: u32,
|
submillis: u32,
|
||||||
/// This is not strictly necessary but still cached because it significantly simplifies the
|
/// This is not strictly necessary but still cached because it significantly simplifies the
|
||||||
/// calculation of [`DateTime<Utc>`].
|
/// calculation of [`DateTime<Utc>`].
|
||||||
@ -195,36 +214,47 @@ trait CdsConverter: CdsCommon {
|
|||||||
struct ConversionFromUnix {
|
struct ConversionFromUnix {
|
||||||
ccsds_days: u32,
|
ccsds_days: u32,
|
||||||
ms_of_day: u32,
|
ms_of_day: u32,
|
||||||
|
submilis_prec: SubmillisPrecision,
|
||||||
|
submillis: u32,
|
||||||
/// This is a side-product of the calculation of the CCSDS days. It is useful for
|
/// This is a side-product of the calculation of the CCSDS days. It is useful for
|
||||||
/// re-calculating the datetime at a later point and therefore supplied as well.
|
/// re-calculating the datetime at a later point and therefore supplied as well.
|
||||||
unix_days_seconds: i64,
|
unix_days_seconds: i64,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConversionFromUnix {
|
impl ConversionFromUnix {
|
||||||
fn new(unix_seconds: i64, subsec_millis: u32) -> Result<Self, TimestampError> {
|
fn new(
|
||||||
|
unix_seconds: i64,
|
||||||
|
subsec_nanos: u32,
|
||||||
|
precision: SubmillisPrecision,
|
||||||
|
) -> Result<Self, TimestampError> {
|
||||||
let (unix_days, secs_of_day) = calc_unix_days_and_secs_of_day(unix_seconds);
|
let (unix_days, secs_of_day) = calc_unix_days_and_secs_of_day(unix_seconds);
|
||||||
let ccsds_days = unix_to_ccsds_days(unix_days);
|
let ccsds_days = unix_to_ccsds_days(unix_days);
|
||||||
if ccsds_days == 0 && (secs_of_day > 0 || subsec_millis > 0) || ccsds_days < 0 {
|
if ccsds_days == 0 && (secs_of_day > 0 || subsec_nanos > 0) || ccsds_days < 0 {
|
||||||
let millis = if unix_seconds < 0 {
|
|
||||||
unix_seconds * 1000 - subsec_millis as i64
|
|
||||||
} else {
|
|
||||||
unix_seconds * 1000 + subsec_millis as i64
|
|
||||||
};
|
|
||||||
return Err(TimestampError::DateBeforeCcsdsEpoch(
|
return Err(TimestampError::DateBeforeCcsdsEpoch(
|
||||||
Utc.timestamp_millis_opt(millis).unwrap(),
|
UnixTimestamp::new_checked(unix_seconds, subsec_nanos)
|
||||||
|
.expect("unix timestamp creation failed"),
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
let ms_of_day = secs_of_day * 1000 + subsec_nanos / 10_u32.pow(6);
|
||||||
|
|
||||||
|
let submillis = match precision {
|
||||||
|
SubmillisPrecision::Microseconds => (subsec_nanos / 1_000) % 1000,
|
||||||
|
SubmillisPrecision::Picoseconds => (subsec_nanos % 10_u32.pow(6)) * 1000,
|
||||||
|
_ => 0,
|
||||||
|
};
|
||||||
Ok(Self {
|
Ok(Self {
|
||||||
ccsds_days: unix_to_ccsds_days(unix_days) as u32,
|
ccsds_days: unix_to_ccsds_days(unix_days) as u32,
|
||||||
ms_of_day: secs_of_day * 1000 + subsec_millis,
|
ms_of_day,
|
||||||
unix_days_seconds: unix_days * SECONDS_PER_DAY as i64,
|
unix_days_seconds: unix_days * SECONDS_PER_DAY as i64,
|
||||||
|
submilis_prec: precision,
|
||||||
|
submillis,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CdsCommon for ConversionFromUnix {
|
impl CdsCommon for ConversionFromUnix {
|
||||||
fn submillis_precision(&self) -> SubmillisPrecision {
|
fn submillis_precision(&self) -> SubmillisPrecision {
|
||||||
SubmillisPrecision::Absent
|
self.submilis_prec
|
||||||
}
|
}
|
||||||
|
|
||||||
fn ms_of_day(&self) -> u32 {
|
fn ms_of_day(&self) -> u32 {
|
||||||
@ -236,7 +266,7 @@ impl CdsCommon for ConversionFromUnix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn submillis(&self) -> u32 {
|
fn submillis(&self) -> u32 {
|
||||||
0
|
self.submillis
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -246,13 +276,13 @@ impl CdsConverter for ConversionFromUnix {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
/// Helper struct which generates fields for the CDS time provider from a datetime.
|
/// Helper struct which generates fields for the CDS time provider from a datetime.
|
||||||
struct ConversionFromDatetime {
|
struct ConversionFromChronoDatetime {
|
||||||
unix_conversion: ConversionFromUnix,
|
unix_conversion: ConversionFromUnix,
|
||||||
submillis_prec: SubmillisPrecision,
|
submillis_prec: SubmillisPrecision,
|
||||||
submillis: u32,
|
submillis: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CdsCommon for ConversionFromDatetime {
|
impl CdsCommon for ConversionFromChronoDatetime {
|
||||||
fn submillis_precision(&self) -> SubmillisPrecision {
|
fn submillis_precision(&self) -> SubmillisPrecision {
|
||||||
self.submillis_prec
|
self.submillis_prec
|
||||||
}
|
}
|
||||||
@ -269,7 +299,7 @@ impl CdsCommon for ConversionFromDatetime {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl CdsConverter for ConversionFromDatetime {
|
impl CdsConverter for ConversionFromChronoDatetime {
|
||||||
delegate! {to self.unix_conversion { fn unix_days_seconds(&self) -> i64; }}
|
delegate! {to self.unix_conversion { fn unix_days_seconds(&self) -> i64; }}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -286,27 +316,38 @@ fn calc_unix_days_and_secs_of_day(unix_seconds: i64) -> (i64, u32) {
|
|||||||
(unix_days, secs_of_day as u32)
|
(unix_days, secs_of_day as u32)
|
||||||
}
|
}
|
||||||
|
|
||||||
impl ConversionFromDatetime {
|
#[cfg(feature = "chrono")]
|
||||||
fn new(dt: &DateTime<Utc>) -> Result<Self, TimestampError> {
|
impl ConversionFromChronoDatetime {
|
||||||
|
fn new(dt: &chrono::DateTime<chrono::Utc>) -> Result<Self, TimestampError> {
|
||||||
Self::new_generic(dt, SubmillisPrecision::Absent)
|
Self::new_generic(dt, SubmillisPrecision::Absent)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_with_submillis_us_prec(dt: &DateTime<Utc>) -> Result<Self, TimestampError> {
|
fn new_with_submillis_us_prec(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
|
) -> Result<Self, TimestampError> {
|
||||||
Self::new_generic(dt, SubmillisPrecision::Microseconds)
|
Self::new_generic(dt, SubmillisPrecision::Microseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_with_submillis_ps_prec(dt: &DateTime<Utc>) -> Result<Self, TimestampError> {
|
fn new_with_submillis_ps_prec(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
|
) -> Result<Self, TimestampError> {
|
||||||
Self::new_generic(dt, SubmillisPrecision::Picoseconds)
|
Self::new_generic(dt, SubmillisPrecision::Picoseconds)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn new_generic(dt: &DateTime<Utc>, prec: SubmillisPrecision) -> Result<Self, TimestampError> {
|
#[cfg(feature = "chrono")]
|
||||||
|
fn new_generic(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
|
prec: SubmillisPrecision,
|
||||||
|
) -> Result<Self, TimestampError> {
|
||||||
// The CDS timestamp does not support timestamps before the CCSDS epoch.
|
// The CDS timestamp does not support timestamps before the CCSDS epoch.
|
||||||
if dt.year() < 1958 {
|
if dt.year() < 1958 {
|
||||||
return Err(TimestampError::DateBeforeCcsdsEpoch(*dt));
|
return Err(TimestampError::DateBeforeCcsdsEpoch(UnixTimestamp::from(
|
||||||
|
*dt,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
// The contained values in the conversion should be all positive now
|
// The contained values in the conversion should be all positive now
|
||||||
let unix_conversion =
|
let unix_conversion =
|
||||||
ConversionFromUnix::new(dt.timestamp(), dt.timestamp_subsec_millis())?;
|
ConversionFromUnix::new(dt.timestamp(), dt.timestamp_subsec_nanos(), prec)?;
|
||||||
let mut submillis = 0;
|
let mut submillis = 0;
|
||||||
match prec {
|
match prec {
|
||||||
SubmillisPrecision::Microseconds => {
|
SubmillisPrecision::Microseconds => {
|
||||||
@ -351,7 +392,8 @@ impl ConversionFromNow {
|
|||||||
let epoch = now.as_secs();
|
let epoch = now.as_secs();
|
||||||
// This should always return a value with valid (non-negative) CCSDS days,
|
// This should always return a value with valid (non-negative) CCSDS days,
|
||||||
// so it is okay to unwrap
|
// so it is okay to unwrap
|
||||||
let unix_conversion = ConversionFromUnix::new(epoch as i64, now.subsec_millis()).unwrap();
|
let unix_conversion =
|
||||||
|
ConversionFromUnix::new(epoch as i64, now.subsec_nanos(), prec).unwrap();
|
||||||
let mut submillis = 0;
|
let mut submillis = 0;
|
||||||
|
|
||||||
match prec {
|
match prec {
|
||||||
@ -589,20 +631,6 @@ 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;
|
||||||
@ -611,35 +639,11 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
|
|||||||
} else {
|
} else {
|
||||||
unix_days_seconds += seconds_of_day;
|
unix_days_seconds += seconds_of_day;
|
||||||
}
|
}
|
||||||
self.unix_stamp = UnixTimestamp::const_new(unix_days_seconds, (ms_of_day % 1000) as u16);
|
let mut subsec_nanos = (ms_of_day % 1000) * 10_u32.pow(6);
|
||||||
|
if let Some(precision) = self.precision_as_ns() {
|
||||||
|
subsec_nanos += precision;
|
||||||
}
|
}
|
||||||
|
self.unix_stamp = UnixTimestamp::new(unix_days_seconds, subsec_nanos);
|
||||||
#[cfg(feature = "chrono")]
|
|
||||||
fn calc_chrono_date_time(
|
|
||||||
&self,
|
|
||||||
ns_since_last_second: u32,
|
|
||||||
) -> Option<chrono::DateTime<chrono::Utc>> {
|
|
||||||
assert!(
|
|
||||||
ns_since_last_second < 10_u32.pow(9),
|
|
||||||
"Invalid MS since last second"
|
|
||||||
);
|
|
||||||
if let LocalResult::Single(val) =
|
|
||||||
Utc.timestamp_opt(self.unix_stamp.unix_seconds, ns_since_last_second)
|
|
||||||
{
|
|
||||||
return Some(val);
|
|
||||||
}
|
|
||||||
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> {
|
||||||
@ -674,36 +678,43 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
|
|||||||
Ok(provider)
|
Ok(provider)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
fn from_dt_generic(
|
fn from_dt_generic(
|
||||||
dt: &DateTime<Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
days_len: LengthOfDaySegment,
|
days_len: LengthOfDaySegment,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, TimestampError> {
|
||||||
let conv_from_dt = ConversionFromDatetime::new(dt)?;
|
let conv_from_dt = ConversionFromChronoDatetime::new(dt)?;
|
||||||
Self::generic_from_conversion(days_len, conv_from_dt)
|
Self::generic_from_conversion(days_len, conv_from_dt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
fn from_dt_generic_us_prec(
|
fn from_dt_generic_us_prec(
|
||||||
dt: &DateTime<Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
days_len: LengthOfDaySegment,
|
days_len: LengthOfDaySegment,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, TimestampError> {
|
||||||
let conv_from_dt = ConversionFromDatetime::new_with_submillis_us_prec(dt)?;
|
let conv_from_dt = ConversionFromChronoDatetime::new_with_submillis_us_prec(dt)?;
|
||||||
Self::generic_from_conversion(days_len, conv_from_dt)
|
Self::generic_from_conversion(days_len, conv_from_dt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
fn from_dt_generic_ps_prec(
|
fn from_dt_generic_ps_prec(
|
||||||
dt: &DateTime<Utc>,
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
days_len: LengthOfDaySegment,
|
days_len: LengthOfDaySegment,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, TimestampError> {
|
||||||
let conv_from_dt = ConversionFromDatetime::new_with_submillis_ps_prec(dt)?;
|
let conv_from_dt = ConversionFromChronoDatetime::new_with_submillis_ps_prec(dt)?;
|
||||||
Self::generic_from_conversion(days_len, conv_from_dt)
|
Self::generic_from_conversion(days_len, conv_from_dt)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn from_unix_generic(
|
fn from_unix_generic(
|
||||||
unix_stamp: &UnixTimestamp,
|
unix_stamp: &UnixTimestamp,
|
||||||
days_len: LengthOfDaySegment,
|
days_len: LengthOfDaySegment,
|
||||||
|
submillis_prec: SubmillisPrecision,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, TimestampError> {
|
||||||
let conv_from_dt =
|
let conv_from_dt = ConversionFromUnix::new(
|
||||||
ConversionFromUnix::new(unix_stamp.unix_seconds, unix_stamp.subsecond_millis as u32)?;
|
unix_stamp.unix_seconds,
|
||||||
|
unix_stamp.subsecond_nanos,
|
||||||
|
submillis_prec,
|
||||||
|
)?;
|
||||||
Self::generic_from_conversion(days_len, conv_from_dt)
|
Self::generic_from_conversion(days_len, conv_from_dt)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -818,7 +829,10 @@ impl TimeProvider<DaysLen24Bits> {
|
|||||||
/// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
|
/// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
|
||||||
/// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
|
/// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
|
||||||
/// or the CCSDS days value exceeds the allowed bit width (24 bits).
|
/// or the CCSDS days value exceeds the allowed bit width (24 bits).
|
||||||
pub fn from_dt_with_u24_days(dt: &DateTime<Utc>) -> Result<Self, TimestampError> {
|
#[cfg(feature = "chrono")]
|
||||||
|
pub fn from_dt_with_u24_days(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
|
) -> Result<Self, TimestampError> {
|
||||||
Self::from_dt_generic(dt, LengthOfDaySegment::Long24Bits)
|
Self::from_dt_generic(dt, LengthOfDaySegment::Long24Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -829,19 +843,26 @@ impl TimeProvider<DaysLen24Bits> {
|
|||||||
/// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
|
/// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
|
||||||
/// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
|
/// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
|
||||||
/// or the CCSDS days value exceeds the allowed bit width (24 bits).
|
/// or the CCSDS days value exceeds the allowed bit width (24 bits).
|
||||||
pub fn from_unix_secs_with_u24_days(
|
pub fn from_unix_stamp_with_u24_days(
|
||||||
unix_stamp: &UnixTimestamp,
|
unix_stamp: &UnixTimestamp,
|
||||||
|
submillis_prec: SubmillisPrecision,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, TimestampError> {
|
||||||
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Long24Bits)
|
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Long24Bits, submillis_prec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like [Self::from_dt_with_u24_days] but with microsecond sub-millisecond precision.
|
/// Like [Self::from_dt_with_u24_days] but with microsecond sub-millisecond precision.
|
||||||
pub fn from_dt_with_u24_days_us_precision(dt: &DateTime<Utc>) -> Result<Self, TimestampError> {
|
#[cfg(feature = "chrono")]
|
||||||
|
pub fn from_dt_with_u24_days_us_precision(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
|
) -> Result<Self, TimestampError> {
|
||||||
Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Long24Bits)
|
Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Long24Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like [Self::from_dt_with_u24_days] but with picoseconds sub-millisecond precision.
|
/// Like [Self::from_dt_with_u24_days] but with picoseconds sub-millisecond precision.
|
||||||
pub fn from_dt_with_u24_days_ps_precision(dt: &DateTime<Utc>) -> Result<Self, TimestampError> {
|
#[cfg(feature = "chrono")]
|
||||||
|
pub fn from_dt_with_u24_days_ps_precision(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
|
) -> Result<Self, TimestampError> {
|
||||||
Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Long24Bits)
|
Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Long24Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -898,7 +919,10 @@ impl TimeProvider<DaysLen16Bits> {
|
|||||||
/// This function will return a [TimestampError::DateBeforeCcsdsEpoch] or a
|
/// This function will return a [TimestampError::DateBeforeCcsdsEpoch] or a
|
||||||
/// [TimestampError::Cds] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or
|
/// [TimestampError::Cds] if the time is before the CCSDS epoch (01-01-1958 00:00:00) or
|
||||||
/// the CCSDS days value exceeds the allowed bit width (16 bits).
|
/// the CCSDS days value exceeds the allowed bit width (16 bits).
|
||||||
pub fn from_dt_with_u16_days(dt: &DateTime<Utc>) -> Result<Self, TimestampError> {
|
#[cfg(feature = "chrono")]
|
||||||
|
pub fn from_dt_with_u16_days(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
|
) -> Result<Self, TimestampError> {
|
||||||
Self::from_dt_generic(dt, LengthOfDaySegment::Short16Bits)
|
Self::from_dt_generic(dt, LengthOfDaySegment::Short16Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -916,19 +940,26 @@ impl TimeProvider<DaysLen16Bits> {
|
|||||||
/// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
|
/// This function will return [TimestampError::DateBeforeCcsdsEpoch] or
|
||||||
/// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
|
/// [TimestampError::Cds] if the time is before the CCSDS epoch (1958-01-01T00:00:00+00:00)
|
||||||
/// or the CCSDS days value exceeds the allowed bit width (24 bits).
|
/// or the CCSDS days value exceeds the allowed bit width (24 bits).
|
||||||
pub fn from_unix_secs_with_u16_days(
|
pub fn from_unix_stamp_with_u16_days(
|
||||||
unix_stamp: &UnixTimestamp,
|
unix_stamp: &UnixTimestamp,
|
||||||
|
submillis_prec: SubmillisPrecision,
|
||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, TimestampError> {
|
||||||
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Short16Bits)
|
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Short16Bits, submillis_prec)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like [Self::from_dt_with_u16_days] but with microsecond sub-millisecond precision.
|
/// Like [Self::from_dt_with_u16_days] but with microsecond sub-millisecond precision.
|
||||||
pub fn from_dt_with_u16_days_us_precision(dt: &DateTime<Utc>) -> Result<Self, TimestampError> {
|
#[cfg(feature = "chrono")]
|
||||||
|
pub fn from_dt_with_u16_days_us_precision(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
|
) -> Result<Self, TimestampError> {
|
||||||
Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Short16Bits)
|
Self::from_dt_generic_us_prec(dt, LengthOfDaySegment::Short16Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like [Self::from_dt_with_u16_days] but with picoseconds sub-millisecond precision.
|
/// Like [Self::from_dt_with_u16_days] but with picoseconds sub-millisecond precision.
|
||||||
pub fn from_dt_with_u16_days_ps_precision(dt: &DateTime<Utc>) -> Result<Self, TimestampError> {
|
#[cfg(feature = "chrono")]
|
||||||
|
pub fn from_dt_with_u16_days_ps_precision(
|
||||||
|
dt: &chrono::DateTime<chrono::Utc>,
|
||||||
|
) -> Result<Self, TimestampError> {
|
||||||
Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Short16Bits)
|
Self::from_dt_generic_ps_prec(dt, LengthOfDaySegment::Short16Bits)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1140,20 +1171,22 @@ impl AddAssign<Duration> for TimeProvider<DaysLen24Bits> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<DateTime<Utc>> for TimeProvider<DaysLen16Bits> {
|
#[cfg(feature = "chrono")]
|
||||||
|
impl TryFrom<chrono::DateTime<chrono::Utc>> for TimeProvider<DaysLen16Bits> {
|
||||||
type Error = TimestampError;
|
type Error = TimestampError;
|
||||||
|
|
||||||
fn try_from(dt: DateTime<Utc>) -> Result<Self, Self::Error> {
|
fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
|
||||||
let conversion = ConversionFromDatetime::new(&dt)?;
|
let conversion = ConversionFromChronoDatetime::new(&dt)?;
|
||||||
Self::generic_from_conversion(LengthOfDaySegment::Short16Bits, conversion)
|
Self::generic_from_conversion(LengthOfDaySegment::Short16Bits, conversion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TryFrom<DateTime<Utc>> for TimeProvider<DaysLen24Bits> {
|
#[cfg(feature = "chrono")]
|
||||||
|
impl TryFrom<chrono::DateTime<chrono::Utc>> for TimeProvider<DaysLen24Bits> {
|
||||||
type Error = TimestampError;
|
type Error = TimestampError;
|
||||||
|
|
||||||
fn try_from(dt: DateTime<Utc>) -> Result<Self, Self::Error> {
|
fn try_from(dt: chrono::DateTime<chrono::Utc>) -> Result<Self, Self::Error> {
|
||||||
let conversion = ConversionFromDatetime::new(&dt)?;
|
let conversion = ConversionFromChronoDatetime::new(&dt)?;
|
||||||
Self::generic_from_conversion(LengthOfDaySegment::Long24Bits, conversion)
|
Self::generic_from_conversion(LengthOfDaySegment::Long24Bits, conversion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -1176,26 +1209,14 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CcsdsTimeProvider for TimeProvider<Pro
|
|||||||
self.unix_stamp.unix_seconds
|
self.unix_stamp.unix_seconds
|
||||||
}
|
}
|
||||||
#[inline]
|
#[inline]
|
||||||
fn subsecond_millis(&self) -> u16 {
|
fn subsecond_nanos(&self) -> u32 {
|
||||||
self.unix_stamp.subsecond_millis
|
self.unix_stamp.subsecond_nanos
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
fn unix_stamp(&self) -> UnixTimestamp {
|
fn unix_stamp(&self) -> UnixTimestamp {
|
||||||
self.unix_stamp
|
self.unix_stamp
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
|
||||||
fn chrono_date_time(&self) -> Option<chrono::DateTime<chrono::Utc>> {
|
|
||||||
self.calc_chrono_date_time(self.calc_ns_since_last_second())
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg(feature = "timelib")]
|
|
||||||
#[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())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
impl TimeReader for TimeProvider<DaysLen16Bits> {
|
impl TimeReader for TimeProvider<DaysLen16Bits> {
|
||||||
@ -1330,6 +1351,7 @@ impl TryFrom<TimeProvider<DaysLen24Bits>> for TimeProvider<DaysLen16Bits> {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
use crate::time::TimestampError::{ByteConversion, InvalidTimeCode};
|
use crate::time::TimestampError::{ByteConversion, InvalidTimeCode};
|
||||||
|
use crate::time::{UnixTimestamp, DAYS_CCSDS_TO_UNIX, MS_PER_DAY};
|
||||||
use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall};
|
use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall};
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
use chrono::{Datelike, NaiveDate, Timelike};
|
use chrono::{Datelike, NaiveDate, Timelike};
|
||||||
@ -1345,13 +1367,13 @@ mod tests {
|
|||||||
unix_stamp.unix_seconds,
|
unix_stamp.unix_seconds,
|
||||||
(DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as i64
|
(DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as i64
|
||||||
);
|
);
|
||||||
let subsecond_millis = unix_stamp.subsecond_millis;
|
let subsecond_millis = unix_stamp.subsecond_nanos;
|
||||||
assert_eq!(subsecond_millis, 0);
|
assert_eq!(subsecond_millis, 0);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
time_stamper.submillis_precision(),
|
time_stamper.submillis_precision(),
|
||||||
SubmillisPrecision::Absent
|
SubmillisPrecision::Absent
|
||||||
);
|
);
|
||||||
assert_eq!(time_stamper.subsecond_millis(), 0);
|
assert_eq!(time_stamper.subsecond_nanos(), 0);
|
||||||
assert_eq!(time_stamper.ccdsd_time_code(), CcsdsTimeCodes::Cds);
|
assert_eq!(time_stamper.ccdsd_time_code(), CcsdsTimeCodes::Cds);
|
||||||
assert_eq!(
|
assert_eq!(
|
||||||
time_stamper.p_field(),
|
time_stamper.p_field(),
|
||||||
@ -1382,8 +1404,10 @@ mod tests {
|
|||||||
assert_eq!(date_time.minute(), 0);
|
assert_eq!(date_time.minute(), 0);
|
||||||
assert_eq!(date_time.second(), 0);
|
assert_eq!(date_time.second(), 0);
|
||||||
let time_stamper = TimeProvider::new_with_u16_days((-DAYS_CCSDS_TO_UNIX) as u16, 40);
|
let time_stamper = TimeProvider::new_with_u16_days((-DAYS_CCSDS_TO_UNIX) as u16, 40);
|
||||||
|
assert_eq!(time_stamper.subsecond_nanos(), 40 * 10_u32.pow(6));
|
||||||
assert_eq!(time_stamper.subsecond_millis(), 40);
|
assert_eq!(time_stamper.subsecond_millis(), 40);
|
||||||
let time_stamper = TimeProvider::new_with_u16_days((-DAYS_CCSDS_TO_UNIX) as u16, 1040);
|
let time_stamper = TimeProvider::new_with_u16_days((-DAYS_CCSDS_TO_UNIX) as u16, 1040);
|
||||||
|
assert_eq!(time_stamper.subsecond_nanos(), 40 * 10_u32.pow(6));
|
||||||
assert_eq!(time_stamper.subsecond_millis(), 40);
|
assert_eq!(time_stamper.subsecond_millis(), 40);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1565,7 +1589,7 @@ mod tests {
|
|||||||
|
|
||||||
fn generic_now_test<T: ProvidesDaysLength>(
|
fn generic_now_test<T: ProvidesDaysLength>(
|
||||||
timestamp_now: TimeProvider<T>,
|
timestamp_now: TimeProvider<T>,
|
||||||
compare_stamp: DateTime<Utc>,
|
compare_stamp: chrono::DateTime<chrono::Utc>,
|
||||||
) {
|
) {
|
||||||
let dt = timestamp_now.chrono_date_time().unwrap();
|
let dt = timestamp_now.chrono_date_time().unwrap();
|
||||||
if compare_stamp.year() > dt.year() {
|
if compare_stamp.year() > dt.year() {
|
||||||
@ -1591,35 +1615,35 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_time_now() {
|
fn test_time_now() {
|
||||||
let timestamp_now = TimeProvider::from_now_with_u16_days().unwrap();
|
let timestamp_now = TimeProvider::from_now_with_u16_days().unwrap();
|
||||||
let compare_stamp = Utc::now();
|
let compare_stamp = chrono::Utc::now();
|
||||||
generic_now_test(timestamp_now, compare_stamp);
|
generic_now_test(timestamp_now, compare_stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_time_now_us_prec() {
|
fn test_time_now_us_prec() {
|
||||||
let timestamp_now = TimeProvider::from_now_with_u16_days_us_precision().unwrap();
|
let timestamp_now = TimeProvider::from_now_with_u16_days_us_precision().unwrap();
|
||||||
let compare_stamp = Utc::now();
|
let compare_stamp = chrono::Utc::now();
|
||||||
generic_now_test(timestamp_now, compare_stamp);
|
generic_now_test(timestamp_now, compare_stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_time_now_ps_prec() {
|
fn test_time_now_ps_prec() {
|
||||||
let timestamp_now = TimeProvider::from_now_with_u16_days_ps_precision().unwrap();
|
let timestamp_now = TimeProvider::from_now_with_u16_days_ps_precision().unwrap();
|
||||||
let compare_stamp = Utc::now();
|
let compare_stamp = chrono::Utc::now();
|
||||||
generic_now_test(timestamp_now, compare_stamp);
|
generic_now_test(timestamp_now, compare_stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_time_now_ps_prec_u16_days() {
|
fn test_time_now_ps_prec_u16_days() {
|
||||||
let timestamp_now = TimeProvider::from_now_with_u16_days_ps_precision().unwrap();
|
let timestamp_now = TimeProvider::from_now_with_u16_days_ps_precision().unwrap();
|
||||||
let compare_stamp = Utc::now();
|
let compare_stamp = chrono::Utc::now();
|
||||||
generic_now_test(timestamp_now, compare_stamp);
|
generic_now_test(timestamp_now, compare_stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_time_now_ps_prec_u24_days() {
|
fn test_time_now_ps_prec_u24_days() {
|
||||||
let timestamp_now = TimeProvider::from_now_with_u24_days_ps_precision().unwrap();
|
let timestamp_now = TimeProvider::from_now_with_u24_days_ps_precision().unwrap();
|
||||||
let compare_stamp = Utc::now();
|
let compare_stamp = chrono::Utc::now();
|
||||||
generic_now_test(timestamp_now, compare_stamp);
|
generic_now_test(timestamp_now, compare_stamp);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1743,19 +1767,19 @@ mod tests {
|
|||||||
assert_eq!(stamp_deserialized.submillis(), 5e8 as u32);
|
assert_eq!(stamp_deserialized.submillis(), 5e8 as u32);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_dt_case_0_no_prec(subsec_millis: u32) -> DateTime<Utc> {
|
fn generic_dt_case_0_no_prec(subsec_millis: u32) -> chrono::DateTime<chrono::Utc> {
|
||||||
NaiveDate::from_ymd_opt(2023, 1, 14)
|
NaiveDate::from_ymd_opt(2023, 1, 14)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.and_hms_milli_opt(16, 49, 30, subsec_millis)
|
.and_hms_milli_opt(16, 49, 30, subsec_millis)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.and_local_timezone(Utc)
|
.and_local_timezone(chrono::Utc)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_check_dt_case_0<DaysLen: ProvidesDaysLength>(
|
fn generic_check_dt_case_0<DaysLen: ProvidesDaysLength>(
|
||||||
time_provider: &TimeProvider<DaysLen>,
|
time_provider: &TimeProvider<DaysLen>,
|
||||||
subsec_millis: u32,
|
subsec_millis: u32,
|
||||||
datetime_utc: DateTime<Utc>,
|
datetime_utc: chrono::DateTime<chrono::Utc>,
|
||||||
) {
|
) {
|
||||||
// https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023
|
// https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023
|
||||||
// Leap years need to be accounted for as well.
|
// Leap years need to be accounted for as well.
|
||||||
@ -1790,21 +1814,21 @@ mod tests {
|
|||||||
assert_eq!(time_provider, time_provider_2);
|
assert_eq!(time_provider, time_provider_2);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_dt_case_1_us_prec(subsec_millis: u32) -> DateTime<Utc> {
|
fn generic_dt_case_1_us_prec(subsec_millis: u32) -> chrono::DateTime<chrono::Utc> {
|
||||||
// 250 ms + 500 us
|
// 250 ms + 500 us
|
||||||
let subsec_micros = subsec_millis * 1000 + 500;
|
let subsec_micros = subsec_millis * 1000 + 500;
|
||||||
NaiveDate::from_ymd_opt(2023, 1, 14)
|
NaiveDate::from_ymd_opt(2023, 1, 14)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.and_hms_micro_opt(16, 49, 30, subsec_micros)
|
.and_hms_micro_opt(16, 49, 30, subsec_micros)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.and_local_timezone(Utc)
|
.and_local_timezone(chrono::Utc)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_check_dt_case_1_us_prec<DaysLen: ProvidesDaysLength>(
|
fn generic_check_dt_case_1_us_prec<DaysLen: ProvidesDaysLength>(
|
||||||
time_provider: &TimeProvider<DaysLen>,
|
time_provider: &TimeProvider<DaysLen>,
|
||||||
subsec_millis: u32,
|
subsec_millis: u32,
|
||||||
datetime_utc: DateTime<Utc>,
|
datetime_utc: chrono::DateTime<chrono::Utc>,
|
||||||
) {
|
) {
|
||||||
// https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023
|
// https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023
|
||||||
// Leap years need to be accounted for as well.
|
// Leap years need to be accounted for as well.
|
||||||
@ -1839,7 +1863,7 @@ mod tests {
|
|||||||
generic_check_dt_case_1_us_prec(&time_provider, subsec_millis, datetime_utc);
|
generic_check_dt_case_1_us_prec(&time_provider, subsec_millis, datetime_utc);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn generic_dt_case_2_ps_prec(subsec_millis: u32) -> (DateTime<Utc>, u32) {
|
fn generic_dt_case_2_ps_prec(subsec_millis: u32) -> (chrono::DateTime<chrono::Utc>, u32) {
|
||||||
// 250 ms + 500 us
|
// 250 ms + 500 us
|
||||||
let subsec_nanos = subsec_millis * 1000 * 1000 + 500 * 1000;
|
let subsec_nanos = subsec_millis * 1000 * 1000 + 500 * 1000;
|
||||||
let submilli_nanos = subsec_nanos % 10_u32.pow(6);
|
let submilli_nanos = subsec_nanos % 10_u32.pow(6);
|
||||||
@ -1848,7 +1872,7 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.and_hms_nano_opt(16, 49, 30, subsec_nanos)
|
.and_hms_nano_opt(16, 49, 30, subsec_nanos)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.and_local_timezone(Utc)
|
.and_local_timezone(chrono::Utc)
|
||||||
.unwrap(),
|
.unwrap(),
|
||||||
submilli_nanos,
|
submilli_nanos,
|
||||||
)
|
)
|
||||||
@ -1858,7 +1882,7 @@ mod tests {
|
|||||||
time_provider: &TimeProvider<DaysLen>,
|
time_provider: &TimeProvider<DaysLen>,
|
||||||
subsec_millis: u32,
|
subsec_millis: u32,
|
||||||
submilli_nanos: u32,
|
submilli_nanos: u32,
|
||||||
datetime_utc: DateTime<Utc>,
|
datetime_utc: chrono::DateTime<chrono::Utc>,
|
||||||
) {
|
) {
|
||||||
// https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023
|
// https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023
|
||||||
// Leap years need to be accounted for as well.
|
// Leap years need to be accounted for as well.
|
||||||
@ -1907,10 +1931,10 @@ mod tests {
|
|||||||
fn test_creation_from_unix_stamp_0_u16_days() {
|
fn test_creation_from_unix_stamp_0_u16_days() {
|
||||||
let unix_secs = 0;
|
let unix_secs = 0;
|
||||||
let subsec_millis = 0;
|
let subsec_millis = 0;
|
||||||
let time_provider = TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::const_new(
|
let time_provider = TimeProvider::from_unix_stamp_with_u16_days(
|
||||||
unix_secs,
|
&UnixTimestamp::new(unix_secs, subsec_millis),
|
||||||
subsec_millis,
|
SubmillisPrecision::Absent,
|
||||||
))
|
)
|
||||||
.expect("creating provider from unix stamp failed");
|
.expect("creating provider from unix stamp failed");
|
||||||
assert_eq!(time_provider.ccsds_days, -DAYS_CCSDS_TO_UNIX as u16)
|
assert_eq!(time_provider.ccsds_days, -DAYS_CCSDS_TO_UNIX as u16)
|
||||||
}
|
}
|
||||||
@ -1919,10 +1943,10 @@ mod tests {
|
|||||||
fn test_creation_from_unix_stamp_0_u24_days() {
|
fn test_creation_from_unix_stamp_0_u24_days() {
|
||||||
let unix_secs = 0;
|
let unix_secs = 0;
|
||||||
let subsec_millis = 0;
|
let subsec_millis = 0;
|
||||||
let time_provider = TimeProvider::from_unix_secs_with_u24_days(&UnixTimestamp::const_new(
|
let time_provider = TimeProvider::from_unix_stamp_with_u24_days(
|
||||||
unix_secs,
|
&UnixTimestamp::new(unix_secs, subsec_millis),
|
||||||
subsec_millis,
|
SubmillisPrecision::Absent,
|
||||||
))
|
)
|
||||||
.expect("creating provider from unix stamp failed");
|
.expect("creating provider from unix stamp failed");
|
||||||
assert_eq!(time_provider.ccsds_days, (-DAYS_CCSDS_TO_UNIX) as u32)
|
assert_eq!(time_provider.ccsds_days, (-DAYS_CCSDS_TO_UNIX) as u32)
|
||||||
}
|
}
|
||||||
@ -1934,9 +1958,12 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.and_hms_milli_opt(16, 49, 30, subsec_millis)
|
.and_hms_milli_opt(16, 49, 30, subsec_millis)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.and_local_timezone(Utc)
|
.and_local_timezone(chrono::Utc)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let time_provider = TimeProvider::from_unix_secs_with_u16_days(&datetime_utc.into())
|
let time_provider = TimeProvider::from_unix_stamp_with_u16_days(
|
||||||
|
&datetime_utc.into(),
|
||||||
|
SubmillisPrecision::Absent,
|
||||||
|
)
|
||||||
.expect("creating provider from unix stamp failed");
|
.expect("creating provider from unix stamp failed");
|
||||||
// https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023
|
// https://www.timeanddate.com/date/durationresult.html?d1=01&m1=01&y1=1958&d2=14&m2=01&y2=2023
|
||||||
// Leap years need to be accounted for as well.
|
// Leap years need to be accounted for as well.
|
||||||
@ -1953,10 +1980,10 @@ mod tests {
|
|||||||
fn test_creation_0_ccsds_days() {
|
fn test_creation_0_ccsds_days() {
|
||||||
let unix_secs = DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64;
|
let unix_secs = DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64;
|
||||||
let subsec_millis = 0;
|
let subsec_millis = 0;
|
||||||
let time_provider = TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::const_new(
|
let time_provider = TimeProvider::from_unix_stamp_with_u16_days(
|
||||||
unix_secs,
|
&UnixTimestamp::new(unix_secs, subsec_millis),
|
||||||
subsec_millis,
|
SubmillisPrecision::Absent,
|
||||||
))
|
)
|
||||||
.expect("creating provider from unix stamp failed");
|
.expect("creating provider from unix stamp failed");
|
||||||
assert_eq!(time_provider.ccsds_days, 0)
|
assert_eq!(time_provider.ccsds_days, 0)
|
||||||
}
|
}
|
||||||
@ -1965,10 +1992,10 @@ mod tests {
|
|||||||
fn test_invalid_creation_from_unix_stamp_days_too_large() {
|
fn test_invalid_creation_from_unix_stamp_days_too_large() {
|
||||||
let invalid_unix_secs: i64 = (u16::MAX as i64 + 1) * SECONDS_PER_DAY as i64;
|
let invalid_unix_secs: i64 = (u16::MAX as i64 + 1) * SECONDS_PER_DAY as i64;
|
||||||
let subsec_millis = 0;
|
let subsec_millis = 0;
|
||||||
match TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::const_new(
|
match TimeProvider::from_unix_stamp_with_u16_days(
|
||||||
invalid_unix_secs,
|
&UnixTimestamp::new(invalid_unix_secs, subsec_millis),
|
||||||
subsec_millis,
|
SubmillisPrecision::Absent,
|
||||||
)) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
panic!("creation should not succeed")
|
panic!("creation should not succeed")
|
||||||
}
|
}
|
||||||
@ -1992,15 +2019,17 @@ mod tests {
|
|||||||
// precisely 31-12-1957 23:59:55
|
// precisely 31-12-1957 23:59:55
|
||||||
let unix_secs = DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32 - 5;
|
let unix_secs = DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32 - 5;
|
||||||
let subsec_millis = 0;
|
let subsec_millis = 0;
|
||||||
match TimeProvider::from_unix_secs_with_u16_days(&UnixTimestamp::const_new(
|
match TimeProvider::from_unix_stamp_with_u16_days(
|
||||||
unix_secs as i64,
|
&UnixTimestamp::new(unix_secs as i64, subsec_millis),
|
||||||
subsec_millis,
|
SubmillisPrecision::Absent,
|
||||||
)) {
|
) {
|
||||||
Ok(_) => {
|
Ok(_) => {
|
||||||
panic!("creation should not succeed")
|
panic!("creation should not succeed")
|
||||||
}
|
}
|
||||||
Err(e) => {
|
Err(e) => {
|
||||||
if let TimestampError::DateBeforeCcsdsEpoch(dt) = e {
|
if let TimestampError::DateBeforeCcsdsEpoch(dt) = e {
|
||||||
|
let dt = dt.as_date_time();
|
||||||
|
if let chrono::LocalResult::Single(dt) = dt {
|
||||||
assert_eq!(dt.year(), 1957);
|
assert_eq!(dt.year(), 1957);
|
||||||
assert_eq!(dt.month(), 12);
|
assert_eq!(dt.month(), 12);
|
||||||
assert_eq!(dt.day(), 31);
|
assert_eq!(dt.day(), 31);
|
||||||
@ -2010,6 +2039,9 @@ mod tests {
|
|||||||
} else {
|
} else {
|
||||||
panic!("unexpected error {}", e)
|
panic!("unexpected error {}", e)
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
panic!("unexpected error {}", e)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -2222,12 +2254,12 @@ mod tests {
|
|||||||
.unwrap()
|
.unwrap()
|
||||||
.and_hms_milli_opt(23, 59, 59, 999)
|
.and_hms_milli_opt(23, 59, 59, 999)
|
||||||
.unwrap()
|
.unwrap()
|
||||||
.and_local_timezone(Utc)
|
.and_local_timezone(chrono::Utc)
|
||||||
.unwrap();
|
.unwrap();
|
||||||
let time_provider = TimeProvider::from_dt_with_u24_days(&datetime_utc);
|
let time_provider = TimeProvider::from_dt_with_u24_days(&datetime_utc);
|
||||||
assert!(time_provider.is_err());
|
assert!(time_provider.is_err());
|
||||||
if let TimestampError::DateBeforeCcsdsEpoch(dt) = time_provider.unwrap_err() {
|
if let TimestampError::DateBeforeCcsdsEpoch(dt) = time_provider.unwrap_err() {
|
||||||
assert_eq!(dt, datetime_utc);
|
assert_eq!(dt, datetime_utc.into());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,15 +2,29 @@
|
|||||||
//! [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) section 3.2 .
|
//! [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf) section 3.2 .
|
||||||
//!
|
//!
|
||||||
//! The core data structure to do this is the [TimeProviderCcsdsEpoch] struct.
|
//! The core data structure to do this is the [TimeProviderCcsdsEpoch] struct.
|
||||||
use super::*;
|
#[cfg(feature = "serde")]
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
use core::fmt::{Debug, Display, Formatter};
|
||||||
|
use core::ops::{Add, AddAssign};
|
||||||
|
use core::time::Duration;
|
||||||
|
use core::u64;
|
||||||
|
|
||||||
|
use crate::ByteConversionError;
|
||||||
|
|
||||||
|
use super::{CcsdsTimeCodes, TimestampError, unix_epoch_to_ccsds_epoch, UnixTimestamp, ccsds_epoch_to_unix_epoch, TimeReader, TimeWriter, CcsdsTimeProvider};
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use super::StdTimestampError;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::error::Error;
|
||||||
|
#[cfg(feature = "std")]
|
||||||
|
use std::time::SystemTime;
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
use chrono::Datelike;
|
use chrono::Datelike;
|
||||||
|
|
||||||
use core::fmt::Debug;
|
#[cfg(feature = "alloc")]
|
||||||
use core::ops::{Add, AddAssign};
|
use super::ccsds_time_code_from_p_field;
|
||||||
use core::time::Duration;
|
|
||||||
use core::u64;
|
|
||||||
|
|
||||||
const MIN_CUC_LEN: usize = 2;
|
const MIN_CUC_LEN: usize = 2;
|
||||||
|
|
||||||
@ -301,6 +315,7 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
|
||||||
pub fn update_from_now(&mut self) -> Result<(), StdTimestampError> {
|
pub fn update_from_now(&mut self) -> Result<(), StdTimestampError> {
|
||||||
|
|
||||||
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
||||||
self.counter.1 = unix_epoch_to_ccsds_epoch(now.as_secs() as i64) as u32;
|
self.counter.1 = unix_epoch_to_ccsds_epoch(now.as_secs() as i64) as u32;
|
||||||
self.counter
|
self.counter
|
||||||
@ -325,7 +340,9 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
) -> Result<Self, TimestampError> {
|
) -> Result<Self, TimestampError> {
|
||||||
// Year before CCSDS epoch is invalid.
|
// Year before CCSDS epoch is invalid.
|
||||||
if dt.year() < 1958 {
|
if dt.year() < 1958 {
|
||||||
return Err(TimestampError::DateBeforeCcsdsEpoch(*dt));
|
return Err(TimestampError::DateBeforeCcsdsEpoch(UnixTimestamp::from(
|
||||||
|
*dt,
|
||||||
|
)));
|
||||||
}
|
}
|
||||||
let counter = dt
|
let counter = dt
|
||||||
.timestamp()
|
.timestamp()
|
||||||
@ -349,9 +366,7 @@ impl TimeProviderCcsdsEpoch {
|
|||||||
let ccsds_epoch = unix_epoch_to_ccsds_epoch(unix_stamp.unix_seconds);
|
let ccsds_epoch = unix_epoch_to_ccsds_epoch(unix_stamp.unix_seconds);
|
||||||
// Negative CCSDS epoch is invalid.
|
// Negative CCSDS epoch is invalid.
|
||||||
if ccsds_epoch < 0 {
|
if ccsds_epoch < 0 {
|
||||||
return Err(TimestampError::DateBeforeCcsdsEpoch(
|
return Err(TimestampError::DateBeforeCcsdsEpoch(*unix_stamp));
|
||||||
unix_stamp.as_date_time().unwrap(),
|
|
||||||
));
|
|
||||||
}
|
}
|
||||||
ccsds_epoch
|
ccsds_epoch
|
||||||
.checked_add(i64::from(leap_seconds))
|
.checked_add(i64::from(leap_seconds))
|
||||||
@ -682,43 +697,16 @@ impl CcsdsTimeProvider for TimeProviderCcsdsEpoch {
|
|||||||
self.unix_seconds()
|
self.unix_seconds()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn subsecond_millis(&self) -> u16 {
|
fn subsecond_nanos(&self) -> u32 {
|
||||||
if let Some(fractions) = self.fractions {
|
if let Some(fractions) = self.fractions {
|
||||||
if fractions.0 == FractionalResolution::Seconds {
|
if fractions.0 == FractionalResolution::Seconds {
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
// Rounding down here is the correct approach.
|
// Rounding down here is the correct approach.
|
||||||
return (convert_fractional_part_to_ns(fractions) / 10_u32.pow(6) as u64) as u16;
|
return convert_fractional_part_to_ns(fractions) as u32;
|
||||||
}
|
}
|
||||||
0
|
0
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
|
||||||
fn chrono_date_time(&self) -> Option<chrono::DateTime<chrono::Utc>> {
|
|
||||||
let mut ns = 0;
|
|
||||||
if let Some(fractional_part) = self.fractions {
|
|
||||||
ns = convert_fractional_part_to_ns(fractional_part);
|
|
||||||
}
|
|
||||||
if let LocalResult::Single(res) = chrono::Utc.timestamp_opt(self.unix_seconds(), ns as u32)
|
|
||||||
{
|
|
||||||
return Some(res);
|
|
||||||
}
|
|
||||||
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(
|
||||||
@ -818,9 +806,11 @@ impl Add<Duration> for &TimeProviderCcsdsEpoch {
|
|||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
mod tests {
|
mod tests {
|
||||||
|
use crate::time::{UnixTimestamp, DAYS_CCSDS_TO_UNIX, SECONDS_PER_DAY};
|
||||||
|
|
||||||
use super::*;
|
use super::*;
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
use chrono::{Datelike, Timelike};
|
use chrono::{Datelike, TimeZone, Timelike};
|
||||||
#[allow(unused_imports)]
|
#[allow(unused_imports)]
|
||||||
use std::println;
|
use std::println;
|
||||||
|
|
||||||
@ -840,8 +830,7 @@ mod tests {
|
|||||||
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.chrono_date_time();
|
let dt = zero_cuc.chrono_date_time();
|
||||||
assert!(dt.is_some());
|
if let chrono::LocalResult::Single(dt) = dt {
|
||||||
let dt = dt.unwrap();
|
|
||||||
assert_eq!(dt.year(), 1958);
|
assert_eq!(dt.year(), 1958);
|
||||||
assert_eq!(dt.month(), 1);
|
assert_eq!(dt.month(), 1);
|
||||||
assert_eq!(dt.day(), 1);
|
assert_eq!(dt.day(), 1);
|
||||||
@ -849,6 +838,7 @@ mod tests {
|
|||||||
assert_eq!(dt.minute(), 0);
|
assert_eq!(dt.minute(), 0);
|
||||||
assert_eq!(dt.second(), 0);
|
assert_eq!(dt.second(), 0);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_write_no_fractions() {
|
fn test_write_no_fractions() {
|
||||||
@ -862,7 +852,7 @@ mod tests {
|
|||||||
let zero_cuc = zero_cuc.unwrap();
|
let zero_cuc = zero_cuc.unwrap();
|
||||||
let res = zero_cuc.write_to_bytes(&mut buf);
|
let res = zero_cuc.write_to_bytes(&mut buf);
|
||||||
assert!(res.is_ok());
|
assert!(res.is_ok());
|
||||||
assert_eq!(zero_cuc.subsecond_millis(), 0);
|
assert_eq!(zero_cuc.subsecond_nanos(), 0);
|
||||||
assert_eq!(zero_cuc.len_as_bytes(), 5);
|
assert_eq!(zero_cuc.len_as_bytes(), 5);
|
||||||
assert_eq!(pfield_len(buf[0]), 1);
|
assert_eq!(pfield_len(buf[0]), 1);
|
||||||
let written = res.unwrap();
|
let written = res.unwrap();
|
||||||
@ -880,17 +870,19 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_datetime_now() {
|
fn test_datetime_now() {
|
||||||
let now = Utc::now();
|
let now = chrono::Utc::now();
|
||||||
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.chrono_date_time();
|
let dt_opt = cuc_now.chrono_date_time();
|
||||||
assert!(dt_opt.is_some());
|
if let chrono::LocalResult::Single(dt) = dt_opt {
|
||||||
let dt = dt_opt.unwrap();
|
|
||||||
let diff = dt - now;
|
let diff = dt - now;
|
||||||
assert!(diff.num_milliseconds() < 1000);
|
assert!(diff.num_milliseconds() < 1000);
|
||||||
println!("datetime from cuc: {}", dt);
|
println!("datetime from cuc: {}", dt);
|
||||||
println!("datetime now: {}", now);
|
println!("datetime now: {}", now);
|
||||||
|
} else {
|
||||||
|
panic!("date time creation from now failed")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
@ -1316,7 +1308,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 = chrono::Utc.with_ymd_and_hms(2021, 1, 1, 0, 0, 0).unwrap();
|
||||||
let cuc = TimeProviderCcsdsEpoch::from_chrono_date_time(
|
let cuc = TimeProviderCcsdsEpoch::from_chrono_date_time(
|
||||||
&dt,
|
&dt,
|
||||||
FractionalResolution::Seconds,
|
FractionalResolution::Seconds,
|
||||||
@ -1335,7 +1327,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn from_unix_stamp() {
|
fn from_unix_stamp() {
|
||||||
let unix_stamp = UnixTimestamp::new(0, 0).unwrap();
|
let unix_stamp = UnixTimestamp::new(0, 0);
|
||||||
let cuc = TimeProviderCcsdsEpoch::from_unix_stamp(
|
let cuc = TimeProviderCcsdsEpoch::from_unix_stamp(
|
||||||
&unix_stamp,
|
&unix_stamp,
|
||||||
FractionalResolution::Seconds,
|
FractionalResolution::Seconds,
|
||||||
|
137
src/time/mod.rs
137
src/time/mod.rs
@ -1,5 +1,6 @@
|
|||||||
//! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
//! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
|
||||||
use crate::ByteConversionError;
|
use crate::ByteConversionError;
|
||||||
|
#[cfg(feature = "chrono")]
|
||||||
use chrono::{DateTime, LocalResult, TimeZone, Utc};
|
use chrono::{DateTime, LocalResult, TimeZone, Utc};
|
||||||
use core::cmp::Ordering;
|
use core::cmp::Ordering;
|
||||||
use core::fmt::{Display, Formatter};
|
use core::fmt::{Display, Formatter};
|
||||||
@ -13,6 +14,7 @@ use num_traits::float::FloatCore;
|
|||||||
|
|
||||||
#[cfg(feature = "serde")]
|
#[cfg(feature = "serde")]
|
||||||
use serde::{Deserialize, Serialize};
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
use std::error::Error;
|
use std::error::Error;
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -68,7 +70,7 @@ pub enum TimestampError {
|
|||||||
ByteConversion(ByteConversionError),
|
ByteConversion(ByteConversionError),
|
||||||
Cds(cds::CdsError),
|
Cds(cds::CdsError),
|
||||||
Cuc(cuc::CucError),
|
Cuc(cuc::CucError),
|
||||||
DateBeforeCcsdsEpoch(DateTime<Utc>),
|
DateBeforeCcsdsEpoch(UnixTimestamp),
|
||||||
CustomEpochNotSupported,
|
CustomEpochNotSupported,
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -91,7 +93,7 @@ impl Display for TimestampError {
|
|||||||
write!(f, "time stamp: {e}")
|
write!(f, "time stamp: {e}")
|
||||||
}
|
}
|
||||||
TimestampError::DateBeforeCcsdsEpoch(e) => {
|
TimestampError::DateBeforeCcsdsEpoch(e) => {
|
||||||
write!(f, "datetime with date before ccsds epoch: {e}")
|
write!(f, "datetime with date before ccsds epoch: {e:?}")
|
||||||
}
|
}
|
||||||
TimestampError::CustomEpochNotSupported => {
|
TimestampError::CustomEpochNotSupported => {
|
||||||
write!(f, "custom epochs are not supported")
|
write!(f, "custom epochs are not supported")
|
||||||
@ -231,59 +233,101 @@ pub trait CcsdsTimeProvider {
|
|||||||
fn ccdsd_time_code(&self) -> CcsdsTimeCodes;
|
fn ccdsd_time_code(&self) -> CcsdsTimeCodes;
|
||||||
|
|
||||||
fn unix_seconds(&self) -> i64;
|
fn unix_seconds(&self) -> i64;
|
||||||
fn subsecond_millis(&self) -> u16;
|
fn subsecond_nanos(&self) -> u32;
|
||||||
|
|
||||||
|
fn subsecond_millis(&self) -> u16 {
|
||||||
|
(self.subsecond_nanos() / 1_000_000) as u16
|
||||||
|
}
|
||||||
|
|
||||||
fn unix_stamp(&self) -> UnixTimestamp {
|
fn unix_stamp(&self) -> UnixTimestamp {
|
||||||
UnixTimestamp::const_new(self.unix_seconds(), self.subsecond_millis())
|
UnixTimestamp::new(self.unix_seconds(), self.subsecond_nanos())
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "chrono")]
|
#[cfg(feature = "chrono")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
|
||||||
fn chrono_date_time(&self) -> Option<chrono::DateTime<chrono::Utc>>;
|
fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> {
|
||||||
|
chrono::Utc.timestamp_opt(self.unix_seconds(), self.subsecond_nanos())
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(feature = "timelib")]
|
#[cfg(feature = "timelib")]
|
||||||
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
|
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
|
||||||
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange>;
|
fn timelib_date_time(&self) -> Result<time::OffsetDateTime, time::error::ComponentRange> {
|
||||||
|
Ok(
|
||||||
|
time::OffsetDateTime::from_unix_timestamp(self.unix_seconds())?
|
||||||
|
+ time::Duration::nanoseconds(self.subsecond_nanos().into()),
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// UNIX timestamp: Elapsed seconds since 1970-01-01T00:00:00+00:00.
|
/// UNIX timestamp: Elapsed seconds since 1970-01-01T00:00:00+00:00.
|
||||||
///
|
///
|
||||||
/// Also can optionally include subsecond millisecond for greater accuracy.
|
/// Also can optionally include subsecond millisecond for greater accuracy.
|
||||||
|
/// It is assumed that leap second correction was already applied and the reference time is UTC.
|
||||||
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
|
||||||
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
|
||||||
pub struct UnixTimestamp {
|
pub struct UnixTimestamp {
|
||||||
pub unix_seconds: i64,
|
pub unix_seconds: i64,
|
||||||
subsecond_millis: u16,
|
subsecond_nanos: u32,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl UnixTimestamp {
|
impl UnixTimestamp {
|
||||||
/// Returns [None] if the subsecond millisecond value is larger than 999.
|
/// Returns [None] if the subsecond nanosecond value is invalid (larger than fraction of a
|
||||||
pub fn new(unix_seconds: i64, subsec_millis: u16) -> Option<Self> {
|
/// second)
|
||||||
|
pub fn new_checked(unix_seconds: i64, subsec_nanos: u32) -> Option<Self> {
|
||||||
|
if subsec_nanos > 1_000_000_000 - 1 {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
Some(Self::new(unix_seconds, subsec_nanos))
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Returns [None] if the subsecond millisecond value is invalid (larger than fraction of a
|
||||||
|
/// second)
|
||||||
|
pub fn new_subsec_millis_checked(unix_seconds: i64, subsec_millis: u16) -> Option<Self> {
|
||||||
if subsec_millis > 999 {
|
if subsec_millis > 999 {
|
||||||
return None;
|
return None;
|
||||||
}
|
}
|
||||||
Some(Self::const_new(unix_seconds, subsec_millis))
|
Self::new_checked(unix_seconds, subsec_millis as u32 * 1_000_000)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Like [Self::new] but const. Panics if the subsecond value is larger than 999.
|
/// This function will panic if the subsecond value is larger than the fraction of a second.
|
||||||
pub const fn const_new(unix_seconds: i64, subsecond_millis: u16) -> Self {
|
/// Use [new_checked] if you want to handle this case without a panic.
|
||||||
if subsecond_millis > 999 {
|
pub const fn new(unix_seconds: i64, subsecond_nanos: u32) -> Self {
|
||||||
panic!("subsec milliseconds exceeds 999");
|
// This could actually be used to model leap seconds, but we will ignore that for now.
|
||||||
|
if subsecond_nanos > 1_000_000_000 - 1 {
|
||||||
|
panic!("invalid subsecond nanos value");
|
||||||
}
|
}
|
||||||
Self {
|
Self {
|
||||||
unix_seconds,
|
unix_seconds,
|
||||||
subsecond_millis,
|
subsecond_nanos,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This function will panic if the subsecond value is larger than the fraction of a second.
|
||||||
|
/// Use [new_subsecond_millis_checked] if you want to handle this case without a panic.
|
||||||
|
pub const fn new_subsec_millis(unix_seconds: i64, subsecond_millis: u16) -> Self {
|
||||||
|
// This could actually be used to model leap seconds, but we will ignore that for now.
|
||||||
|
if subsecond_millis > 999 {
|
||||||
|
panic!("invalid subsecond millisecond value");
|
||||||
|
}
|
||||||
|
Self {
|
||||||
|
unix_seconds,
|
||||||
|
subsecond_nanos: subsecond_millis as u32 * 1_000_000,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn new_only_seconds(unix_seconds: i64) -> Self {
|
pub fn new_only_seconds(unix_seconds: i64) -> Self {
|
||||||
Self {
|
Self {
|
||||||
unix_seconds,
|
unix_seconds,
|
||||||
subsecond_millis: 0,
|
subsecond_nanos: 0,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn subsecond_millis(&self) -> u16 {
|
pub fn subsecond_millis(&self) -> u16 {
|
||||||
self.subsecond_millis
|
(self.subsecond_nanos / 1_000_000) as u16
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn subsecond_nanos(&self) -> u32 {
|
||||||
|
self.subsecond_nanos
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "std")]
|
#[cfg(feature = "std")]
|
||||||
@ -291,19 +335,16 @@ impl UnixTimestamp {
|
|||||||
pub fn from_now() -> Result<Self, SystemTimeError> {
|
pub fn from_now() -> Result<Self, SystemTimeError> {
|
||||||
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
|
||||||
let epoch = now.as_secs();
|
let epoch = now.as_secs();
|
||||||
Ok(Self::const_new(epoch as i64, now.subsec_millis() as u16))
|
Ok(Self::new(epoch as i64, now.subsec_nanos()))
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline]
|
#[inline]
|
||||||
pub fn unix_seconds_f64(&self) -> f64 {
|
pub fn unix_seconds_f64(&self) -> f64 {
|
||||||
self.unix_seconds as f64 + (self.subsecond_millis as f64 / 1000.0)
|
self.unix_seconds as f64 + (self.subsecond_nanos as f64 / 1_000_000_000.0)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn as_date_time(&self) -> LocalResult<DateTime<Utc>> {
|
pub fn as_date_time(&self) -> LocalResult<DateTime<Utc>> {
|
||||||
Utc.timestamp_opt(
|
Utc.timestamp_opt(self.unix_seconds, self.subsecond_nanos)
|
||||||
self.unix_seconds,
|
|
||||||
self.subsecond_millis as u32 * 10_u32.pow(6),
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Calculate the difference in milliseconds between two UnixTimestamps
|
// Calculate the difference in milliseconds between two UnixTimestamps
|
||||||
@ -313,16 +354,16 @@ impl UnixTimestamp {
|
|||||||
let milliseconds_difference = seconds_difference * 1000;
|
let milliseconds_difference = seconds_difference * 1000;
|
||||||
|
|
||||||
// Calculate the difference in subsecond milliseconds directly
|
// Calculate the difference in subsecond milliseconds directly
|
||||||
let subsecond_difference = self.subsecond_millis as i64 - other.subsecond_millis as i64;
|
let subsecond_difference_nanos = self.subsecond_nanos as i64 - other.subsecond_nanos as i64;
|
||||||
|
|
||||||
// Combine the differences
|
// Combine the differences
|
||||||
milliseconds_difference + subsecond_difference
|
milliseconds_difference + (subsecond_difference_nanos / 1_000_000)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl From<DateTime<Utc>> for UnixTimestamp {
|
impl From<DateTime<Utc>> for UnixTimestamp {
|
||||||
fn from(value: DateTime<Utc>) -> Self {
|
fn from(value: DateTime<Utc>) -> Self {
|
||||||
Self::const_new(value.timestamp(), value.timestamp_subsec_millis() as u16)
|
Self::new(value.timestamp(), value.timestamp_subsec_nanos())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -395,7 +436,7 @@ fn get_new_stamp_after_addition(
|
|||||||
current_stamp: &UnixTimestamp,
|
current_stamp: &UnixTimestamp,
|
||||||
duration: Duration,
|
duration: Duration,
|
||||||
) -> UnixTimestamp {
|
) -> UnixTimestamp {
|
||||||
let mut new_subsec_millis = current_stamp.subsecond_millis() + duration.subsec_millis() as u16;
|
let mut new_subsec_nanos = current_stamp.subsecond_nanos() + duration.subsec_nanos();
|
||||||
let mut new_unix_seconds = current_stamp.unix_seconds;
|
let mut new_unix_seconds = current_stamp.unix_seconds;
|
||||||
let mut increment_seconds = |value: u32| {
|
let mut increment_seconds = |value: u32| {
|
||||||
if new_unix_seconds < 0 {
|
if new_unix_seconds < 0 {
|
||||||
@ -408,8 +449,8 @@ fn get_new_stamp_after_addition(
|
|||||||
.expect("new unix seconds would exceed i64::MAX");
|
.expect("new unix seconds would exceed i64::MAX");
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
if new_subsec_millis >= 1000 {
|
if new_subsec_nanos >= 1_000_000_000 {
|
||||||
new_subsec_millis -= 1000;
|
new_subsec_nanos -= 1_000_000_000;
|
||||||
increment_seconds(1);
|
increment_seconds(1);
|
||||||
}
|
}
|
||||||
increment_seconds(
|
increment_seconds(
|
||||||
@ -418,7 +459,7 @@ fn get_new_stamp_after_addition(
|
|||||||
.try_into()
|
.try_into()
|
||||||
.expect("duration seconds exceeds u32::MAX"),
|
.expect("duration seconds exceeds u32::MAX"),
|
||||||
);
|
);
|
||||||
UnixTimestamp::const_new(new_unix_seconds, new_subsec_millis)
|
UnixTimestamp::new(new_unix_seconds, new_subsec_nanos)
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Please note that this operation will panic on the following conditions:
|
/// Please note that this operation will panic on the following conditions:
|
||||||
@ -457,10 +498,15 @@ impl Add<Duration> for &UnixTimestamp {
|
|||||||
mod tests {
|
mod tests {
|
||||||
use alloc::string::ToString;
|
use alloc::string::ToString;
|
||||||
use chrono::{Datelike, Timelike};
|
use chrono::{Datelike, Timelike};
|
||||||
use std::format;
|
use std::{format, println};
|
||||||
|
|
||||||
use super::{cuc::CucError, *};
|
use super::{cuc::CucError, *};
|
||||||
|
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const UNIX_STAMP_CONST: UnixTimestamp = UnixTimestamp::new(5, 999_999_999);
|
||||||
|
#[allow(dead_code)]
|
||||||
|
const UNIX_STAMP_CONST_2: UnixTimestamp = UnixTimestamp::new_subsec_millis(5, 999);
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_days_conversion() {
|
fn test_days_conversion() {
|
||||||
assert_eq!(unix_to_ccsds_days(DAYS_CCSDS_TO_UNIX.into()), 0);
|
assert_eq!(unix_to_ccsds_days(DAYS_CCSDS_TO_UNIX.into()), 0);
|
||||||
@ -506,17 +552,18 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn basic_float_unix_stamp_test() {
|
fn basic_float_unix_stamp_test() {
|
||||||
let stamp = UnixTimestamp::new(500, 600).unwrap();
|
let stamp = UnixTimestamp::new_subsec_millis_checked(500, 600).unwrap();
|
||||||
assert_eq!(stamp.unix_seconds, 500);
|
assert_eq!(stamp.unix_seconds, 500);
|
||||||
let subsec_millis = stamp.subsecond_millis();
|
let subsec_millis = stamp.subsecond_millis();
|
||||||
assert_eq!(subsec_millis, 600);
|
assert_eq!(subsec_millis, 600);
|
||||||
|
println!("{:?}", (500.6 - stamp.unix_seconds_f64()).to_string());
|
||||||
assert!((500.6 - stamp.unix_seconds_f64()).abs() < 0.0001);
|
assert!((500.6 - stamp.unix_seconds_f64()).abs() < 0.0001);
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_ord_larger() {
|
fn test_ord_larger() {
|
||||||
let stamp0 = UnixTimestamp::new_only_seconds(5);
|
let stamp0 = UnixTimestamp::new_only_seconds(5);
|
||||||
let stamp1 = UnixTimestamp::new(5, 500).unwrap();
|
let stamp1 = UnixTimestamp::new_subsec_millis_checked(5, 500).unwrap();
|
||||||
let stamp2 = UnixTimestamp::new_only_seconds(6);
|
let stamp2 = UnixTimestamp::new_only_seconds(6);
|
||||||
assert!(stamp1 > stamp0);
|
assert!(stamp1 > stamp0);
|
||||||
assert!(stamp2 > stamp0);
|
assert!(stamp2 > stamp0);
|
||||||
@ -526,7 +573,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_ord_smaller() {
|
fn test_ord_smaller() {
|
||||||
let stamp0 = UnixTimestamp::new_only_seconds(5);
|
let stamp0 = UnixTimestamp::new_only_seconds(5);
|
||||||
let stamp1 = UnixTimestamp::new(5, 500).unwrap();
|
let stamp1 = UnixTimestamp::new_subsec_millis_checked(5, 500).unwrap();
|
||||||
let stamp2 = UnixTimestamp::new_only_seconds(6);
|
let stamp2 = UnixTimestamp::new_only_seconds(6);
|
||||||
assert!(stamp0 < stamp1);
|
assert!(stamp0 < stamp1);
|
||||||
assert!(stamp0 < stamp2);
|
assert!(stamp0 < stamp2);
|
||||||
@ -536,7 +583,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_ord_larger_neg_numbers() {
|
fn test_ord_larger_neg_numbers() {
|
||||||
let stamp0 = UnixTimestamp::new_only_seconds(-5);
|
let stamp0 = UnixTimestamp::new_only_seconds(-5);
|
||||||
let stamp1 = UnixTimestamp::new(-5, 500).unwrap();
|
let stamp1 = UnixTimestamp::new_subsec_millis_checked(-5, 500).unwrap();
|
||||||
let stamp2 = UnixTimestamp::new_only_seconds(-6);
|
let stamp2 = UnixTimestamp::new_only_seconds(-6);
|
||||||
assert!(stamp0 > stamp1);
|
assert!(stamp0 > stamp1);
|
||||||
assert!(stamp0 > stamp2);
|
assert!(stamp0 > stamp2);
|
||||||
@ -548,7 +595,7 @@ mod tests {
|
|||||||
#[test]
|
#[test]
|
||||||
fn test_ord_smaller_neg_numbers() {
|
fn test_ord_smaller_neg_numbers() {
|
||||||
let stamp0 = UnixTimestamp::new_only_seconds(-5);
|
let stamp0 = UnixTimestamp::new_only_seconds(-5);
|
||||||
let stamp1 = UnixTimestamp::new(-5, 500).unwrap();
|
let stamp1 = UnixTimestamp::new_subsec_millis_checked(-5, 500).unwrap();
|
||||||
let stamp2 = UnixTimestamp::new_only_seconds(-6);
|
let stamp2 = UnixTimestamp::new_only_seconds(-6);
|
||||||
assert!(stamp2 < stamp1);
|
assert!(stamp2 < stamp1);
|
||||||
assert!(stamp2 < stamp0);
|
assert!(stamp2 < stamp0);
|
||||||
@ -560,7 +607,7 @@ mod tests {
|
|||||||
#[allow(clippy::nonminimal_bool)]
|
#[allow(clippy::nonminimal_bool)]
|
||||||
#[test]
|
#[test]
|
||||||
fn test_eq() {
|
fn test_eq() {
|
||||||
let stamp0 = UnixTimestamp::new(5, 0).unwrap();
|
let stamp0 = UnixTimestamp::new(5, 0);
|
||||||
let stamp1 = UnixTimestamp::new_only_seconds(5);
|
let stamp1 = UnixTimestamp::new_only_seconds(5);
|
||||||
assert_eq!(stamp0, stamp1);
|
assert_eq!(stamp0, stamp1);
|
||||||
assert!(stamp0 <= stamp1);
|
assert!(stamp0 <= stamp1);
|
||||||
@ -582,7 +629,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_addition_on_ref() {
|
fn test_addition_on_ref() {
|
||||||
let stamp0 = &UnixTimestamp::new(20, 500).unwrap();
|
let stamp0 = &UnixTimestamp::new_subsec_millis_checked(20, 500).unwrap();
|
||||||
let stamp1 = stamp0 + Duration::from_millis(2500);
|
let stamp1 = stamp0 + Duration::from_millis(2500);
|
||||||
assert_eq!(stamp1.unix_seconds, 23);
|
assert_eq!(stamp1.unix_seconds, 23);
|
||||||
assert_eq!(stamp1.subsecond_millis(), 0);
|
assert_eq!(stamp1.subsecond_millis(), 0);
|
||||||
@ -609,19 +656,19 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stamp_diff_positive_0() {
|
fn test_stamp_diff_positive_0() {
|
||||||
let stamp_later = UnixTimestamp::new(2, 0).unwrap();
|
let stamp_later = UnixTimestamp::new(2, 0);
|
||||||
let StampDiff {
|
let StampDiff {
|
||||||
positive_duration,
|
positive_duration,
|
||||||
duration_absolute,
|
duration_absolute,
|
||||||
} = stamp_later - UnixTimestamp::new(1, 0).unwrap();
|
} = stamp_later - UnixTimestamp::new(1, 0);
|
||||||
assert!(positive_duration);
|
assert!(positive_duration);
|
||||||
assert_eq!(duration_absolute, Duration::from_secs(1));
|
assert_eq!(duration_absolute, Duration::from_secs(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stamp_diff_positive_1() {
|
fn test_stamp_diff_positive_1() {
|
||||||
let stamp_later = UnixTimestamp::new(3, 800).unwrap();
|
let stamp_later = UnixTimestamp::new(3, 800 * 1_000_000);
|
||||||
let stamp_earlier = UnixTimestamp::new(1, 900).unwrap();
|
let stamp_earlier = UnixTimestamp::new_subsec_millis_checked(1, 900).unwrap();
|
||||||
let StampDiff {
|
let StampDiff {
|
||||||
positive_duration,
|
positive_duration,
|
||||||
duration_absolute,
|
duration_absolute,
|
||||||
@ -632,8 +679,8 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_stamp_diff_negative() {
|
fn test_stamp_diff_negative() {
|
||||||
let stamp_later = UnixTimestamp::new(3, 800).unwrap();
|
let stamp_later = UnixTimestamp::new_subsec_millis_checked(3, 800).unwrap();
|
||||||
let stamp_earlier = UnixTimestamp::new(1, 900).unwrap();
|
let stamp_earlier = UnixTimestamp::new_subsec_millis_checked(1, 900).unwrap();
|
||||||
let StampDiff {
|
let StampDiff {
|
||||||
positive_duration,
|
positive_duration,
|
||||||
duration_absolute,
|
duration_absolute,
|
||||||
@ -644,7 +691,7 @@ mod tests {
|
|||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn test_addition_spillover() {
|
fn test_addition_spillover() {
|
||||||
let mut stamp0 = UnixTimestamp::new(1, 900).unwrap();
|
let mut stamp0 = UnixTimestamp::new_subsec_millis_checked(1, 900).unwrap();
|
||||||
stamp0 += Duration::from_millis(100);
|
stamp0 += Duration::from_millis(100);
|
||||||
assert_eq!(stamp0.unix_seconds, 2);
|
assert_eq!(stamp0.unix_seconds, 2);
|
||||||
assert_eq!(stamp0.subsecond_millis(), 0);
|
assert_eq!(stamp0.subsecond_millis(), 0);
|
||||||
|
Loading…
Reference in New Issue
Block a user