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_b("%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, macros::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/*
102uncomment this fn and run
103cargo test --release --features "parse"
104
105if it builds then std is being silently pulled in
106
107update to use something that requires std if
108.round() can work without std in the future
109*/
110
111// #[allow(dead_code)]
112// fn check_if_std_silently_pulled_in() {
113// let x: f64 = 0.5;
114// let _: f64 = x.round();
115// }
116
117// ──────────────────────────────────────────────────────────────
118// Optional panic handler (opt-in via feature)
119// ──────────────────────────────────────────────────────────────
120#[cfg(all(feature = "panic-handler", not(feature = "std"), not(test)))]
121use core::panic::PanicInfo;
122
123#[cfg(all(feature = "panic-handler", not(feature = "std"), not(test)))]
124#[panic_handler]
125fn panic(_info: &PanicInfo) -> ! {
126 // Uses spin_loop() for better power characteristics than plain loop{}
127 loop {
128 core::hint::spin_loop();
129 }
130}
131
132/// Alias for f64, maybe upgrade one day
133pub type Real = f64;
134
135/// Convert a number to the crates [`Real`] type (f64).
136///
137/// Equivalent to `n as f64`.
138#[macro_export]
139macro_rules! f {
140 ($x:expr) => {
141 $x as $crate::Real
142 };
143}
144
145/// Safe Euclidean division.
146/// Returns `default` if `rhs == 0` or if `lhs == i128::MIN && rhs == -1`.
147macro_rules! safe_div_euc {
148 ($lhs:expr, $rhs:expr, $default:expr) => {{
149 match ($lhs).checked_div_euclid($rhs) {
150 Some(q) => q,
151 None => $default,
152 }
153 }};
154}
155
156/// Safe Euclidean remainder.
157/// Returns `$default` if `rhs == 0` or if `lhs == Self::MIN && rhs == -1`.
158macro_rules! safe_rem_euc {
159 ($lhs:expr, $rhs:expr, $default:expr) => {{
160 match ($lhs).checked_rem_euclid($rhs) {
161 Some(r) => r,
162 None => $default,
163 }
164 }};
165}
166
167/// Turns a list of string literals into an array of `&'static [u8]`.
168macro_rules! byte_arrays {
169 ( $( $s:literal ),+ $(,)? ) => {
170 [ $( $s.as_bytes() ),+ ]
171 };
172}
173
174// _________________________________________
175// MOD
176// _________________________________________
177mod an_err;
178mod buf_str;
179mod dt;
180mod error;
181mod locale;
182mod scale;
183mod strtime;
184mod time_range;
185mod ymdhms;
186
187#[cfg(feature = "parse")]
188mod alloc_parse;
189
190#[cfg(feature = "physics")]
191mod physics;
192
193// _________________________________________
194// PUB MOD
195// _________________________________________
196pub mod civil_parts;
197pub mod consts;
198pub mod macros;
199pub mod math;
200pub mod tz;
201pub mod utc;
202
203#[cfg(feature = "eop")]
204pub mod eop;
205
206#[cfg(feature = "sidereal")]
207pub mod sidereal;
208
209// _________________________________________
210// CRATE USE
211// _________________________________________
212pub(crate) use civil_parts::*;
213pub(crate) use consts::*;
214pub(crate) use locale::*;
215#[allow(unused_imports)]
216pub(crate) use math::*;
217pub(crate) use strtime::*;
218
219#[cfg(feature = "parse")]
220pub(crate) use alloc_parse::{
221 alloc_consts::*, date::*, date_classification::*, duration::*, helpers::*, parse_date::*,
222 types::*,
223};
224
225#[cfg(feature = "parse")]
226pub(crate) use locale::{lang_data::*, lang_map::*};
227
228// _________________________________________
229// PUB USE
230// _________________________________________
231pub use an_err::AnErr;
232pub use buf_str::BufStr;
233pub use dt::Dt;
234pub use dt::lunar;
235pub use dt::numbers_traits::TraitsTime;
236pub use error::{DtErr, DtErrKind};
237pub use locale::Lang;
238pub use scale::Scale;
239pub use strtime::StrPTimeFmt;
240pub use time_range::{Every, TimeRange};
241pub use ymdhms::YmdHms;
242
243#[cfg(feature = "tdb-hi")]
244pub use dt::tdb_hi;
245
246#[cfg(feature = "parse")]
247pub use alloc_parse::types::{Mode, Order, ParseCfg};
248
249#[cfg(feature = "mars")]
250pub use dt::mars;
251
252#[cfg(feature = "sidereal")]
253#[doc(hidden)]
254pub use sidereal::Sidereal;
255
256#[cfg(feature = "physics")]
257pub use physics::{
258 drift::Drift, observer::Observer, position::Position, spacetime::Spacetime, velocity::Velocity,
259};