Skip to main content

deep_time/
lib.rs

1/*!
2# deep-time
3
4A fully featured, high performance, no_std and no alloc Rust date and
5time library with attosecond precision that provides astronomical and
6civil timekeeping.
7
8Functionality:
9<https://github.com/ragardner/deep-time#overview>
10
11Readme example:
12<https://github.com/ragardner/deep-time#examples>
13
14Additional examples:
15<https://github.com/ragardner/deep-time#additional-examples>
16
17The library's central time type is [`Dt`], a 32 byte struct that holds:
18
19- An `i128` attoseconds count
20- A `scale` [`Scale`] field for its current time scale
21- A `target` [`Scale`] field for the time scale the object came from,
22  and/or which time scale it should be converted to in various output
23  functions.
24
25[`Dt`] can act as an instant or duration.
26
27While [`Dt`] can hold attoseconds counts from any epoch (start time),
28the library's epoch for most functionality is 2000-01-01 noon on the
29TAI time scale.
30
31```
32use deep_time::{Dt, Scale, from_ymd};
33
34let dt = from_ymd!(2000, 1, 1; 12, on=Scale::TAI);
35assert_eq!(dt, Dt::ZERO);
36
37let dt = Dt::from_str_iso("2000-01-01 12:00 TAI").unwrap();
38assert_eq!(dt, Dt::ZERO);
39```
40
41[`Dt`] handles massive datetimes and always with attosecond
42resolution:
43
44```
45use deep_time::{Dt, Lang, Scale, from_ymd};
46
47let mut dt = Dt::from_str_iso("292000000000-1-1").unwrap();
48dt = dt.add_days(4);
49let s = dt.to_str_lite("%Y-%m-%dT%H:%M:%S %L", Lang::En).unwrap();
50
51assert_eq!(s.as_str(), "292000000000-01-05T00:00:00 UTC");
52
53// negatives too
54let dt = from_ymd!(-5000, 1, 1; 18, on=Scale::TAI);
55assert_eq!(dt, Dt::parse("-5000-01-01T18:00:00 TAI").unwrap());
56
57assert_eq!(dt.to_jd_f(), -105151.75);
58assert_eq!(dt.to_mjd_f(), -2505152.25);
59```
60
61Once you have a [`Dt`] you can change its time scale:
62
63```
64use deep_time::{Dt, Scale, from_jd_f};
65
66let dt = from_jd_f!(2451545.0);
67assert_eq!(dt.scale, Scale::TAI);
68
69// leap seconds have been subtracted when going from TAI -> UTC
70let utc = dt.to(Scale::UTC);
71assert_eq!(utc.to_sec_f(), -32.0);
72```
73
74This crate has no default features.
75
76The minimum Rust version is `1.90` and minimum Rust edition is `2024`.
77
78This is mainly due to some `const` functionality that only became stable
79recently.
80
81To add deep-time to your Rust project with the parse and timezone
82features, go to your project folder and run this terminal command:
83
84```text
85cargo add deep-time --features "parse,jiff-tz"
86```
87
88List of features you can enable:
89<https://github.com/ragardner/deep-time#feature-flags>
90*/
91
92#![forbid(unsafe_code)]
93#![cfg_attr(test, allow(clippy::all))]
94#![cfg_attr(not(feature = "std"), no_std)]
95
96#[cfg(feature = "alloc")]
97extern crate alloc;
98#[cfg(feature = "std")]
99extern crate std;
100
101// ──────────────────────────────────────────────────────────────
102// Optional panic handler (opt-in via feature)
103// ──────────────────────────────────────────────────────────────
104#[cfg(all(feature = "panic-handler", not(feature = "std"), not(test)))]
105use core::panic::PanicInfo;
106
107#[cfg(all(feature = "panic-handler", not(feature = "std"), not(test)))]
108#[panic_handler]
109fn panic(_info: &PanicInfo) -> ! {
110    // Uses spin_loop() for better power characteristics than plain loop{}
111    loop {
112        core::hint::spin_loop();
113    }
114}
115
116/// Alias for f64, maybe upgrade one day
117pub type Real = f64;
118
119/// Convert a number to the crates [`Real`] type (f64).
120///
121/// Equivalent to `n as f64`.
122#[macro_export]
123macro_rules! f {
124    ($x:expr) => {
125        $x as $crate::Real
126    };
127}
128
129/// Safe Euclidean division.
130/// Returns `default` if `rhs == 0` or if `lhs == i128::MIN && rhs == -1`.
131macro_rules! safe_div_euc {
132    ($lhs:expr, $rhs:expr, $default:expr) => {{
133        match ($lhs).checked_div_euclid($rhs) {
134            Some(q) => q,
135            None => $default,
136        }
137    }};
138}
139
140/// Safe Euclidean remainder.
141/// Returns `$default` if `rhs == 0` or if `lhs == Self::MIN && rhs == -1`.
142macro_rules! safe_rem_euc {
143    ($lhs:expr, $rhs:expr, $default:expr) => {{
144        match ($lhs).checked_rem_euclid($rhs) {
145            Some(r) => r,
146            None => $default,
147        }
148    }};
149}
150
151/// Turns a list of string literals into an array of `&'static [u8]`.
152macro_rules! byte_arrays {
153    ( $( $s:literal ),+ $(,)? ) => {
154        [ $( $s.as_bytes() ),+ ]
155    };
156}
157
158// _________________________________________
159// MOD
160// _________________________________________
161mod an_err;
162mod dt;
163mod error;
164mod lite_str;
165mod locale;
166mod scale;
167mod strtime;
168mod time_range;
169mod ymdhms;
170
171#[cfg(feature = "parse")]
172mod alloc_parse;
173
174#[cfg(feature = "physics")]
175mod physics;
176
177// _________________________________________
178// PUB MOD
179// _________________________________________
180pub mod civil_parts;
181pub mod consts;
182pub mod macros;
183pub mod math;
184pub mod tz;
185pub mod utc;
186
187#[cfg(feature = "eop")]
188pub mod eop;
189
190#[cfg(feature = "sidereal")]
191pub mod sidereal;
192
193// _________________________________________
194// CRATE USE
195// _________________________________________
196pub(crate) use civil_parts::*;
197pub(crate) use consts::*;
198pub(crate) use locale::*;
199#[allow(unused_imports)]
200pub(crate) use math::{
201    atan2::atan2,
202    cos::cos,
203    div::rem_euclid_f,
204    floor::floor_f,
205    log::log,
206    sin::sin,
207    sqrt::{hypot, sqrt},
208};
209pub(crate) use strtime::*;
210
211#[cfg(feature = "parse")]
212pub(crate) use alloc_parse::{
213    alloc_consts::*, date::*, date_classification::*, duration::*, helpers::*, parse_date::*,
214    types::*,
215};
216
217#[cfg(feature = "parse")]
218pub(crate) use locale::{lang_data::*, lang_map::*};
219
220// _________________________________________
221// PUB USE
222// _________________________________________
223pub use an_err::AnErr;
224pub use dt::Dt;
225pub use dt::lunar;
226pub use dt::numbers_traits::{TraitsAttos, TraitsTime};
227pub use error::{DtErr, DtErrKind};
228pub use lite_str::LiteStr;
229pub use locale::Lang;
230pub use scale::Scale;
231pub use strtime::StrPTimeFmt;
232pub use time_range::{Every, TimeRange};
233pub use ymdhms::YmdHms;
234
235#[cfg(feature = "tdb-hi")]
236pub use dt::tdb_hi;
237
238#[cfg(feature = "parse")]
239pub use alloc_parse::types::{Mode, Order, ParseCfg};
240
241#[cfg(feature = "mars")]
242pub use dt::mars;
243
244#[cfg(feature = "sidereal")]
245#[doc(hidden)]
246pub use sidereal::Sidereal;
247
248#[cfg(feature = "physics")]
249pub use physics::{
250    drift::Drift, observer::Observer, position::Position, spacetime::Spacetime, velocity::Velocity,
251};