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::macros::from_ymd;
33use deep_time::{Dt, Scale};
34
35let dt = from_ymd!(2000, 1, 1; 12, on=Scale::TAI);
36assert_eq!(dt, Dt::ZERO);
37
38let dt = Dt::from_str_iso("2000-01-01 12:00 TAI").unwrap();
39assert_eq!(dt, Dt::ZERO);
40```
41
42[`Dt`] handles massive datetimes and always with attosecond
43resolution:
44
45```
46use deep_time::macros::from_ymd;
47use deep_time::{Dt, Lang, Scale};
48
49let mut dt = Dt::from_str_iso("292000000000-1-1").unwrap();
50dt = dt.add_days(4);
51let s = dt.to_str_b("%Y-%m-%dT%H:%M:%S %L", Lang::En).unwrap();
52
53assert_eq!(s.as_str(), "292000000000-01-05T00:00:00 UTC");
54
55// negatives too
56let dt = from_ymd!(-5000, 1, 1; 18, on=Scale::TAI);
57assert_eq!(dt, Dt::parse("-5000-01-01T18:00:00 TAI").unwrap());
58
59assert_eq!(dt.to_jd_f(), -105151.75);
60assert_eq!(dt.to_mjd_f(), -2505152.25);
61```
62
63Once you have a [`Dt`] you can change its time scale:
64
65```
66use deep_time::macros::from_jd_f;
67use deep_time::{Dt, Scale};
68
69let dt = from_jd_f!(2451545.0);
70assert_eq!(dt.scale, Scale::TAI);
71
72// leap seconds have been subtracted when going from TAI -> UTC
73let utc = dt.to(Scale::UTC);
74assert_eq!(utc.to_sec_f(), -32.0);
75```
76
77This crate has no default features.
78
79The minimum Rust version is `1.90` and minimum Rust edition is `2024`.
80
81This is mainly due to some `const` functionality that only became stable
82recently.
83
84To add deep-time to your Rust project with the parse and timezone
85features, go to your project folder and run this terminal command:
86
87```text
88cargo add deep-time --features "parse,jiff-tz"
89```
90
91List of features you can enable:
92<https://github.com/ragardner/deep-time#feature-flags>
93*/
94
95#![forbid(unsafe_code)]
96#![deny(missing_docs)]
97#![cfg_attr(test, allow(clippy::all))]
98#![cfg_attr(not(feature = "std"), no_std)]
99
100#[cfg(feature = "alloc")]
101extern crate alloc;
102#[cfg(feature = "std")]
103extern crate std;
104
105/*
106uncomment this fn and run
107cargo test --release --features "parse"
108
109if it builds then std is being silently pulled in
110
111update to use something that requires std if
112.round() can work without std in the future
113*/
114
115// #[allow(dead_code)]
116// fn check_if_std_silently_pulled_in() {
117//     let x: f64 = 0.5;
118//     let _: f64 = x.round();
119// }
120
121// ──────────────────────────────────────────────────────────────
122// Optional panic handler (opt-in via feature)
123// ──────────────────────────────────────────────────────────────
124#[cfg(all(feature = "panic-handler", not(feature = "std"), not(test)))]
125use core::panic::PanicInfo;
126
127#[cfg(all(feature = "panic-handler", not(feature = "std"), not(test)))]
128#[panic_handler]
129fn panic(_info: &PanicInfo) -> ! {
130    // Uses spin_loop() for better power characteristics than plain loop{}
131    loop {
132        core::hint::spin_loop();
133    }
134}
135
136/// Alias for f64, maybe upgrade one day
137pub type Real = f64;
138
139/// Convert a number to the crates [`Real`] type (f64).
140///
141/// Equivalent to `n as f64`.
142#[macro_export]
143macro_rules! f {
144    ($x:expr) => {
145        $x as $crate::Real
146    };
147}
148
149/// Safe Euclidean division.
150/// Returns `default` if `rhs == 0` or if `lhs == i128::MIN && rhs == -1`.
151macro_rules! safe_div_euc {
152    ($lhs:expr, $rhs:expr, $default:expr) => {{
153        match ($lhs).checked_div_euclid($rhs) {
154            Some(q) => q,
155            None => $default,
156        }
157    }};
158}
159
160/// Safe Euclidean remainder.
161/// Returns `$default` if `rhs == 0` or if `lhs == Self::MIN && rhs == -1`.
162macro_rules! safe_rem_euc {
163    ($lhs:expr, $rhs:expr, $default:expr) => {{
164        match ($lhs).checked_rem_euclid($rhs) {
165            Some(r) => r,
166            None => $default,
167        }
168    }};
169}
170
171/// Turns a list of string literals into an array of `&'static [u8]`.
172macro_rules! byte_arrays {
173    ( $( $s:literal ),+ $(,)? ) => {
174        [ $( $s.as_bytes() ),+ ]
175    };
176}
177
178// _________________________________________
179// MOD
180// _________________________________________
181mod an_err;
182mod buf_str;
183mod dt;
184mod error;
185mod locale;
186mod scale;
187mod strtime;
188mod time_range;
189mod ymdhms;
190
191#[cfg(feature = "parse")]
192mod alloc_parse;
193
194#[cfg(feature = "physics")]
195pub mod physics;
196
197// _________________________________________
198// PUB MOD
199// _________________________________________
200pub mod civil_parts;
201pub mod consts;
202pub mod macros;
203pub mod math;
204pub mod tz;
205pub mod utc;
206
207#[cfg(feature = "eop")]
208pub mod eop;
209
210#[cfg(feature = "sidereal")]
211pub mod sidereal;
212
213// _________________________________________
214// CRATE USE
215// _________________________________________
216pub(crate) use civil_parts::*;
217pub(crate) use consts::*;
218pub(crate) use locale::*;
219#[allow(unused_imports)]
220pub(crate) use math::*;
221pub(crate) use strtime::*;
222
223#[cfg(feature = "parse")]
224pub(crate) use alloc_parse::{
225    alloc_consts::*, date::*, date_classification::*, duration::*, helpers::*, parse_date::*,
226    types::*,
227};
228
229#[cfg(feature = "parse")]
230pub(crate) use locale::{lang_data::*, lang_map::*};
231
232// _________________________________________
233// PUB USE
234// _________________________________________
235pub use an_err::AnErr;
236pub use buf_str::BufStr;
237pub use dt::Dt;
238pub use dt::lunar;
239pub use dt::numbers_traits::TraitsTime;
240pub use error::{DtErr, DtErrKind};
241pub use locale::Lang;
242pub use scale::Scale;
243pub use strtime::StrPTimeFmt;
244pub use time_range::{Every, TimeRange};
245pub use ymdhms::YmdHms;
246
247#[cfg(feature = "tdb-hi")]
248pub use dt::tdb_hi;
249
250#[cfg(feature = "parse")]
251pub use alloc_parse::types::{Mode, Order, ParseCfg};
252
253#[cfg(feature = "mars")]
254pub use dt::mars;
255
256#[cfg(feature = "sidereal")]
257#[doc(hidden)]
258pub use sidereal::Sidereal;