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, 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 dt;
179mod error;
180mod lite_str;
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::{
217 atan2::atan2,
218 cos::cos,
219 div::rem_euclid_f,
220 floor::floor_f,
221 log::log,
222 sin::sin,
223 sqrt::{hypot, sqrt},
224};
225pub(crate) use strtime::*;
226
227#[cfg(feature = "parse")]
228pub(crate) use alloc_parse::{
229 alloc_consts::*, date::*, date_classification::*, duration::*, helpers::*, parse_date::*,
230 types::*,
231};
232
233#[cfg(feature = "parse")]
234pub(crate) use locale::{lang_data::*, lang_map::*};
235
236// _________________________________________
237// PUB USE
238// _________________________________________
239pub use an_err::AnErr;
240pub use dt::Dt;
241pub use dt::lunar;
242pub use dt::numbers_traits::TraitsTime;
243pub use error::{DtErr, DtErrKind};
244pub use lite_str::LiteStr;
245pub use locale::Lang;
246pub use scale::Scale;
247pub use strtime::StrPTimeFmt;
248pub use time_range::{Every, TimeRange};
249pub use ymdhms::YmdHms;
250
251#[cfg(feature = "tdb-hi")]
252pub use dt::tdb_hi;
253
254#[cfg(feature = "parse")]
255pub use alloc_parse::types::{Mode, Order, ParseCfg};
256
257#[cfg(feature = "mars")]
258pub use dt::mars;
259
260#[cfg(feature = "sidereal")]
261#[doc(hidden)]
262pub use sidereal::Sidereal;
263
264#[cfg(feature = "physics")]
265pub use physics::{
266 drift::Drift, observer::Observer, position::Position, spacetime::Spacetime, velocity::Velocity,
267};