Skip to content

Commit

Permalink
Draft WallClock and Dateable implementations
Browse files Browse the repository at this point in the history
  • Loading branch information
AngusGMorrison committed Jan 16, 2024
1 parent ad7c00f commit e77c922
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 92 deletions.
2 changes: 1 addition & 1 deletion crates/lox_core/src/earth/nutation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ fn point1_microarcsec_to_rad(p1_uas: Point1Microarcsec) -> Radians {
mod tests {
use float_eq::assert_float_eq;

use crate::time::continuous::TimeScale;
use crate::time::scales::TimeScale;

use super::*;

Expand Down
76 changes: 76 additions & 0 deletions crates/lox_core/src/time.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,85 @@
* file, you can obtain one at https://mozilla.org/MPL/2.0/.
*/

use crate::errors::LoxError;
use crate::time::dates::Date;
use crate::time::scales::TimeScale;
use std::fmt::Display;

pub mod constants;
pub mod continuous;
pub mod dates;
pub mod intervals;
pub mod leap_seconds;
pub mod scales;
pub mod utc;

/// `WallClock` is the trait by which high-precision time representations expose human-readable time components.
///
/// The components returned by a `WallClock` must be interpreted in terms of its [TimeScale]. For example, a UTC
/// `WallClock` will have a `second` component of 60 during a leap second.
pub trait WallClock {
fn scale(&self) -> TimeScale;
fn hour(&self) -> u8;
fn minute(&self) -> u8;
fn second(&self) -> u8;
fn millisecond(&self) -> Thousandths;
fn microsecond(&self) -> Thousandths;
fn nanosecond(&self) -> Thousandths;
fn picosecond(&self) -> Thousandths;
fn femtosecond(&self) -> Thousandths;
fn attosecond(&self) -> Thousandths;
}

/// Newtype wrapper for thousandths of an SI-prefixed subsecond (milli, micro, nano, etc.).
#[repr(transparent)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Thousandths(u16);

impl Thousandths {
pub fn new(thousandths: u16) -> Result<Self, LoxError> {
if !(0..1000).contains(&thousandths) {
Err(LoxError::InvalidThousandths(thousandths))
} else {
Ok(Self(thousandths))
}
}
}

impl Display for Thousandths {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:03}", self.0)
}
}

impl TryFrom<u16> for Thousandths {
type Error = LoxError;

fn try_from(thousandths: u16) -> Result<Self, Self::Error> {
Self::new(thousandths)
}
}

#[allow(clippy::from_over_into)] // the Into conversion is infallible, but From is not
impl Into<u64> for Thousandths {
fn into(self) -> u64 {
self.0 as u64
}
}

#[cfg(test)]
mod thousandths_tests {
use crate::time::Thousandths;

#[test]
fn test_new_valid() {
assert!(Thousandths::new(0).is_ok());
assert!(Thousandths::new(999).is_ok());
}

#[test]
fn test_new_invalid() {
assert!(Thousandths::new(1000).is_err());
assert!(Thousandths::new(1001).is_err());
}
}
91 changes: 55 additions & 36 deletions crates/lox_core/src/time/continuous.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,36 +12,13 @@ use std::ops::{Add, Sub};

use num::ToPrimitive;

use crate::time::constants;
use crate::time::constants::i64::{SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE};
use crate::time::constants::u64::ATTOSECONDS_PER_SECOND;
use crate::time::dates::Calendar::ProlepticJulian;
use crate::time::dates::Date;
use crate::time::scales::TimeScale;
use crate::time::utc::{UTCDateTime, UTC};

/// The continuous time scales supported by Lox.
#[derive(Debug, Copy, Clone)]
pub enum TimeScale {
TAI,
TCB,
TCG,
TDB,
TT,
UT1,
}

impl fmt::Display for TimeScale {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
TimeScale::TAI => write!(f, "TAI"),
TimeScale::TCB => write!(f, "TCB"),
TimeScale::TCG => write!(f, "TCG"),
TimeScale::TDB => write!(f, "TDB"),
TimeScale::TT => write!(f, "TT"),
TimeScale::UT1 => write!(f, "UT1"),
}
}
}
use crate::time::{constants, Thousandths, WallClock};

/// `ContinuousTime` is the base time representation for time scales without leap seconds. It is measured relative to an
/// arbitrary epoch. `RawTime::default()` represents the epoch itself.
Expand Down Expand Up @@ -97,6 +74,11 @@ pub struct Delta {
attoseconds: u64,
}

/// Dateable continuous time formats can report their date in their respective calendar.
pub trait Dateable {
fn date(&self) -> Date;
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct TAI(ContinuousTime);

Expand All @@ -106,6 +88,54 @@ impl TAI {
}
}

impl Dateable for TAI {
fn date(&self) -> Date {
todo!()
}
}

impl WallClock for TAI {
fn scale(&self) -> TimeScale {
TimeScale::TAI
}

fn hour(&self) -> u8 {
todo!()
}

fn minute(&self) -> u8 {
todo!()
}

fn second(&self) -> u8 {
todo!()
}

fn millisecond(&self) -> Thousandths {
todo!()
}

fn microsecond(&self) -> Thousandths {
todo!()
}

fn nanosecond(&self) -> Thousandths {
todo!()
}

fn picosecond(&self) -> Thousandths {
todo!()
}

fn femtosecond(&self) -> Thousandths {
todo!()
}

fn attosecond(&self) -> Thousandths {
todo!()
}
}

#[derive(Debug, Copy, Clone, Eq, PartialEq)]
pub struct TCB(ContinuousTime);

