make the API even more similar
Some checks failed
Rust/spacepackets/pipeline/head There was a failure building this commit
Rust/spacepackets/pipeline/pr-main There was a failure building this commit

This commit is contained in:
Robin Müller 2024-03-14 10:50:31 +01:00
parent 05b6503851
commit 83c84bb31d
Signed by: muellerr
GPG Key ID: A649FB78196E3849
4 changed files with 200 additions and 166 deletions

View File

@ -9,7 +9,7 @@ and this project adheres to [Semantic Versioning](http://semver.org/).
# [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.
to check all the API changes in the **Changed** chapter.
## Fixed
@ -21,18 +21,23 @@ to check all the API changes.
- `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
feature gate.
- Added `CcsdsTimeProvider::timelib_date_time`.
## Changed
- `UnixTimestamp::new` renamed to `UnixTimestamp::new_checked`.
- `UnixTimestamp` now has a nanosecond subsecond precision. The `new` constructor now expects
- Renamed `CcsdsTimeProvider::date_time` to `CcsdsTimeProvider::chrono_date_time`.
- `UnixTimestamp` renamed to `UnixTime`
- `UnixTime` seconds are now private and can be retrieved using the `secs` member method.
- `UnixTime::new` renamed to `UnixTime::new_checked`.
- `UnixTime` 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
- Added new `UnixTime::new_subsec_millis` and `UnixTime::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`
- `CcsdsTimeProvider` now has a new `subsec_nanos` method in addition to a default
implementation for the `subsec_millis` method.
- `CcsdsTimeProvider::date_time` renamed to `CcsdsTimeProvider::chrono_date_time`.
- Added `UnixTime::MIN`, `UnixTime::MAX` and `UnixTime::EPOCH`.
# [v0.11.0-rc.0] 2024-03-04

View File

@ -35,7 +35,7 @@ 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,
TimeWriter, TimestampError, UnixTime, MS_PER_DAY, SECONDS_PER_DAY,
};
/// Base value for the preamble field for a time field parser to determine the time field type.
@ -188,7 +188,7 @@ pub struct TimeProvider<DaysLen: ProvidesDaysLength = DaysLen16Bits> {
submillis: u32,
/// This is not strictly necessary but still cached because it significantly simplifies the
/// calculation of [`DateTime<Utc>`].
unix_stamp: UnixTimestamp,
unix_stamp: UnixTime,
}
/// Common properties for all CDS time providers.
@ -231,7 +231,7 @@ impl ConversionFromUnix {
let ccsds_days = unix_to_ccsds_days(unix_days);
if ccsds_days == 0 && (secs_of_day > 0 || subsec_nanos > 0) || ccsds_days < 0 {
return Err(TimestampError::DateBeforeCcsdsEpoch(
UnixTimestamp::new_checked(unix_seconds, subsec_nanos)
UnixTime::new_checked(unix_seconds, subsec_nanos)
.expect("unix timestamp creation failed"),
));
}
@ -341,7 +341,7 @@ impl ConversionFromChronoDatetime {
) -> Result<Self, TimestampError> {
// The CDS timestamp does not support timestamps before the CCSDS epoch.
if dt.year() < 1958 {
return Err(TimestampError::DateBeforeCcsdsEpoch(UnixTimestamp::from(
return Err(TimestampError::DateBeforeCcsdsEpoch(UnixTime::from(
*dt,
)));
}
@ -643,7 +643,7 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
if let Some(precision) = self.precision_as_ns() {
subsec_nanos += precision;
}
self.unix_stamp = UnixTimestamp::new(unix_days_seconds, subsec_nanos);
self.unix_stamp = UnixTime::new(unix_days_seconds, subsec_nanos);
}
fn length_check(&self, buf: &[u8], len_as_bytes: usize) -> Result<(), TimestampError> {
@ -706,13 +706,13 @@ impl<ProvidesDaysLen: ProvidesDaysLength> TimeProvider<ProvidesDaysLen> {
}
fn from_unix_generic(
unix_stamp: &UnixTimestamp,
unix_stamp: &UnixTime,
days_len: LengthOfDaySegment,
submillis_prec: SubmillisPrecision,
) -> Result<Self, TimestampError> {
let conv_from_dt = ConversionFromUnix::new(
unix_stamp.unix_seconds,
unix_stamp.subsecond_nanos,
unix_stamp.secs,
unix_stamp.subsec_nanos,
submillis_prec,
)?;
Self::generic_from_conversion(days_len, conv_from_dt)
@ -844,7 +844,7 @@ impl TimeProvider<DaysLen24Bits> {
/// [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).
pub fn from_unix_stamp_with_u24_days(
unix_stamp: &UnixTimestamp,
unix_stamp: &UnixTime,
submillis_prec: SubmillisPrecision,
) -> Result<Self, TimestampError> {
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Long24Bits, submillis_prec)
@ -941,7 +941,7 @@ impl TimeProvider<DaysLen16Bits> {
/// [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).
pub fn from_unix_stamp_with_u16_days(
unix_stamp: &UnixTimestamp,
unix_stamp: &UnixTime,
submillis_prec: SubmillisPrecision,
) -> Result<Self, TimestampError> {
Self::from_unix_generic(unix_stamp, LengthOfDaySegment::Short16Bits, submillis_prec)
@ -1205,16 +1205,16 @@ impl<ProvidesDaysLen: ProvidesDaysLength> CcsdsTimeProvider for TimeProvider<Pro
}
#[inline]
fn unix_seconds(&self) -> i64 {
self.unix_stamp.unix_seconds
fn unix_secs(&self) -> i64 {
self.unix_stamp.secs
}
#[inline]
fn subsecond_nanos(&self) -> u32 {
self.unix_stamp.subsecond_nanos
fn subsec_nanos(&self) -> u32 {
self.unix_stamp.subsec_nanos
}
#[inline]
fn unix_stamp(&self) -> UnixTimestamp {
fn unix_stamp(&self) -> UnixTime {
self.unix_stamp
}
}
@ -1351,7 +1351,7 @@ impl TryFrom<TimeProvider<DaysLen24Bits>> for TimeProvider<DaysLen16Bits> {
mod tests {
use super::*;
use crate::time::TimestampError::{ByteConversion, InvalidTimeCode};
use crate::time::{UnixTimestamp, DAYS_CCSDS_TO_UNIX, MS_PER_DAY};
use crate::time::{UnixTime, DAYS_CCSDS_TO_UNIX, MS_PER_DAY};
use crate::ByteConversionError::{FromSliceTooSmall, ToSliceTooSmall};
use alloc::string::ToString;
use chrono::{Datelike, NaiveDate, Timelike};
@ -1364,16 +1364,16 @@ mod tests {
let time_stamper = TimeProvider::new_with_u16_days(0, 0);
let unix_stamp = time_stamper.unix_stamp();
assert_eq!(
unix_stamp.unix_seconds,
unix_stamp.secs,
(DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32) as i64
);
let subsecond_millis = unix_stamp.subsecond_nanos;
let subsecond_millis = unix_stamp.subsec_nanos;
assert_eq!(subsecond_millis, 0);
assert_eq!(
time_stamper.submillis_precision(),
SubmillisPrecision::Absent
);
assert_eq!(time_stamper.subsecond_nanos(), 0);
assert_eq!(time_stamper.subsec_nanos(), 0);
assert_eq!(time_stamper.ccdsd_time_code(), CcsdsTimeCodes::Cds);
assert_eq!(
time_stamper.p_field(),
@ -1391,7 +1391,7 @@ mod tests {
#[test]
fn test_time_stamp_unix_epoch() {
let time_stamper = TimeProvider::new_with_u16_days((-DAYS_CCSDS_TO_UNIX) as u16, 0);
assert_eq!(time_stamper.unix_stamp().unix_seconds, 0);
assert_eq!(time_stamper.unix_stamp().secs, 0);
assert_eq!(
time_stamper.submillis_precision(),
SubmillisPrecision::Absent
@ -1404,11 +1404,11 @@ mod tests {
assert_eq!(date_time.minute(), 0);
assert_eq!(date_time.second(), 0);
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.subsec_nanos(), 40 * 10_u32.pow(6));
assert_eq!(time_stamper.subsec_millis(), 40);
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.subsec_nanos(), 40 * 10_u32.pow(6));
assert_eq!(time_stamper.subsec_millis(), 40);
}
#[test]
@ -1470,7 +1470,7 @@ mod tests {
let time_stamper_0 = TimeProvider::new_with_u16_days(0, 0);
let unix_stamp = time_stamper_0.unix_stamp();
assert_eq!(
unix_stamp.unix_seconds,
unix_stamp.secs,
(DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32).into()
);
let mut res = time_stamper_0.write_to_bytes(&mut buf);
@ -1932,7 +1932,7 @@ mod tests {
let unix_secs = 0;
let subsec_millis = 0;
let time_provider = TimeProvider::from_unix_stamp_with_u16_days(
&UnixTimestamp::new(unix_secs, subsec_millis),
&UnixTime::new(unix_secs, subsec_millis),
SubmillisPrecision::Absent,
)
.expect("creating provider from unix stamp failed");
@ -1944,7 +1944,7 @@ mod tests {
let unix_secs = 0;
let subsec_millis = 0;
let time_provider = TimeProvider::from_unix_stamp_with_u24_days(
&UnixTimestamp::new(unix_secs, subsec_millis),
&UnixTime::new(unix_secs, subsec_millis),
SubmillisPrecision::Absent,
)
.expect("creating provider from unix stamp failed");
@ -1981,7 +1981,7 @@ mod tests {
let unix_secs = DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64;
let subsec_millis = 0;
let time_provider = TimeProvider::from_unix_stamp_with_u16_days(
&UnixTimestamp::new(unix_secs, subsec_millis),
&UnixTime::new(unix_secs, subsec_millis),
SubmillisPrecision::Absent,
)
.expect("creating provider from unix stamp failed");
@ -1993,7 +1993,7 @@ mod tests {
let invalid_unix_secs: i64 = (u16::MAX as i64 + 1) * SECONDS_PER_DAY as i64;
let subsec_millis = 0;
match TimeProvider::from_unix_stamp_with_u16_days(
&UnixTimestamp::new(invalid_unix_secs, subsec_millis),
&UnixTime::new(invalid_unix_secs, subsec_millis),
SubmillisPrecision::Absent,
) {
Ok(_) => {
@ -2020,7 +2020,7 @@ mod tests {
let unix_secs = DAYS_CCSDS_TO_UNIX * SECONDS_PER_DAY as i32 - 5;
let subsec_millis = 0;
match TimeProvider::from_unix_stamp_with_u16_days(
&UnixTimestamp::new(unix_secs as i64, subsec_millis),
&UnixTime::new(unix_secs as i64, subsec_millis),
SubmillisPrecision::Absent,
) {
Ok(_) => {
@ -2028,7 +2028,7 @@ mod tests {
}
Err(e) => {
if let TimestampError::DateBeforeCcsdsEpoch(dt) = e {
let dt = dt.as_date_time();
let dt = dt.chrono_date_time();
if let chrono::LocalResult::Single(dt) = dt {
assert_eq!(dt.year(), 1957);
assert_eq!(dt.month(), 12);
@ -2304,7 +2304,7 @@ mod tests {
fn test_update_from_now() {
let mut stamp = TimeProvider::new_with_u16_days(0, 0);
let _ = stamp.update_from_now();
let dt = stamp.unix_stamp().as_date_time().unwrap();
let dt = stamp.unix_stamp().chrono_date_time().unwrap();
assert!(dt.year() > 2020);
}

View File

@ -12,9 +12,12 @@ 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;
use super::{
ccsds_epoch_to_unix_epoch, unix_epoch_to_ccsds_epoch, CcsdsTimeCodes, CcsdsTimeProvider,
TimeReader, TimeWriter, TimestampError, UnixTime,
};
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "std")]
@ -315,7 +318,6 @@ impl TimeProviderCcsdsEpoch {
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn update_from_now(&mut self) -> Result<(), StdTimestampError> {
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
@ -340,9 +342,7 @@ impl TimeProviderCcsdsEpoch {
) -> Result<Self, TimestampError> {
// Year before CCSDS epoch is invalid.
if dt.year() < 1958 {
return Err(TimestampError::DateBeforeCcsdsEpoch(UnixTimestamp::from(
*dt,
)));
return Err(TimestampError::DateBeforeCcsdsEpoch(UnixTime::from(*dt)));
}
let counter = dt
.timestamp()
@ -359,11 +359,11 @@ impl TimeProviderCcsdsEpoch {
/// Generates a CUC timestamp from a UNIX timestamp with a width of 4. This width is able
/// to accomodate all possible UNIX timestamp values.
pub fn from_unix_stamp(
unix_stamp: &UnixTimestamp,
unix_stamp: &UnixTime,
res: FractionalResolution,
leap_seconds: u32,
) -> Result<Self, TimestampError> {
let ccsds_epoch = unix_epoch_to_ccsds_epoch(unix_stamp.unix_seconds);
let ccsds_epoch = unix_epoch_to_ccsds_epoch(unix_stamp.secs);
// Negative CCSDS epoch is invalid.
if ccsds_epoch < 0 {
return Err(TimestampError::DateBeforeCcsdsEpoch(*unix_stamp));
@ -371,10 +371,8 @@ impl TimeProviderCcsdsEpoch {
ccsds_epoch
.checked_add(i64::from(leap_seconds))
.ok_or(TimestampError::Cuc(CucError::LeapSecondCorrectionError))?;
let fractions = fractional_part_from_subsec_ns(
res,
unix_stamp.subsecond_millis() as u64 * 10_u64.pow(6),
);
let fractions =
fractional_part_from_subsec_ns(res, unix_stamp.subsec_millis() as u64 * 10_u64.pow(6));
Self::new_generic(
WidthCounterPair(4, ccsds_epoch as u32),
fractions,
@ -693,11 +691,11 @@ impl CcsdsTimeProvider for TimeProviderCcsdsEpoch {
CcsdsTimeCodes::CucCcsdsEpoch
}
fn unix_seconds(&self) -> i64 {
fn unix_secs(&self) -> i64 {
self.unix_seconds()
}
fn subsecond_nanos(&self) -> u32 {
fn subsec_nanos(&self) -> u32 {
if let Some(fractions) = self.fractions {
if fractions.0 == FractionalResolution::Seconds {
return 0;
@ -806,7 +804,7 @@ impl Add<Duration> for &TimeProviderCcsdsEpoch {
#[cfg(test)]
mod tests {
use crate::time::{UnixTimestamp, DAYS_CCSDS_TO_UNIX, SECONDS_PER_DAY};
use crate::time::{UnixTime, DAYS_CCSDS_TO_UNIX, SECONDS_PER_DAY};
use super::*;
use alloc::string::ToString;
@ -852,7 +850,7 @@ mod tests {
let zero_cuc = zero_cuc.unwrap();
let res = zero_cuc.write_to_bytes(&mut buf);
assert!(res.is_ok());
assert_eq!(zero_cuc.subsecond_nanos(), 0);
assert_eq!(zero_cuc.subsec_nanos(), 0);
assert_eq!(zero_cuc.len_as_bytes(), 5);
assert_eq!(pfield_len(buf[0]), 1);
let written = res.unwrap();
@ -1245,7 +1243,7 @@ mod tests {
// What I would roughly expect
assert_eq!(cuc_stamp2.counter.1, 203);
assert!(cuc_stamp2.fractions.unwrap().1 < 100);
assert!(cuc_stamp2.subsecond_millis() <= 1);
assert!(cuc_stamp2.subsec_millis() <= 1);
}
#[test]
@ -1327,7 +1325,7 @@ mod tests {
#[test]
fn from_unix_stamp() {
let unix_stamp = UnixTimestamp::new(0, 0);
let unix_stamp = UnixTime::new(0, 0);
let cuc = TimeProviderCcsdsEpoch::from_unix_stamp(
&unix_stamp,
FractionalResolution::Seconds,

View File

@ -29,6 +29,7 @@ pub mod cuc;
pub const DAYS_CCSDS_TO_UNIX: i32 = -4383;
pub const SECONDS_PER_DAY: u32 = 86400;
pub const MS_PER_DAY: u32 = SECONDS_PER_DAY * 1000;
pub const NANOS_PER_SECOND: u32 = 1_000_000_000;
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
@ -70,7 +71,7 @@ pub enum TimestampError {
ByteConversion(ByteConversionError),
Cds(cds::CdsError),
Cuc(cuc::CucError),
DateBeforeCcsdsEpoch(UnixTimestamp),
DateBeforeCcsdsEpoch(UnixTime),
CustomEpochNotSupported,
}
@ -232,21 +233,21 @@ pub trait CcsdsTimeProvider {
fn p_field(&self) -> (usize, [u8; 2]);
fn ccdsd_time_code(&self) -> CcsdsTimeCodes;
fn unix_seconds(&self) -> i64;
fn subsecond_nanos(&self) -> u32;
fn unix_secs(&self) -> i64;
fn subsec_nanos(&self) -> u32;
fn subsecond_millis(&self) -> u16 {
(self.subsecond_nanos() / 1_000_000) as u16
fn subsec_millis(&self) -> u16 {
(self.subsec_nanos() / 1_000_000) as u16
}
fn unix_stamp(&self) -> UnixTimestamp {
UnixTimestamp::new(self.unix_seconds(), self.subsecond_nanos())
fn unix_stamp(&self) -> UnixTime {
UnixTime::new(self.unix_secs(), self.subsec_nanos())
}
#[cfg(feature = "chrono")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
fn chrono_date_time(&self) -> chrono::LocalResult<chrono::DateTime<chrono::Utc>> {
chrono::Utc.timestamp_opt(self.unix_seconds(), self.subsecond_nanos())
chrono::Utc.timestamp_opt(self.unix_secs(), self.subsec_nanos())
}
#[cfg(feature = "timelib")]
@ -259,22 +260,41 @@ pub trait CcsdsTimeProvider {
}
}
/// UNIX timestamp: Elapsed seconds since 1970-01-01T00:00:00+00:00.
/// UNIX time: Elapsed non-leap seconds since 1970-01-01T00:00:00+00:00 UTC.
///
/// 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.
/// This is a commonly used time format and can therefore also be used as a generic format to
/// convert other CCSDS time formats to and from. The subsecond precision is in nanoseconds
/// similarly to other common time formats and libraries.
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub struct UnixTimestamp {
pub unix_seconds: i64,
subsecond_nanos: u32,
pub struct UnixTime {
secs: i64,
subsec_nanos: u32,
}
impl UnixTimestamp {
impl UnixTime {
/// The UNIX epoch time: 1970-01-01T00:00:00+00:00 UTC.
pub const EPOCH: Self = Self {
secs: 0,
subsec_nanos: 0,
};
/// The minimum possible `UnixTime`.
pub const MIN: Self = Self {
secs: i64::MIN,
subsec_nanos: 0,
};
/// The maximum possible `UnixTime`.
pub const MAX: Self = Self {
secs: i64::MAX,
subsec_nanos: NANOS_PER_SECOND - 1,
};
/// Returns [None] if the subsecond nanosecond value is invalid (larger than fraction of a
/// second)
pub fn new_checked(unix_seconds: i64, subsec_nanos: u32) -> Option<Self> {
if subsec_nanos > 1_000_000_000 - 1 {
if subsec_nanos >= NANOS_PER_SECOND {
return None;
}
Some(Self::new(unix_seconds, subsec_nanos))
@ -283,7 +303,7 @@ impl UnixTimestamp {
/// 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 >= 1000 {
return None;
}
Self::new_checked(unix_seconds, subsec_millis as u32 * 1_000_000)
@ -292,42 +312,41 @@ impl UnixTimestamp {
/// This function will panic if the subsecond value is larger than the fraction of a second.
/// Use [new_checked] if you want to handle this case without a panic.
pub const fn new(unix_seconds: i64, subsecond_nanos: u32) -> Self {
// This could actually be used to model leap seconds, but we will ignore that for now.
if subsecond_nanos > 1_000_000_000 - 1 {
if subsecond_nanos >= NANOS_PER_SECOND {
panic!("invalid subsecond nanos value");
}
Self {
unix_seconds,
subsecond_nanos,
secs: unix_seconds,
subsec_nanos: 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 {
if subsecond_millis >= 1000 {
panic!("invalid subsecond millisecond value");
}
Self {
unix_seconds,
subsecond_nanos: subsecond_millis as u32 * 1_000_000,
secs: unix_seconds,
subsec_nanos: subsecond_millis as u32 * 1_000_000,
}
}
pub fn new_only_seconds(unix_seconds: i64) -> Self {
pub fn new_only_secs(unix_seconds: i64) -> Self {
Self {
unix_seconds,
subsecond_nanos: 0,
secs: unix_seconds,
subsec_nanos: 0,
}
}
pub fn subsecond_millis(&self) -> u16 {
(self.subsecond_nanos / 1_000_000) as u16
#[inline]
pub fn subsec_millis(&self) -> u16 {
(self.subsec_nanos / 1_000_000) as u16
}
pub fn subsecond_nanos(&self) -> u32 {
self.subsecond_nanos
pub fn subsec_nanos(&self) -> u32 {
self.subsec_nanos
}
#[cfg(feature = "std")]
@ -339,61 +358,76 @@ impl UnixTimestamp {
}
#[inline]
pub fn unix_seconds_f64(&self) -> f64 {
self.unix_seconds as f64 + (self.subsecond_nanos as f64 / 1_000_000_000.0)
pub fn unix_secs_f64(&self) -> f64 {
self.secs as f64 + (self.subsec_nanos as f64 / 1_000_000_000.0)
}
pub fn as_date_time(&self) -> LocalResult<DateTime<Utc>> {
Utc.timestamp_opt(self.unix_seconds, self.subsecond_nanos)
pub fn secs(&self) -> i64 {
self.secs
}
#[cfg(feature = "chrono")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "chrono")))]
pub fn chrono_date_time(&self) -> LocalResult<DateTime<Utc>> {
Utc.timestamp_opt(self.secs, self.subsec_nanos)
}
#[cfg(feature = "timelib")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "timelib")))]
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()),
)
}
// Calculate the difference in milliseconds between two UnixTimestamps
pub fn difference_in_millis(&self, other: &UnixTimestamp) -> i64 {
let seconds_difference = self.unix_seconds - other.unix_seconds;
pub fn diff_in_millis(&self, other: &UnixTime) -> i64 {
let seconds_difference = self.secs - other.secs;
// Convert seconds difference to milliseconds
let milliseconds_difference = seconds_difference * 1000;
// Calculate the difference in subsecond milliseconds directly
let subsecond_difference_nanos = self.subsecond_nanos as i64 - other.subsecond_nanos as i64;
let subsecond_difference_nanos = self.subsec_nanos as i64 - other.subsec_nanos as i64;
// Combine the differences
milliseconds_difference + (subsecond_difference_nanos / 1_000_000)
}
}
impl From<DateTime<Utc>> for UnixTimestamp {
impl From<DateTime<Utc>> for UnixTime {
fn from(value: DateTime<Utc>) -> Self {
Self::new(value.timestamp(), value.timestamp_subsec_nanos())
}
}
impl PartialOrd for UnixTimestamp {
impl PartialOrd for UnixTime {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
Some(self.cmp(other))
}
}
impl Ord for UnixTimestamp {
impl Ord for UnixTime {
fn cmp(&self, other: &Self) -> Ordering {
if self == other {
return Ordering::Equal;
}
match self.unix_seconds.cmp(&other.unix_seconds) {
match self.secs.cmp(&other.secs) {
Ordering::Less => return Ordering::Less,
Ordering::Greater => return Ordering::Greater,
_ => (),
}
match self.subsecond_millis().cmp(&other.subsecond_millis()) {
match self.subsec_millis().cmp(&other.subsec_millis()) {
Ordering::Less => {
return if self.unix_seconds < 0 {
return if self.secs < 0 {
Ordering::Greater
} else {
Ordering::Less
}
}
Ordering::Greater => {
return if self.unix_seconds < 0 {
return if self.secs < 0 {
Ordering::Less
} else {
Ordering::Greater
@ -413,11 +447,11 @@ pub struct StampDiff {
pub duration_absolute: Duration,
}
impl Sub for UnixTimestamp {
impl Sub for UnixTime {
type Output = StampDiff;
fn sub(self, rhs: Self) -> Self::Output {
let difference = self.difference_in_millis(&rhs);
let difference = self.diff_in_millis(&rhs);
if difference < 0 {
StampDiff {
positive_duration: false,
@ -432,12 +466,9 @@ impl Sub for UnixTimestamp {
}
}
fn get_new_stamp_after_addition(
current_stamp: &UnixTimestamp,
duration: Duration,
) -> UnixTimestamp {
let mut new_subsec_nanos = current_stamp.subsecond_nanos() + duration.subsec_nanos();
let mut new_unix_seconds = current_stamp.unix_seconds;
fn get_new_stamp_after_addition(current_stamp: &UnixTime, duration: Duration) -> UnixTime {
let mut new_subsec_nanos = current_stamp.subsec_nanos() + duration.subsec_nanos();
let mut new_unix_seconds = current_stamp.secs;
let mut increment_seconds = |value: u32| {
if new_unix_seconds < 0 {
new_unix_seconds = new_unix_seconds
@ -459,7 +490,7 @@ fn get_new_stamp_after_addition(
.try_into()
.expect("duration seconds exceeds u32::MAX"),
);
UnixTimestamp::new(new_unix_seconds, new_subsec_nanos)
UnixTime::new(new_unix_seconds, new_subsec_nanos)
}
/// Please note that this operation will panic on the following conditions:
@ -467,7 +498,7 @@ fn get_new_stamp_after_addition(
/// - Unix seconds after subtraction for stamps before the unix epoch exceeds [i64::MIN].
/// - Unix seconds after addition exceeds [i64::MAX].
/// - Seconds from duration to add exceeds [u32::MAX].
impl AddAssign<Duration> for UnixTimestamp {
impl AddAssign<Duration> for UnixTime {
fn add_assign(&mut self, duration: Duration) {
*self = get_new_stamp_after_addition(self, duration);
}
@ -478,7 +509,7 @@ impl AddAssign<Duration> for UnixTimestamp {
/// - Unix seconds after subtraction for stamps before the unix epoch exceeds [i64::MIN].
/// - Unix seconds after addition exceeds [i64::MAX].
/// - Unix seconds exceeds [u32::MAX].
impl Add<Duration> for UnixTimestamp {
impl Add<Duration> for UnixTime {
type Output = Self;
fn add(self, duration: Duration) -> Self::Output {
@ -486,8 +517,8 @@ impl Add<Duration> for UnixTimestamp {
}
}
impl Add<Duration> for &UnixTimestamp {
type Output = UnixTimestamp;
impl Add<Duration> for &UnixTime {
type Output = UnixTime;
fn add(self, duration: Duration) -> Self::Output {
get_new_stamp_after_addition(self, duration)
@ -503,9 +534,9 @@ mod tests {
use super::{cuc::CucError, *};
#[allow(dead_code)]
const UNIX_STAMP_CONST: UnixTimestamp = UnixTimestamp::new(5, 999_999_999);
const UNIX_STAMP_CONST: UnixTime = UnixTime::new(5, 999_999_999);
#[allow(dead_code)]
const UNIX_STAMP_CONST_2: UnixTimestamp = UnixTimestamp::new_subsec_millis(5, 999);
const UNIX_STAMP_CONST_2: UnixTime = UnixTime::new_subsec_millis(5, 999);
#[test]
fn test_days_conversion() {
@ -542,29 +573,29 @@ mod tests {
#[test]
fn basic_unix_stamp_test() {
let stamp = UnixTimestamp::new_only_seconds(-200);
assert_eq!(stamp.unix_seconds, -200);
assert_eq!(stamp.subsecond_millis(), 0);
let stamp = UnixTimestamp::new_only_seconds(250);
assert_eq!(stamp.unix_seconds, 250);
assert_eq!(stamp.subsecond_millis(), 0);
let stamp = UnixTime::new_only_secs(-200);
assert_eq!(stamp.secs, -200);
assert_eq!(stamp.subsec_millis(), 0);
let stamp = UnixTime::new_only_secs(250);
assert_eq!(stamp.secs, 250);
assert_eq!(stamp.subsec_millis(), 0);
}
#[test]
fn basic_float_unix_stamp_test() {
let stamp = UnixTimestamp::new_subsec_millis_checked(500, 600).unwrap();
assert_eq!(stamp.unix_seconds, 500);
let subsec_millis = stamp.subsecond_millis();
let stamp = UnixTime::new_subsec_millis_checked(500, 600).unwrap();
assert_eq!(stamp.secs, 500);
let subsec_millis = stamp.subsec_millis();
assert_eq!(subsec_millis, 600);
println!("{:?}", (500.6 - stamp.unix_seconds_f64()).to_string());
assert!((500.6 - stamp.unix_seconds_f64()).abs() < 0.0001);
println!("{:?}", (500.6 - stamp.unix_secs_f64()).to_string());
assert!((500.6 - stamp.unix_secs_f64()).abs() < 0.0001);
}
#[test]
fn test_ord_larger() {
let stamp0 = UnixTimestamp::new_only_seconds(5);
let stamp1 = UnixTimestamp::new_subsec_millis_checked(5, 500).unwrap();
let stamp2 = UnixTimestamp::new_only_seconds(6);
let stamp0 = UnixTime::new_only_secs(5);
let stamp1 = UnixTime::new_subsec_millis_checked(5, 500).unwrap();
let stamp2 = UnixTime::new_only_secs(6);
assert!(stamp1 > stamp0);
assert!(stamp2 > stamp0);
assert!(stamp2 > stamp1);
@ -572,9 +603,9 @@ mod tests {
#[test]
fn test_ord_smaller() {
let stamp0 = UnixTimestamp::new_only_seconds(5);
let stamp1 = UnixTimestamp::new_subsec_millis_checked(5, 500).unwrap();
let stamp2 = UnixTimestamp::new_only_seconds(6);
let stamp0 = UnixTime::new_only_secs(5);
let stamp1 = UnixTime::new_subsec_millis_checked(5, 500).unwrap();
let stamp2 = UnixTime::new_only_secs(6);
assert!(stamp0 < stamp1);
assert!(stamp0 < stamp2);
assert!(stamp1 < stamp2);
@ -582,9 +613,9 @@ mod tests {
#[test]
fn test_ord_larger_neg_numbers() {
let stamp0 = UnixTimestamp::new_only_seconds(-5);
let stamp1 = UnixTimestamp::new_subsec_millis_checked(-5, 500).unwrap();
let stamp2 = UnixTimestamp::new_only_seconds(-6);
let stamp0 = UnixTime::new_only_secs(-5);
let stamp1 = UnixTime::new_subsec_millis_checked(-5, 500).unwrap();
let stamp2 = UnixTime::new_only_secs(-6);
assert!(stamp0 > stamp1);
assert!(stamp0 > stamp2);
assert!(stamp1 > stamp2);
@ -594,9 +625,9 @@ mod tests {
#[test]
fn test_ord_smaller_neg_numbers() {
let stamp0 = UnixTimestamp::new_only_seconds(-5);
let stamp1 = UnixTimestamp::new_subsec_millis_checked(-5, 500).unwrap();
let stamp2 = UnixTimestamp::new_only_seconds(-6);
let stamp0 = UnixTime::new_only_secs(-5);
let stamp1 = UnixTime::new_subsec_millis_checked(-5, 500).unwrap();
let stamp2 = UnixTime::new_only_secs(-6);
assert!(stamp2 < stamp1);
assert!(stamp2 < stamp0);
assert!(stamp1 < stamp0);
@ -607,8 +638,8 @@ mod tests {
#[allow(clippy::nonminimal_bool)]
#[test]
fn test_eq() {
let stamp0 = UnixTimestamp::new(5, 0);
let stamp1 = UnixTimestamp::new_only_seconds(5);
let stamp0 = UnixTime::new(5, 0);
let stamp1 = UnixTime::new_only_secs(5);
assert_eq!(stamp0, stamp1);
assert!(stamp0 <= stamp1);
assert!(stamp0 >= stamp1);
@ -618,27 +649,27 @@ mod tests {
#[test]
fn test_addition() {
let mut stamp0 = UnixTimestamp::new_only_seconds(1);
let mut stamp0 = UnixTime::new_only_secs(1);
stamp0 += Duration::from_secs(5);
assert_eq!(stamp0.unix_seconds, 6);
assert_eq!(stamp0.subsecond_millis(), 0);
assert_eq!(stamp0.secs, 6);
assert_eq!(stamp0.subsec_millis(), 0);
let stamp1 = stamp0 + Duration::from_millis(500);
assert_eq!(stamp1.unix_seconds, 6);
assert_eq!(stamp1.subsecond_millis(), 500);
assert_eq!(stamp1.secs, 6);
assert_eq!(stamp1.subsec_millis(), 500);
}
#[test]
fn test_addition_on_ref() {
let stamp0 = &UnixTimestamp::new_subsec_millis_checked(20, 500).unwrap();
let stamp0 = &UnixTime::new_subsec_millis_checked(20, 500).unwrap();
let stamp1 = stamp0 + Duration::from_millis(2500);
assert_eq!(stamp1.unix_seconds, 23);
assert_eq!(stamp1.subsecond_millis(), 0);
assert_eq!(stamp1.secs, 23);
assert_eq!(stamp1.subsec_millis(), 0);
}
#[test]
fn test_as_dt() {
let stamp = UnixTimestamp::new_only_seconds(0);
let dt = stamp.as_date_time().unwrap();
let stamp = UnixTime::new_only_secs(0);
let dt = stamp.chrono_date_time().unwrap();
assert_eq!(dt.year(), 1970);
assert_eq!(dt.month(), 1);
assert_eq!(dt.day(), 1);
@ -649,26 +680,26 @@ mod tests {
#[test]
fn test_from_now() {
let stamp_now = UnixTimestamp::from_now().unwrap();
let dt_now = stamp_now.as_date_time().unwrap();
let stamp_now = UnixTime::from_now().unwrap();
let dt_now = stamp_now.chrono_date_time().unwrap();
assert!(dt_now.year() >= 2020);
}
#[test]
fn test_stamp_diff_positive_0() {
let stamp_later = UnixTimestamp::new(2, 0);
let stamp_later = UnixTime::new(2, 0);
let StampDiff {
positive_duration,
duration_absolute,
} = stamp_later - UnixTimestamp::new(1, 0);
} = stamp_later - UnixTime::new(1, 0);
assert!(positive_duration);
assert_eq!(duration_absolute, Duration::from_secs(1));
}
#[test]
fn test_stamp_diff_positive_1() {
let stamp_later = UnixTimestamp::new(3, 800 * 1_000_000);
let stamp_earlier = UnixTimestamp::new_subsec_millis_checked(1, 900).unwrap();
let stamp_later = UnixTime::new(3, 800 * 1_000_000);
let stamp_earlier = UnixTime::new_subsec_millis_checked(1, 900).unwrap();
let StampDiff {
positive_duration,
duration_absolute,
@ -679,8 +710,8 @@ mod tests {
#[test]
fn test_stamp_diff_negative() {
let stamp_later = UnixTimestamp::new_subsec_millis_checked(3, 800).unwrap();
let stamp_earlier = UnixTimestamp::new_subsec_millis_checked(1, 900).unwrap();
let stamp_later = UnixTime::new_subsec_millis_checked(3, 800).unwrap();
let stamp_earlier = UnixTime::new_subsec_millis_checked(1, 900).unwrap();
let StampDiff {
positive_duration,
duration_absolute,
@ -691,13 +722,13 @@ mod tests {
#[test]
fn test_addition_spillover() {
let mut stamp0 = UnixTimestamp::new_subsec_millis_checked(1, 900).unwrap();
let mut stamp0 = UnixTime::new_subsec_millis_checked(1, 900).unwrap();
stamp0 += Duration::from_millis(100);
assert_eq!(stamp0.unix_seconds, 2);
assert_eq!(stamp0.subsecond_millis(), 0);
assert_eq!(stamp0.secs, 2);
assert_eq!(stamp0.subsec_millis(), 0);
stamp0 += Duration::from_millis(1100);
assert_eq!(stamp0.unix_seconds, 3);
assert_eq!(stamp0.subsecond_millis(), 100);
assert_eq!(stamp0.secs, 3);
assert_eq!(stamp0.subsec_millis(), 100);
}
#[test]