spacepackets/src/time/mod.rs

651 lines
21 KiB
Rust
Raw Normal View History

2022-12-08 15:22:19 +01:00
//! CCSDS Time Code Formats according to [CCSDS 301.0-B-4](https://public.ccsds.org/Pubs/301x0b4e1.pdf)
2023-08-18 10:09:32 +02:00
use crate::ByteConversionError;
2022-12-08 15:22:19 +01:00
use chrono::{DateTime, LocalResult, TimeZone, Utc};
2023-01-21 01:25:05 +01:00
use core::cmp::Ordering;
2023-01-21 11:37:57 +01:00
use core::fmt::{Display, Formatter};
2024-02-27 15:59:04 +01:00
use core::ops::{Add, AddAssign, Sub};
2023-01-21 13:17:38 +01:00
use core::time::Duration;
2023-08-28 17:10:45 +02:00
use core::u8;
2022-12-08 15:22:19 +01:00
#[allow(unused_imports)]
#[cfg(not(feature = "std"))]
use num_traits::float::FloatCore;
#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};
#[cfg(feature = "std")]
use std::error::Error;
#[cfg(feature = "std")]
use std::time::{SystemTime, SystemTimeError};
2023-07-09 16:48:53 +02:00
#[cfg(feature = "std")]
2023-07-10 00:00:20 +02:00
pub use std_mod::*;
2022-12-08 15:22:19 +01:00
pub mod ascii;
pub mod cds;
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;
2022-12-08 15:22:19 +01:00
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
pub enum CcsdsTimeCodes {
CucCcsdsEpoch = 0b001,
CucAgencyEpoch = 0b010,
Cds = 0b100,
Ccs = 0b101,
AgencyDefined = 0b110,
}
impl TryFrom<u8> for CcsdsTimeCodes {
type Error = ();
fn try_from(value: u8) -> Result<Self, Self::Error> {
match value {
x if x == CcsdsTimeCodes::CucCcsdsEpoch as u8 => Ok(CcsdsTimeCodes::CucCcsdsEpoch),
x if x == CcsdsTimeCodes::CucAgencyEpoch as u8 => Ok(CcsdsTimeCodes::CucAgencyEpoch),
x if x == CcsdsTimeCodes::Cds as u8 => Ok(CcsdsTimeCodes::Cds),
x if x == CcsdsTimeCodes::Ccs as u8 => Ok(CcsdsTimeCodes::Ccs),
x if x == CcsdsTimeCodes::AgencyDefined as u8 => Ok(CcsdsTimeCodes::AgencyDefined),
_ => Err(()),
}
}
}
/// Retrieve the CCSDS time code from the p-field. If no valid time code identifier is found, the
/// value of the raw time code identification field is returned.
pub fn ccsds_time_code_from_p_field(pfield: u8) -> Result<CcsdsTimeCodes, u8> {
let raw_bits = (pfield >> 4) & 0b111;
CcsdsTimeCodes::try_from(raw_bits).map_err(|_| raw_bits)
}
#[derive(Debug, PartialEq, Eq, Copy, Clone)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2023-01-15 20:48:39 +01:00
#[non_exhaustive]
2022-12-08 15:22:19 +01:00
pub enum TimestampError {
2023-08-28 17:10:45 +02:00
InvalidTimeCode { expected: CcsdsTimeCodes, found: u8 },
2023-07-09 16:46:25 +02:00
ByteConversion(ByteConversionError),
Cds(cds::CdsError),
Cuc(cuc::CucError),
2023-01-14 15:37:18 +01:00
DateBeforeCcsdsEpoch(DateTime<Utc>),
2022-12-08 15:22:19 +01:00
CustomEpochNotSupported,
}
impl Display for TimestampError {
fn fmt(&self, f: &mut Formatter<'_>) -> core::fmt::Result {
match self {
2023-08-28 17:10:45 +02:00
TimestampError::InvalidTimeCode { expected, found } => {
2022-12-08 15:22:19 +01:00
write!(
f,
2023-08-28 17:10:45 +02:00
"invalid raw time code value {found} for time code {expected:?}"
2022-12-08 15:22:19 +01:00
)
}
2023-07-09 16:46:25 +02:00
TimestampError::Cds(e) => {
2023-12-05 15:05:00 +01:00
write!(f, "cds error: {e}")
2022-12-08 15:22:19 +01:00
}
2023-07-09 16:46:25 +02:00
TimestampError::Cuc(e) => {
2023-12-05 15:05:00 +01:00
write!(f, "cuc error: {e}")
2022-12-08 15:22:19 +01:00
}
2023-07-09 16:46:25 +02:00
TimestampError::ByteConversion(e) => {
2023-12-05 15:05:00 +01:00
write!(f, "time stamp: {e}")
2022-12-08 15:22:19 +01:00
}
2023-01-14 15:37:18 +01:00
TimestampError::DateBeforeCcsdsEpoch(e) => {
2023-01-26 21:57:45 +01:00
write!(f, "datetime with date before ccsds epoch: {e}")
2023-01-14 15:37:18 +01:00
}
2022-12-08 15:22:19 +01:00
TimestampError::CustomEpochNotSupported => {
write!(f, "custom epochs are not supported")
}
}
}
}
#[cfg(feature = "std")]
impl Error for TimestampError {
fn source(&self) -> Option<&(dyn Error + 'static)> {
match self {
2023-07-09 16:46:25 +02:00
TimestampError::ByteConversion(e) => Some(e),
TimestampError::Cds(e) => Some(e),
TimestampError::Cuc(e) => Some(e),
2022-12-08 15:22:19 +01:00
_ => None,
}
}
}
impl From<cds::CdsError> for TimestampError {
fn from(e: cds::CdsError) -> Self {
2023-07-09 16:46:25 +02:00
TimestampError::Cds(e)
}
}
impl From<cuc::CucError> for TimestampError {
fn from(e: cuc::CucError) -> Self {
2023-07-09 16:46:25 +02:00
TimestampError::Cuc(e)
}
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub mod std_mod {
use crate::time::TimestampError;
use std::time::SystemTimeError;
use thiserror::Error;
#[derive(Debug, Clone, Error)]
pub enum StdTimestampError {
#[error("system time error: {0}")]
2023-07-09 16:46:25 +02:00
SystemTime(#[from] SystemTimeError),
#[error("timestamp error: {0}")]
2023-07-09 16:46:25 +02:00
Timestamp(#[from] TimestampError),
}
}
2022-12-08 15:22:19 +01:00
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn seconds_since_epoch() -> f64 {
SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.expect("System time generation failed")
.as_secs_f64()
}
/// Convert UNIX days to CCSDS days
///
2023-01-22 13:18:51 +01:00
/// - CCSDS epoch: 1958-01-01T00:00:00+00:00
/// - UNIX Epoch: 1970-01-01T00:00:00+00:00
2022-12-08 15:22:19 +01:00
pub const fn unix_to_ccsds_days(unix_days: i64) -> i64 {
unix_days - DAYS_CCSDS_TO_UNIX as i64
}
/// Convert CCSDS days to UNIX days
///
2023-01-22 13:18:51 +01:00
/// - CCSDS epoch: 1958-01-01T00:00:00+00:00
/// - UNIX Epoch: 1970-01-01T00:00:00+00:00
2022-12-08 15:22:19 +01:00
pub const fn ccsds_to_unix_days(ccsds_days: i64) -> i64 {
ccsds_days + DAYS_CCSDS_TO_UNIX as i64
}
2022-12-09 13:50:04 +01:00
/// Similar to [unix_to_ccsds_days] but converts the epoch instead, which is the number of elpased
/// seconds since the CCSDS and UNIX epoch times.
2023-01-17 01:16:07 +01:00
pub const fn unix_epoch_to_ccsds_epoch(unix_epoch: i64) -> i64 {
unix_epoch - (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64)
2022-12-09 13:50:04 +01:00
}
2023-01-17 01:16:07 +01:00
pub const fn ccsds_epoch_to_unix_epoch(ccsds_epoch: i64) -> i64 {
ccsds_epoch + (DAYS_CCSDS_TO_UNIX as i64 * SECONDS_PER_DAY as i64)
2022-12-09 17:21:45 +01:00
}
2022-12-08 15:22:19 +01:00
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn ms_of_day_using_sysclock() -> u32 {
ms_of_day(seconds_since_epoch())
}
pub fn ms_of_day(seconds_since_epoch: f64) -> u32 {
let fraction_ms = seconds_since_epoch - seconds_since_epoch.floor();
let ms_of_day: u32 = (((seconds_since_epoch.floor() as u32 % SECONDS_PER_DAY) * 1000) as f64
+ fraction_ms)
.floor() as u32;
ms_of_day
}
pub trait TimeWriter {
2024-02-05 15:04:29 +01:00
fn len_written(&self) -> usize;
2022-12-08 15:22:19 +01:00
/// Generic function to convert write a timestamp into a raw buffer.
/// Returns the number of written bytes on success.
fn write_to_bytes(&self, bytes: &mut [u8]) -> Result<usize, TimestampError>;
2024-02-05 15:04:29 +01:00
#[cfg(feature = "alloc")]
2024-03-04 12:55:16 +01:00
#[cfg_attr(doc_cfg, doc(cfg(feature = "alloc")))]
2024-02-05 15:04:29 +01:00
fn to_vec(&self) -> Result<alloc::vec::Vec<u8>, TimestampError> {
let mut vec = alloc::vec![0; self.len_written()];
self.write_to_bytes(&mut vec)?;
Ok(vec)
}
2022-12-08 15:22:19 +01:00
}
pub trait TimeReader {
fn from_bytes(buf: &[u8]) -> Result<Self, TimestampError>
where
Self: Sized;
}
/// Trait for generic CCSDS time providers.
2023-01-15 17:42:23 +01:00
///
2023-01-20 19:46:26 +01:00
/// The UNIX helper methods and the [Self::date_time] method are not strictly necessary but extremely
2023-01-15 17:42:23 +01:00
/// practical because they are a very common and simple exchange format for time information.
2022-12-08 15:22:19 +01:00
pub trait CcsdsTimeProvider {
fn len_as_bytes(&self) -> usize;
/// Returns the pfield of the time provider. The pfield can have one or two bytes depending
/// on the extension bit (first bit). The time provider should returns a tuple where the first
/// entry denotes the length of the pfield and the second entry is the value of the pfield
/// in big endian format.
fn p_field(&self) -> (usize, [u8; 2]);
fn ccdsd_time_code(&self) -> CcsdsTimeCodes;
2023-01-15 17:42:23 +01:00
2022-12-08 15:22:19 +01:00
fn unix_seconds(&self) -> i64;
2024-02-27 15:59:04 +01:00
fn subsecond_millis(&self) -> u16;
2023-01-15 20:48:39 +01:00
fn unix_stamp(&self) -> UnixTimestamp {
2024-02-27 15:59:04 +01:00
UnixTimestamp::const_new(self.unix_seconds(), self.subsecond_millis())
2023-01-15 17:42:23 +01:00
}
2022-12-08 15:22:19 +01:00
fn date_time(&self) -> Option<DateTime<Utc>>;
}
2023-01-22 13:18:51 +01:00
/// UNIX timestamp: Elapsed seconds since 1970-01-01T00:00:00+00:00.
2023-01-15 17:42:23 +01:00
///
2024-02-27 15:59:04 +01:00
/// Also can optionally include subsecond millisecond for greater accuracy.
2023-01-15 17:42:23 +01:00
#[derive(Default, Debug, Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
2023-01-15 20:48:39 +01:00
pub struct UnixTimestamp {
2023-01-15 17:42:23 +01:00
pub unix_seconds: i64,
2024-02-27 15:59:04 +01:00
subsecond_millis: u16,
2023-01-15 17:42:23 +01:00
}
2023-01-15 20:48:39 +01:00
impl UnixTimestamp {
2024-02-27 15:59:04 +01:00
/// Returns [None] if the subsecond millisecond value is larger than 999.
2023-01-15 21:30:31 +01:00
pub fn new(unix_seconds: i64, subsec_millis: u16) -> Option<Self> {
2023-01-15 17:42:23 +01:00
if subsec_millis > 999 {
return None;
}
2023-01-21 01:25:05 +01:00
Some(Self::const_new(unix_seconds, subsec_millis))
2023-01-15 17:42:23 +01:00
}
2023-01-21 01:25:05 +01:00
/// Like [Self::new] but const. Panics if the subsecond value is larger than 999.
2024-02-27 15:59:04 +01:00
pub const fn const_new(unix_seconds: i64, subsecond_millis: u16) -> Self {
if subsecond_millis > 999 {
2023-01-15 21:30:31 +01:00
panic!("subsec milliseconds exceeds 999");
}
Self {
unix_seconds,
2023-01-21 01:25:05 +01:00
subsecond_millis,
2023-01-15 21:30:31 +01:00
}
}
pub fn new_only_seconds(unix_seconds: i64) -> Self {
Self {
unix_seconds,
2024-02-27 15:59:04 +01:00
subsecond_millis: 0,
2023-01-15 21:30:31 +01:00
}
}
2024-02-27 15:59:04 +01:00
pub fn subsecond_millis(&self) -> u16 {
2023-01-15 17:42:23 +01:00
self.subsecond_millis
}
#[cfg(feature = "std")]
#[cfg_attr(doc_cfg, doc(cfg(feature = "std")))]
pub fn from_now() -> Result<Self, SystemTimeError> {
let now = SystemTime::now().duration_since(SystemTime::UNIX_EPOCH)?;
let epoch = now.as_secs();
2023-02-05 18:47:46 +01:00
Ok(Self::const_new(epoch as i64, now.subsec_millis() as u16))
2023-01-15 17:42:23 +01:00
}
#[inline]
pub fn unix_seconds_f64(&self) -> f64 {
2024-02-27 15:59:04 +01:00
self.unix_seconds as f64 + (self.subsecond_millis as f64 / 1000.0)
2023-01-15 17:42:23 +01:00
}
2023-01-15 21:47:35 +01:00
pub fn as_date_time(&self) -> LocalResult<DateTime<Utc>> {
Utc.timestamp_opt(
self.unix_seconds,
2024-02-27 15:59:04 +01:00
self.subsecond_millis as u32 * 10_u32.pow(6),
2023-01-15 21:47:35 +01:00
)
}
2024-02-27 15:59:04 +01:00
// 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;
// Convert seconds difference to milliseconds
let milliseconds_difference = seconds_difference * 1000;
// Calculate the difference in subsecond milliseconds directly
let subsecond_difference = self.subsecond_millis as i64 - other.subsecond_millis as i64;
// Combine the differences
milliseconds_difference + subsecond_difference
}
2023-01-15 17:42:23 +01:00
}
2023-01-15 21:30:31 +01:00
impl From<DateTime<Utc>> for UnixTimestamp {
fn from(value: DateTime<Utc>) -> Self {
2023-02-05 18:47:46 +01:00
Self::const_new(value.timestamp(), value.timestamp_subsec_millis() as u16)
2023-01-15 21:30:31 +01:00
}
}
2023-01-21 01:25:05 +01:00
impl PartialOrd for UnixTimestamp {
fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
2023-10-10 10:00:14 +02:00
Some(self.cmp(other))
}
}
impl Ord for UnixTimestamp {
fn cmp(&self, other: &Self) -> Ordering {
2023-01-21 01:25:05 +01:00
if self == other {
2023-10-10 10:00:14 +02:00
return Ordering::Equal;
2023-01-21 01:25:05 +01:00
}
match self.unix_seconds.cmp(&other.unix_seconds) {
2023-10-10 10:00:14 +02:00
Ordering::Less => return Ordering::Less,
Ordering::Greater => return Ordering::Greater,
2023-01-21 01:25:05 +01:00
_ => (),
}
2024-02-27 15:59:04 +01:00
match self.subsecond_millis().cmp(&other.subsecond_millis()) {
2023-01-21 01:25:05 +01:00
Ordering::Less => {
return if self.unix_seconds < 0 {
2023-10-10 10:00:14 +02:00
Ordering::Greater
2023-01-21 01:25:05 +01:00
} else {
2023-10-10 10:00:14 +02:00
Ordering::Less
2023-01-21 01:25:05 +01:00
}
}
Ordering::Greater => {
return if self.unix_seconds < 0 {
2023-10-10 10:00:14 +02:00
Ordering::Less
2023-01-21 01:25:05 +01:00
} else {
2023-10-10 10:00:14 +02:00
Ordering::Greater
2023-01-21 01:25:05 +01:00
}
}
Ordering::Equal => (),
}
2023-10-10 10:00:14 +02:00
Ordering::Equal
2023-01-21 01:25:05 +01:00
}
}
2024-02-27 15:59:04 +01:00
/// Difference between two UNIX timestamps. The [Duration] type can not contain negative durations,
/// so the sign information is supplied separately.
#[derive(Clone, Copy, PartialEq, Eq)]
pub struct StampDiff {
pub positive_duration: bool,
pub duration_absolute: Duration,
}
impl Sub for UnixTimestamp {
type Output = StampDiff;
fn sub(self, rhs: Self) -> Self::Output {
let difference = self.difference_in_millis(&rhs);
if difference < 0 {
StampDiff {
positive_duration: false,
duration_absolute: Duration::from_millis(-difference as u64),
}
} else {
StampDiff {
positive_duration: true,
duration_absolute: Duration::from_millis(difference as u64),
}
}
}
}
2023-01-21 13:17:38 +01:00
fn get_new_stamp_after_addition(
current_stamp: &UnixTimestamp,
duration: Duration,
) -> UnixTimestamp {
2024-02-27 15:59:04 +01:00
let mut new_subsec_millis = current_stamp.subsecond_millis() + duration.subsec_millis() as u16;
2023-01-21 13:17:38 +01:00
let mut new_unix_seconds = current_stamp.unix_seconds;
let mut increment_seconds = |value: u32| {
2023-01-21 13:17:38 +01:00
if new_unix_seconds < 0 {
new_unix_seconds = new_unix_seconds
.checked_sub(value.into())
2023-01-21 13:17:38 +01:00
.expect("new unix seconds would exceed i64::MIN");
} else {
new_unix_seconds = new_unix_seconds
.checked_add(value.into())
2023-01-21 13:17:38 +01:00
.expect("new unix seconds would exceed i64::MAX");
}
};
if new_subsec_millis >= 1000 {
new_subsec_millis -= 1000;
increment_seconds(1);
}
2023-01-22 12:53:39 +01:00
increment_seconds(
duration
.as_secs()
.try_into()
.expect("duration seconds exceeds u32::MAX"),
);
2023-01-21 13:17:38 +01:00
UnixTimestamp::const_new(new_unix_seconds, new_subsec_millis)
}
/// Please note that this operation will panic on the following conditions:
///
/// - 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].
2023-01-21 13:17:38 +01:00
impl AddAssign<Duration> for UnixTimestamp {
fn add_assign(&mut self, duration: Duration) {
*self = get_new_stamp_after_addition(self, duration);
}
}
/// Please note that this operation will panic for the following conditions:
///
/// - 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].
2023-01-21 13:17:38 +01:00
impl Add<Duration> for UnixTimestamp {
type Output = Self;
fn add(self, duration: Duration) -> Self::Output {
get_new_stamp_after_addition(&self, duration)
}
}
2023-01-21 14:28:29 +01:00
impl Add<Duration> for &UnixTimestamp {
type Output = UnixTimestamp;
fn add(self, duration: Duration) -> Self::Output {
get_new_stamp_after_addition(self, duration)
}
}
2022-12-08 15:22:19 +01:00
#[cfg(all(test, feature = "std"))]
mod tests {
2023-12-05 15:05:00 +01:00
use alloc::string::ToString;
use chrono::{Datelike, Timelike};
use std::format;
use super::{cuc::CucError, *};
2022-12-08 15:22:19 +01:00
#[test]
fn test_days_conversion() {
assert_eq!(unix_to_ccsds_days(DAYS_CCSDS_TO_UNIX.into()), 0);
assert_eq!(ccsds_to_unix_days(0), DAYS_CCSDS_TO_UNIX.into());
}
#[test]
fn test_get_current_time() {
let sec_floats = seconds_since_epoch();
assert!(sec_floats > 0.0);
}
2022-12-09 13:50:04 +01:00
2023-12-05 15:05:00 +01:00
#[test]
fn test_ms_of_day() {
let ms = ms_of_day(0.0);
assert_eq!(ms, 0);
let ms = ms_of_day(5.0);
assert_eq!(ms, 5000);
}
2022-12-09 13:50:04 +01:00
#[test]
fn test_ccsds_epoch() {
let now = SystemTime::now()
.duration_since(SystemTime::UNIX_EPOCH)
.unwrap();
let unix_epoch = now.as_secs();
2023-01-17 01:16:07 +01:00
let ccsds_epoch = unix_epoch_to_ccsds_epoch(now.as_secs() as i64) as u64;
2022-12-09 13:50:04 +01:00
assert!(ccsds_epoch > unix_epoch);
assert_eq!((ccsds_epoch - unix_epoch) % SECONDS_PER_DAY as u64, 0);
let days_diff = (ccsds_epoch - unix_epoch) / SECONDS_PER_DAY as u64;
assert_eq!(days_diff, -DAYS_CCSDS_TO_UNIX as u64);
}
2023-01-15 17:42:23 +01:00
#[test]
fn basic_unix_stamp_test() {
2023-01-15 21:30:31 +01:00
let stamp = UnixTimestamp::new_only_seconds(-200);
2023-01-15 17:42:23 +01:00
assert_eq!(stamp.unix_seconds, -200);
2024-02-27 15:59:04 +01:00
assert_eq!(stamp.subsecond_millis(), 0);
2023-01-15 21:30:31 +01:00
let stamp = UnixTimestamp::new_only_seconds(250);
2023-01-15 17:42:23 +01:00
assert_eq!(stamp.unix_seconds, 250);
2024-02-27 15:59:04 +01:00
assert_eq!(stamp.subsecond_millis(), 0);
2023-01-15 17:42:23 +01:00
}
#[test]
fn basic_float_unix_stamp_test() {
2023-01-15 21:30:31 +01:00
let stamp = UnixTimestamp::new(500, 600).unwrap();
2023-01-15 17:42:23 +01:00
assert_eq!(stamp.unix_seconds, 500);
2024-02-27 15:59:04 +01:00
let subsec_millis = stamp.subsecond_millis();
2023-01-15 17:42:23 +01:00
assert_eq!(subsec_millis, 600);
assert!((500.6 - stamp.unix_seconds_f64()).abs() < 0.0001);
}
2023-01-21 01:25:05 +01:00
#[test]
fn test_ord_larger() {
let stamp0 = UnixTimestamp::new_only_seconds(5);
let stamp1 = UnixTimestamp::new(5, 500).unwrap();
let stamp2 = UnixTimestamp::new_only_seconds(6);
assert!(stamp1 > stamp0);
assert!(stamp2 > stamp0);
assert!(stamp2 > stamp1);
}
#[test]
fn test_ord_smaller() {
let stamp0 = UnixTimestamp::new_only_seconds(5);
let stamp1 = UnixTimestamp::new(5, 500).unwrap();
let stamp2 = UnixTimestamp::new_only_seconds(6);
assert!(stamp0 < stamp1);
assert!(stamp0 < stamp2);
assert!(stamp1 < stamp2);
}
#[test]
fn test_ord_larger_neg_numbers() {
let stamp0 = UnixTimestamp::new_only_seconds(-5);
let stamp1 = UnixTimestamp::new(-5, 500).unwrap();
let stamp2 = UnixTimestamp::new_only_seconds(-6);
assert!(stamp0 > stamp1);
assert!(stamp0 > stamp2);
assert!(stamp1 > stamp2);
assert!(stamp1 >= stamp2);
assert!(stamp0 >= stamp1);
}
#[test]
fn test_ord_smaller_neg_numbers() {
let stamp0 = UnixTimestamp::new_only_seconds(-5);
let stamp1 = UnixTimestamp::new(-5, 500).unwrap();
let stamp2 = UnixTimestamp::new_only_seconds(-6);
assert!(stamp2 < stamp1);
assert!(stamp2 < stamp0);
assert!(stamp1 < stamp0);
assert!(stamp1 <= stamp0);
assert!(stamp2 <= stamp1);
}
2024-02-27 15:59:04 +01:00
#[allow(clippy::nonminimal_bool)]
2023-01-21 01:25:05 +01:00
#[test]
fn test_eq() {
let stamp0 = UnixTimestamp::new(5, 0).unwrap();
let stamp1 = UnixTimestamp::new_only_seconds(5);
assert_eq!(stamp0, stamp1);
assert!(stamp0 <= stamp1);
assert!(stamp0 >= stamp1);
assert!(!(stamp0 < stamp1));
assert!(!(stamp0 > stamp1));
}
2023-01-21 13:17:38 +01:00
#[test]
fn test_addition() {
let mut stamp0 = UnixTimestamp::new_only_seconds(1);
stamp0 += Duration::from_secs(5);
assert_eq!(stamp0.unix_seconds, 6);
2024-02-27 15:59:04 +01:00
assert_eq!(stamp0.subsecond_millis(), 0);
2023-01-21 13:17:38 +01:00
let stamp1 = stamp0 + Duration::from_millis(500);
assert_eq!(stamp1.unix_seconds, 6);
2024-02-27 15:59:04 +01:00
assert_eq!(stamp1.subsecond_millis(), 500);
2023-01-21 13:17:38 +01:00
}
2023-01-22 12:53:39 +01:00
#[test]
fn test_addition_on_ref() {
let stamp0 = &UnixTimestamp::new(20, 500).unwrap();
let stamp1 = stamp0 + Duration::from_millis(2500);
assert_eq!(stamp1.unix_seconds, 23);
2024-02-27 15:59:04 +01:00
assert_eq!(stamp1.subsecond_millis(), 0);
2023-01-22 12:53:39 +01:00
}
2023-12-05 15:05:00 +01:00
#[test]
fn test_as_dt() {
let stamp = UnixTimestamp::new_only_seconds(0);
let dt = stamp.as_date_time().unwrap();
assert_eq!(dt.year(), 1970);
assert_eq!(dt.month(), 1);
assert_eq!(dt.day(), 1);
assert_eq!(dt.hour(), 0);
assert_eq!(dt.minute(), 0);
assert_eq!(dt.second(), 0);
}
#[test]
fn test_from_now() {
let stamp_now = UnixTimestamp::from_now().unwrap();
let dt_now = stamp_now.as_date_time().unwrap();
assert!(dt_now.year() >= 2020);
}
2024-02-27 15:59:04 +01:00
#[test]
fn test_stamp_diff_positive_0() {
let stamp_later = UnixTimestamp::new(2, 0).unwrap();
let StampDiff {
positive_duration,
duration_absolute,
} = stamp_later - UnixTimestamp::new(1, 0).unwrap();
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).unwrap();
let stamp_earlier = UnixTimestamp::new(1, 900).unwrap();
let StampDiff {
positive_duration,
duration_absolute,
} = stamp_later - stamp_earlier;
assert!(positive_duration);
assert_eq!(duration_absolute, Duration::from_millis(1900));
}
#[test]
fn test_stamp_diff_negative() {
let stamp_later = UnixTimestamp::new(3, 800).unwrap();
let stamp_earlier = UnixTimestamp::new(1, 900).unwrap();
let StampDiff {
positive_duration,
duration_absolute,
} = stamp_earlier - stamp_later;
assert!(!positive_duration);
assert_eq!(duration_absolute, Duration::from_millis(1900));
}
2023-01-21 13:17:38 +01:00
#[test]
fn test_addition_spillover() {
let mut stamp0 = UnixTimestamp::new(1, 900).unwrap();
stamp0 += Duration::from_millis(100);
assert_eq!(stamp0.unix_seconds, 2);
2024-02-27 15:59:04 +01:00
assert_eq!(stamp0.subsecond_millis(), 0);
2023-01-21 13:17:38 +01:00
stamp0 += Duration::from_millis(1100);
assert_eq!(stamp0.unix_seconds, 3);
2024-02-27 15:59:04 +01:00
assert_eq!(stamp0.subsecond_millis(), 100);
2023-01-21 13:17:38 +01:00
}
2023-12-05 15:05:00 +01:00
#[test]
fn test_cuc_error_printout() {
let cuc_error = CucError::InvalidCounterWidth(12);
let stamp_error = TimestampError::from(cuc_error);
assert_eq!(stamp_error.to_string(), format!("cuc error: {cuc_error}"));
}
2022-12-08 15:22:19 +01:00
}