diff --git a/.idea/lox-space.iml b/.idea/lox-space.iml
index 5407fa9f..2ddf744f 100644
--- a/.idea/lox-space.iml
+++ b/.idea/lox-space.iml
@@ -13,6 +13,7 @@
+
diff --git a/Cargo.lock b/Cargo.lock
index 0f62b14d..a364f4df 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -328,9 +328,20 @@ dependencies = [
name = "lox-space"
version = "0.0.0"
dependencies = [
+ "lox-time",
"lox_core",
]
+[[package]]
+name = "lox-time"
+version = "0.1.0"
+dependencies = [
+ "num",
+ "proptest",
+ "rstest",
+ "thiserror",
+]
+
[[package]]
name = "lox_core"
version = "0.1.0"
@@ -338,6 +349,7 @@ dependencies = [
"divan",
"float_eq",
"glam",
+ "lox-time",
"num",
"proptest",
"rstest",
@@ -355,6 +367,7 @@ dependencies = [
name = "lox_py"
version = "0.1.0"
dependencies = [
+ "lox-time",
"lox_core",
"pyo3",
"thiserror",
diff --git a/Cargo.toml b/Cargo.toml
index e73037a1..1d5e2375 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -14,8 +14,13 @@ license = "MPL-2.0"
authors = ["Helge Eichhorn and the lox-space contributors"]
[workspace.dependencies]
+divan = "0.1.2"
+lox-time = { path = "./crates/lox-time" }
lox_core = { path = "./crates/lox_core" }
lox_io = { path = "./crates/lox_io" }
lox_py = { path = "./crates/lox_py" }
+num = "0.4.0"
+proptest = "1.1.0"
+rstest = "0.18.2"
thiserror = "1.0"
diff --git a/crates/lox-space/Cargo.toml b/crates/lox-space/Cargo.toml
index 9bd62682..efc202c2 100644
--- a/crates/lox-space/Cargo.toml
+++ b/crates/lox-space/Cargo.toml
@@ -9,3 +9,4 @@ rust-version.workspace = true
[dependencies]
lox_core.workspace = true
+lox-time.workspace = true
diff --git a/crates/lox-space/src/prelude.rs b/crates/lox-space/src/prelude.rs
index c8ddf9d8..ec45cdee 100644
--- a/crates/lox-space/src/prelude.rs
+++ b/crates/lox-space/src/prelude.rs
@@ -10,7 +10,7 @@ pub use lox_core::bodies::barycenters::*;
pub use lox_core::bodies::planets::*;
pub use lox_core::bodies::{Ellipsoid, PointMass, Spheroid, TriAxial};
-pub use lox_core::time::dates::*;
-pub use lox_core::time::epochs::*;
+pub use lox_time::dates::*;
+pub use lox_time::epochs::*;
pub use lox_core::two_body::{Cartesian, DVec3, Keplerian, TwoBody};
diff --git a/crates/lox-time/Cargo.toml b/crates/lox-time/Cargo.toml
new file mode 100644
index 00000000..2e9e23f4
--- /dev/null
+++ b/crates/lox-time/Cargo.toml
@@ -0,0 +1,15 @@
+[package]
+name = "lox-time"
+version = "0.1.0"
+rust-version.workspace = true
+edition.workspace = true
+license.workspace = true
+authors.workspace = true
+
+[dependencies]
+num.workspace = true
+thiserror.workspace = true
+
+[dev-dependencies]
+proptest.workspace = true
+rstest.workspace = true
diff --git a/crates/lox_core/src/time/constants.rs b/crates/lox-time/src/constants.rs
similarity index 100%
rename from crates/lox_core/src/time/constants.rs
rename to crates/lox-time/src/constants.rs
diff --git a/crates/lox_core/src/time/constants/f64.rs b/crates/lox-time/src/constants/f64.rs
similarity index 100%
rename from crates/lox_core/src/time/constants/f64.rs
rename to crates/lox-time/src/constants/f64.rs
diff --git a/crates/lox_core/src/time/constants/i64.rs b/crates/lox-time/src/constants/i64.rs
similarity index 100%
rename from crates/lox_core/src/time/constants/i64.rs
rename to crates/lox-time/src/constants/i64.rs
diff --git a/crates/lox_core/src/time/dates.rs b/crates/lox-time/src/dates.rs
similarity index 83%
rename from crates/lox_core/src/time/dates.rs
rename to crates/lox-time/src/dates.rs
index 88a790fe..b5fd05de 100644
--- a/crates/lox_core/src/time/dates.rs
+++ b/crates/lox-time/src/dates.rs
@@ -3,10 +3,10 @@
*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ * file, you can obtain one at http://mozilla.org/MPL/2.0/.
*/
-use crate::errors::LoxError;
+use crate::errors::LoxTimeError;
use num::ToPrimitive;
#[derive(Debug, Copy, Clone)]
@@ -44,15 +44,15 @@ impl Date {
self.day
}
- pub fn new(year: i64, month: i64, day: i64) -> Result {
+ pub fn new(year: i64, month: i64, day: i64) -> Result {
if !(1..=12).contains(&month) {
- Err(LoxError::InvalidDate(year, month, day))
+ Err(LoxTimeError::InvalidDate(year, month, day))
} else {
let calendar = get_calendar(year, month, day);
let check = Date::from_days(j2000(calendar, year, month, day))?;
if check.year() != year || check.month() != month || check.day() != day {
- Err(LoxError::InvalidDate(year, month, day))
+ Err(LoxTimeError::InvalidDate(year, month, day))
} else {
Ok(Date {
calendar,
@@ -64,7 +64,7 @@ impl Date {
}
}
- pub fn from_days(offset: i64) -> Result {
+ pub fn from_days(offset: i64) -> Result {
let calendar = if offset < LAST_JULIAN_DAY_J2K {
if offset > LAST_PROLEPTIC_JULIAN_DAY_J2K {
Calendar::Julian
@@ -108,9 +108,9 @@ pub struct Time {
}
impl Time {
- pub fn new(hour: i64, minute: i64, second: i64) -> Result {
+ pub fn new(hour: i64, minute: i64, second: i64) -> Result {
if !(0..24).contains(&hour) || !(0..60).contains(&minute) || !(0..61).contains(&second) {
- Err(LoxError::InvalidTime(hour, minute, second))
+ Err(LoxTimeError::InvalidTime(hour, minute, second))
} else {
Ok(Self {
hour,
@@ -151,9 +151,9 @@ impl Time {
self
}
- pub fn from_seconds(hour: i64, minute: i64, seconds: f64) -> Result {
+ pub fn from_seconds(hour: i64, minute: i64, seconds: f64) -> Result {
if !(0.0..61.0).contains(&seconds) {
- return Err(LoxError::InvalidSeconds(hour, minute, seconds));
+ return Err(LoxTimeError::InvalidSeconds(hour, minute, seconds));
}
let sub = split_seconds(seconds.fract()).unwrap();
let second = seconds.round().to_i64().unwrap();
@@ -253,9 +253,9 @@ fn find_month(day_in_year: i64, is_leap: bool) -> i64 {
}
}
-fn find_day(day_in_year: i64, month: i64, is_leap: bool) -> Result {
+fn find_day(day_in_year: i64, month: i64, is_leap: bool) -> Result {
if !is_leap && day_in_year > 365 {
- Err(LoxError::NonLeapYear)
+ Err(LoxTimeError::NonLeapYear)
} else {
let previous_days = if is_leap {
PREVIOUS_MONTH_END_DAY_LEAP
@@ -314,6 +314,7 @@ fn split_seconds(seconds: f64) -> Option<[i64; 6]> {
mod tests {
use super::*;
use proptest::prelude::*;
+ use rstest::rstest;
proptest! {
#[test]
@@ -380,4 +381,47 @@ mod tests {
assert!(split_seconds(2.0).is_none());
assert!(split_seconds(-0.2).is_none());
}
+
+ #[rstest]
+ #[case(-4713, 12, 31, -2451546)]
+ #[case(-4712, 1, 1, -2451545)]
+ #[case(0, 12, 31, -730122)]
+ #[case(1, 1, 1, -730121)]
+ #[case(1500, 2, 28, -182554)]
+ #[case(1500, 2, 29, -182553)]
+ #[case(1500, 3, 1, -182552)]
+ #[case(1582, 10, 4, -152385)]
+ #[case(1582, 10, 15, -152384)]
+ #[case(1600, 2, 28, -146039)]
+ #[case(1600, 2, 29, -146038)]
+ #[case(1600, 3, 1, -146037)]
+ #[case(1700, 2, 28, -109514)]
+ #[case(1700, 3, 1, -109513)]
+ #[case(1800, 2, 28, -72990)]
+ #[case(1800, 3, 1, -72989)]
+ #[case(1858, 11, 15, -51546)]
+ #[case(1858, 11, 16, -51545)]
+ #[case(1999, 12, 31, -1)]
+ #[case(2000, 1, 1, 0)]
+ #[case(2000, 2, 28, 58)]
+ #[case(2000, 2, 29, 59)]
+ #[case(2000, 3, 1, 60)]
+ fn test_dates(#[case] year: i64, #[case] month: i64, #[case] day: i64, #[case] exp: i64) {
+ let date = Date::new(year, month, day).expect("date should be valid");
+ assert_eq!(exp, date.j2000())
+ }
+
+ #[test]
+ fn test_illegal_dates() {
+ assert!(Date::new(2018, 2, 29).is_err());
+ assert!(Date::new(2018, 0, 1).is_err());
+ assert!(Date::new(2018, 13, 1).is_err());
+ }
+
+ #[test]
+ fn test_illegal_times() {
+ assert!(Time::from_seconds(24, 59, 59.0).is_err());
+ assert!(Time::from_seconds(23, 60, 59.0).is_err());
+ assert!(Time::from_seconds(23, 59, 61.0).is_err());
+ }
}
diff --git a/crates/lox_core/src/time/epochs.rs b/crates/lox-time/src/epochs.rs
similarity index 94%
rename from crates/lox_core/src/time/epochs.rs
rename to crates/lox-time/src/epochs.rs
index 342ae24c..eb3a14f5 100644
--- a/crates/lox_core/src/time/epochs.rs
+++ b/crates/lox-time/src/epochs.rs
@@ -9,11 +9,11 @@
use std::fmt;
use std::fmt::Formatter;
-use crate::time::constants;
-use crate::time::constants::i64::{SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE};
use num::ToPrimitive;
-use crate::time::dates::{Date, DateTime, Time};
+use crate::constants;
+use crate::constants::i64::{SECONDS_PER_DAY, SECONDS_PER_HOUR, SECONDS_PER_MINUTE};
+use crate::dates::{Date, DateTime, Time};
#[derive(Debug, Copy, Clone)]
pub enum TimeScale {
diff --git a/crates/lox-time/src/errors.rs b/crates/lox-time/src/errors.rs
new file mode 100644
index 00000000..a746731b
--- /dev/null
+++ b/crates/lox-time/src/errors.rs
@@ -0,0 +1,21 @@
+/*
+ * Copyright (c) 2023. Helge Eichhorn and the LOX contributors
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, you can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+
+use thiserror::Error;
+
+#[derive(Error, Debug)]
+pub enum LoxTimeError {
+ #[error("invalid date `{0}-{1}-{2}`")]
+ InvalidDate(i64, i64, i64),
+ #[error("invalid time `{0}:{1}:{2}`")]
+ InvalidTime(i64, i64, i64),
+ #[error("invalid time `{0}:{1}:{2}`")]
+ InvalidSeconds(i64, i64, f64),
+ #[error("day of year cannot be 366 for a non-leap year")]
+ NonLeapYear,
+}
diff --git a/crates/lox_core/src/time.rs b/crates/lox-time/src/lib.rs
similarity index 95%
rename from crates/lox_core/src/time.rs
rename to crates/lox-time/src/lib.rs
index 954a7ffc..1aa2a301 100644
--- a/crates/lox_core/src/time.rs
+++ b/crates/lox-time/src/lib.rs
@@ -9,3 +9,4 @@
pub mod constants;
pub mod dates;
pub mod epochs;
+pub mod errors;
diff --git a/crates/lox_core/Cargo.toml b/crates/lox_core/Cargo.toml
index b90e4a25..da99cec6 100644
--- a/crates/lox_core/Cargo.toml
+++ b/crates/lox_core/Cargo.toml
@@ -6,8 +6,9 @@ edition = "2021"
[dependencies]
float_eq = "1.0.1"
glam = "0.24.2"
-num = "0.4.0"
+num.workspace = true
thiserror.workspace = true
+lox-time.workspace = true
[dev-dependencies]
proptest = "1.1.0"
diff --git a/crates/lox_core/src/bodies.rs b/crates/lox_core/src/bodies.rs
index c83d2b26..e347cf21 100644
--- a/crates/lox_core/src/bodies.rs
+++ b/crates/lox_core/src/bodies.rs
@@ -8,7 +8,7 @@
use std::f64::consts::PI;
-use crate::time::constants::f64::{SECONDS_PER_DAY, SECONDS_PER_JULIAN_CENTURY};
+use lox_time::constants::f64::{SECONDS_PER_DAY, SECONDS_PER_JULIAN_CENTURY};
pub mod barycenters;
pub mod minor;
diff --git a/crates/lox_core/src/errors.rs b/crates/lox_core/src/errors.rs
index 7ce753fb..f4bc9fec 100644
--- a/crates/lox_core/src/errors.rs
+++ b/crates/lox_core/src/errors.rs
@@ -6,16 +6,11 @@
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
*/
+use lox_time::errors::LoxTimeError;
use thiserror::Error;
#[derive(Error, Debug)]
pub enum LoxError {
- #[error("invalid date `{0}-{1}-{2}`")]
- InvalidDate(i64, i64, i64),
- #[error("invalid time `{0}:{1}:{2}`")]
- InvalidTime(i64, i64, i64),
- #[error("invalid time `{0}:{1}:{2}`")]
- InvalidSeconds(i64, i64, f64),
- #[error("day of year cannot be 366 for a non-leap year")]
- NonLeapYear,
+ #[error(transparent)]
+ LoxTimeError(#[from] LoxTimeError),
}
diff --git a/crates/lox_core/src/lib.rs b/crates/lox_core/src/lib.rs
index 97efaf5a..7cfdd0df 100644
--- a/crates/lox_core/src/lib.rs
+++ b/crates/lox_core/src/lib.rs
@@ -10,5 +10,4 @@ pub mod bodies;
pub mod communications;
pub mod errors;
pub mod frames;
-pub mod time;
pub mod two_body;
diff --git a/crates/lox_core/src/two_body.rs b/crates/lox_core/src/two_body.rs
index 0f926933..7f7586f6 100644
--- a/crates/lox_core/src/two_body.rs
+++ b/crates/lox_core/src/two_body.rs
@@ -9,8 +9,8 @@
pub use glam::DVec3;
use crate::bodies::{gravitational_parameter, PointMass};
-use crate::time::epochs::Epoch;
use crate::two_body::elements::{cartesian_to_keplerian, keplerian_to_cartesian};
+use lox_time::epochs::Epoch;
pub mod elements;
@@ -225,8 +225,8 @@ mod tests {
use std::ops::Mul;
use crate::bodies::planets::Earth;
- use crate::time::dates::{Date, Time};
- use crate::time::epochs::TimeScale;
+ use lox_time::dates::{Date, Time};
+ use lox_time::epochs::TimeScale;
use super::*;
diff --git a/crates/lox_core/tests/dates.rs b/crates/lox_core/tests/dates.rs
deleted file mode 100644
index 6c1e2a6a..00000000
--- a/crates/lox_core/tests/dates.rs
+++ /dev/null
@@ -1,53 +0,0 @@
-/*
- * Copyright (c) 2023. Helge Eichhorn and the LOX contributors
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- */
-
-use lox_core::time::dates::{Date, Time};
-use rstest::rstest;
-
-#[rstest]
-#[case(-4713, 12, 31, -2451546)]
-#[case(-4712, 1, 1, -2451545)]
-#[case(0, 12, 31, -730122)]
-#[case(1, 1, 1, -730121)]
-#[case(1500, 2, 28, -182554)]
-#[case(1500, 2, 29, -182553)]
-#[case(1500, 3, 1, -182552)]
-#[case(1582, 10, 4, -152385)]
-#[case(1582, 10, 15, -152384)]
-#[case(1600, 2, 28, -146039)]
-#[case(1600, 2, 29, -146038)]
-#[case(1600, 3, 1, -146037)]
-#[case(1700, 2, 28, -109514)]
-#[case(1700, 3, 1, -109513)]
-#[case(1800, 2, 28, -72990)]
-#[case(1800, 3, 1, -72989)]
-#[case(1858, 11, 15, -51546)]
-#[case(1858, 11, 16, -51545)]
-#[case(1999, 12, 31, -1)]
-#[case(2000, 1, 1, 0)]
-#[case(2000, 2, 28, 58)]
-#[case(2000, 2, 29, 59)]
-#[case(2000, 3, 1, 60)]
-fn test_dates(#[case] year: i64, #[case] month: i64, #[case] day: i64, #[case] exp: i64) {
- let date = Date::new(year, month, day).expect("date should be valid");
- assert_eq!(exp, date.j2000())
-}
-
-#[test]
-fn test_illegal_dates() {
- assert!(Date::new(2018, 2, 29).is_err());
- assert!(Date::new(2018, 0, 1).is_err());
- assert!(Date::new(2018, 13, 1).is_err());
-}
-
-#[test]
-fn test_illegal_times() {
- assert!(Time::from_seconds(24, 59, 59.0).is_err());
- assert!(Time::from_seconds(23, 60, 59.0).is_err());
- assert!(Time::from_seconds(23, 59, 61.0).is_err());
-}
diff --git a/crates/lox_py/Cargo.toml b/crates/lox_py/Cargo.toml
index 87d8ab13..7716f535 100644
--- a/crates/lox_py/Cargo.toml
+++ b/crates/lox_py/Cargo.toml
@@ -14,4 +14,5 @@ crate-type = ["cdylib"]
[dependencies]
pyo3 = "0.20.0"
lox_core.workspace = true
+lox-time.workspace = true
thiserror.workspace = true
diff --git a/crates/lox_py/src/lib.rs b/crates/lox_py/src/lib.rs
index 538dec79..d5442aa9 100644
--- a/crates/lox_py/src/lib.rs
+++ b/crates/lox_py/src/lib.rs
@@ -11,9 +11,10 @@ use pyo3::prelude::*;
use thiserror::Error;
use lox_core::errors::LoxError;
-use lox_core::time::dates::{Date, Time};
-use lox_core::time::epochs::Epoch;
-use lox_core::time::epochs::TimeScale;
+use lox_time::dates::{Date, Time};
+use lox_time::epochs::Epoch;
+use lox_time::epochs::TimeScale;
+use lox_time::errors::LoxTimeError;
#[derive(Error, Debug)]
pub enum LoxPyError {
@@ -22,6 +23,8 @@ pub enum LoxPyError {
#[error(transparent)]
LoxError(#[from] LoxError),
#[error(transparent)]
+ LoxTimeError(#[from] LoxTimeError),
+ #[error(transparent)]
PyError(#[from] PyErr),
}
@@ -30,6 +33,7 @@ impl From for PyErr {
match value {
LoxPyError::InvalidTimeScale(_) => PyValueError::new_err(value.to_string()),
LoxPyError::LoxError(value) => PyValueError::new_err(value.to_string()),
+ LoxPyError::LoxTimeError(value) => PyValueError::new_err(value.to_string()),
LoxPyError::PyError(value) => value,
}
}