Expand Down Expand Up @@ -164,17 +194,6 @@ impl Time {
Self::from_date_and_utc_timestamp(scale, first_proleptic_day, midday)
}

fn from_raw(scale: TimeScale, raw_time: ContinuousTime) -> Self {
match scale {
TimeScale::TAI => Time::TAI(TAI(raw_time)),
TimeScale::TCB => Time::TCB(TCB(raw_time)),
TimeScale::TCG => Time::TCG(TCG(raw_time)),
TimeScale::TDB => Time::TDB(TDB(raw_time)),
TimeScale::TT => Time::TT(TT(raw_time)),
TimeScale::UT1 => Time::UT1(UT1(raw_time)),
}
}

fn raw(&self) -> ContinuousTime {
match self {
Time::TAI(tai) => tai.0,
Expand Down
3 changes: 2 additions & 1 deletion crates/lox_core/src/time/intervals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,10 @@ pub type UT1DaysSinceJ2000 = f64;
mod epoch_tests {
use float_eq::assert_float_eq;

use crate::time::continuous::{Time, TimeScale};
use crate::time::continuous::Time;
use crate::time::dates::Calendar::Gregorian;
use crate::time::dates::Date;
use crate::time::scales::TimeScale;
use crate::time::utc::UTC;

use super::tdb_julian_centuries_since_j2000;
Expand Down
28 changes: 28 additions & 0 deletions crates/lox_core/src/time/scales.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
use std::fmt;
use std::fmt::Formatter;

/// The time scales supported by Lox.
#[derive(Debug, Copy, Clone)]
pub enum TimeScale {
TAI,
TCB,
TCG,
TDB,
TT,
UT1,
UTC,
}

impl fmt::Display for TimeScale {
fn fmt(&self, f: &mut Formatter<'_>) -> fmt::Result {
match self {
TimeScale::TAI => write!(f, "TAI"),
TimeScale::TCB => write!(f, "TCB"),
TimeScale::TCG => write!(f, "TCG"),
TimeScale::TDB => write!(f, "TDB"),
TimeScale::TT => write!(f, "TT"),
TimeScale::UT1 => write!(f, "UT1"),
TimeScale::UTC => write!(f, "UTC"),
}
}
}
97 changes: 44 additions & 53 deletions crates/lox_core/src/time/utc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,62 +4,11 @@ use crate::time::constants::u64::{
ATTOSECONDS_PER_NANOSECOND, ATTOSECONDS_PER_PICOSECOND,
};
use crate::time::dates::Date;
use crate::time::scales::TimeScale;
use crate::time::{Thousandths, WallClock};
use num::ToPrimitive;
use std::fmt::Display;

/// Newtype wrapper for thousandths of an SI-prefixed subsecond (milli, micro, nano, etc.).
#[repr(transparent)]
#[derive(Debug, Default, Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
pub struct Thousandths(u16);

impl Thousandths {
pub fn new(thousandths: u16) -> Result<Self, LoxError> {
if !(0..1000).contains(&thousandths) {
Err(LoxError::InvalidThousandths(thousandths))
} else {
Ok(Self(thousandths))
}
}
}

impl Display for Thousandths {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{:03}", self.0)
}
}

impl TryFrom<u16> for Thousandths {
type Error = LoxError;

fn try_from(thousandths: u16) -> Result<Self, Self::Error> {
Self::new(thousandths)
}
}

#[allow(clippy::from_over_into)] // the Into conversion is infallible, but From is not
impl Into<u64> for Thousandths {
fn into(self) -> u64 {
self.0 as u64
}
}

#[cfg(test)]
mod thousandths_tests {
use crate::time::utc::Thousandths;

#[test]
fn test_new_valid() {
assert!(Thousandths::new(0).is_ok());
assert!(Thousandths::new(999).is_ok());
}

#[test]
fn test_new_invalid() {
assert!(Thousandths::new(1000).is_err());
assert!(Thousandths::new(1001).is_err());
}
}

/// A UTC timestamp with additional support for fractional seconds represented with attosecond
/// precision.
///
Expand Down Expand Up @@ -159,6 +108,48 @@ impl Display for UTC {
}
}

impl WallClock for UTC {
fn scale(&self) -> TimeScale {
TimeScale::UTC
}

fn hour(&self) -> u8 {
self.hour
}

fn minute(&self) -> u8 {
self.minute
}

fn second(&self) -> u8 {
self.second
}

fn millisecond(&self) -> Thousandths {
self.milli
}

fn microsecond(&self) -> Thousandths {
self.micro
}

fn nanosecond(&self) -> Thousandths {
self.nano
}

fn picosecond(&self) -> Thousandths {
self.pico
}

fn femtosecond(&self) -> Thousandths {
self.femto
}

fn attosecond(&self) -> Thousandths {
self.atto
}
}

/// Split a floating-point second into SI-prefixed integer parts.
fn split_seconds(seconds: f64) -> Option<[i64; 6]> {
if !(0.0..1.0).contains(&seconds) {
Expand Down
2 changes: 1 addition & 1 deletion crates/lox_core/src/two_body.rs
Original file line number Diff line number Diff line change
Expand Up @@ -226,8 +226,8 @@ mod tests {
use float_eq::assert_float_eq;

use crate::bodies::Earth;
use crate::time::continuous::TimeScale;
use crate::time::dates::Date;
use crate::time::scales::TimeScale;
use crate::time::utc::UTC;

use super::*;
Expand Down

0 comments on commit e77c922

Please sign in to comment.