Skip to main content

deep_time/civil_parts/
mod.rs

1//! Intermediate "parts" of a civil date and time.
2//!
3//! [`Parts`] is produced by the parsers (string formats, ISO-like, CCSDS
4//! binary and text, etc.). It is then typically converted into a
5//! [`Dt`](../struct.Dt.html) or a type from `chrono`/`jiff`.
6//!
7//! It holds the individual components (various ways to express the date,
8//! time-of-day down to attoseconds, offset, scale, weekday/week info, etc.).
9
10mod from_bin_ccsds;
11mod from_str;
12mod from_str_iso;
13mod to_bin_ccsds;
14mod to_deep_time;
15
16#[cfg(feature = "alloc")]
17mod to_str_ccsds;
18
19#[cfg(feature = "chrono")]
20mod to_chrono;
21
22#[cfg(feature = "jiff")]
23mod to_jiff;
24
25use crate::{LiteStr, Scale};
26
27/// Intermediate representation of parsed civil date and time.
28///
29/// After parsing you typically convert the `Parts` to a final type
30/// such as [`Dt`] or one from `chrono`/`jiff`.
31///
32/// ## Examples
33///
34/// ```rust
35/// use deep_time::civil_parts::Parts;
36///
37/// let parts = Parts::from_str_iso("2024-06-20T14:30:00Z").unwrap();
38///
39/// // now you can convert to whichever type you need
40/// let dt = parts.to_dt().unwrap();
41/// ```
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43#[cfg_attr(feature = "tsify", derive(tsify::Tsify))]
44#[derive(Debug, Clone, Copy, Default, PartialEq)]
45pub struct Parts {
46    /// Year (can be negative for BCE dates).
47    pub yr: Option<i64>,
48    /// Month of the year (1–12).
49    pub mo: Option<u8>,
50    /// Day of the month (1–31).
51    pub day: Option<u8>,
52    /// Hour of the day (0–23).
53    pub hr: u8,
54    /// Minute of the hour (0–59).
55    pub min: u8,
56    /// Second of the minute (0–60). Value 60 is used for leap seconds.
57    pub sec: u8,
58    /// Attoseconds (0 ≤ value < 10¹⁸).
59    pub attos: u64,
60    /// Timezone offset from UTC.
61    pub offset: Option<Offset>,
62    /// IANA timezone name (e.g. `"America/New_York"`), stored as ASCII.
63    pub iana_name: Option<LiteStr<49>>,
64    /// The time scale this value belongs to (TAI, UTC, etc.).
65    pub scale: Scale,
66    /// Day of the week.
67    pub wkday: Option<Weekday>,
68    /// Day of the year (1–366), corresponding to `%j`.
69    pub day_of_yr: Option<u16>,
70    /// ISO week year (`%G` / `%g`).
71    pub iso_wk_yr: Option<i64>,
72    /// ISO week number (1–53), corresponding to `%V`.
73    pub iso_wk: Option<u8>,
74    /// Week number with Sunday as first day of week (0–53), `%U`.
75    pub wk_sun: Option<u8>,
76    /// Week number with Monday as first day of week (0–53), `%W`.
77    pub wk_mon: Option<u8>,
78    /// AM / PM indicator.
79    pub meridiem: Option<Meridiem>,
80    /// Timestamp in seconds since a known epoch (`%s` = Unix 1970, `%J` = noon 2000 / J2000).
81    pub timestamp: Option<Timestamp>,
82}
83
84/// Raw parsed components from a "seconds + optional fraction" string.
85#[derive(Clone, Copy)]
86pub(crate) struct SecF {
87    pub(crate) negative: bool,
88    /// Accumulated absolute integer part (u64::MAX on overflow during accumulation).
89    pub(crate) int_u: u64,
90    /// Fractional attoseconds, already left-padded to 18 digits.
91    pub(crate) frac_attos: u64,
92    pub(crate) scale: Scale,
93}
94
95impl Parts {
96    #[inline(always)]
97    pub fn new_utc() -> Parts {
98        Self {
99            scale: Scale::UTC,
100            ..Default::default()
101        }
102    }
103
104    /// Sets the IANA timezone name.
105    #[inline(always)]
106    pub fn set_iana_name(&mut self, name: Option<&str>) {
107        self.iana_name = name.map(LiteStr::new);
108    }
109}
110
111/// Used by [`TimestampSec`]
112///
113/// Records the epoch of the timestamp.
114#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
115#[cfg_attr(feature = "tsify", derive(tsify::Tsify))]
116#[derive(Copy, Clone, Debug, PartialEq, Eq)]
117pub enum Epoch {
118    Unix,
119    Noon2000,
120}
121
122/// Timestamp seconds relative to a specific epoch.
123///
124/// Used by the `%s` (Unix epoch) and `%J` (J2000.0 noon 2000-01-01 12:00 TAI) directives.
125#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
126#[cfg_attr(feature = "tsify", derive(tsify::Tsify))]
127#[derive(Debug, Clone, Copy, PartialEq, Eq)]
128pub struct Timestamp {
129    pub attos: i128,
130    pub epoch: Epoch,
131}
132
133/// AM / PM indicator.
134#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
135#[cfg_attr(feature = "tsify", derive(tsify::Tsify))]
136#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
137pub enum Meridiem {
138    #[default]
139    AM,
140    PM,
141}
142
143/// Day of the week. Default is set to Sunday.
144#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
145#[cfg_attr(feature = "tsify", derive(tsify::Tsify))]
146#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
147pub enum Weekday {
148    #[default]
149    Sunday,
150    Monday,
151    Tuesday,
152    Wednesday,
153    Thursday,
154    Friday,
155    Saturday,
156}
157
158impl Weekday {
159    /// Converts a Sunday-based weekday number (0 = Sunday … 6 = Saturday) to `Weekday`.
160    pub const fn from_sunday_0_based(n: u8) -> Option<Self> {
161        match n {
162            0 => Some(Weekday::Sunday),
163            1 => Some(Weekday::Monday),
164            2 => Some(Weekday::Tuesday),
165            3 => Some(Weekday::Wednesday),
166            4 => Some(Weekday::Thursday),
167            5 => Some(Weekday::Friday),
168            6 => Some(Weekday::Saturday),
169            _ => None,
170        }
171    }
172
173    /// Converts a Monday-based weekday number (1 = Monday … 7 = Sunday) to `Weekday`.
174    pub const fn from_monday_1_based(n: u8) -> Option<Self> {
175        match n {
176            1 => Some(Weekday::Monday),
177            2 => Some(Weekday::Tuesday),
178            3 => Some(Weekday::Wednesday),
179            4 => Some(Weekday::Thursday),
180            5 => Some(Weekday::Friday),
181            6 => Some(Weekday::Saturday),
182            7 => Some(Weekday::Sunday),
183            _ => None,
184        }
185    }
186
187    /// Sunday-based weekday number (0 = Sunday … 6 = Saturday).
188    pub const fn wkday_sun_0_based(self) -> u8 {
189        match self {
190            Weekday::Sunday => 0,
191            Weekday::Monday => 1,
192            Weekday::Tuesday => 2,
193            Weekday::Wednesday => 3,
194            Weekday::Thursday => 4,
195            Weekday::Friday => 5,
196            Weekday::Saturday => 6,
197        }
198    }
199
200    /// Sunday-based weekday number (1 = Sunday … 7 = Saturday).
201    pub const fn wkday_sun_1_based(self) -> u8 {
202        match self {
203            Weekday::Sunday => 1,
204            Weekday::Monday => 2,
205            Weekday::Tuesday => 3,
206            Weekday::Wednesday => 4,
207            Weekday::Thursday => 5,
208            Weekday::Friday => 6,
209            Weekday::Saturday => 7,
210        }
211    }
212
213    /// Monday-based weekday number (0 = Monday … 6 = Sunday).
214    pub const fn wkday_mon_0_based(self) -> u8 {
215        match self {
216            Weekday::Monday => 0,
217            Weekday::Tuesday => 1,
218            Weekday::Wednesday => 2,
219            Weekday::Thursday => 3,
220            Weekday::Friday => 4,
221            Weekday::Saturday => 5,
222            Weekday::Sunday => 6,
223        }
224    }
225
226    /// Monday-based weekday number (1 = Monday … 7 = Sunday).
227    pub const fn wkday_mon_1_based(self) -> u8 {
228        match self {
229            Weekday::Monday => 1,
230            Weekday::Tuesday => 2,
231            Weekday::Wednesday => 3,
232            Weekday::Thursday => 4,
233            Weekday::Friday => 5,
234            Weekday::Saturday => 6,
235            Weekday::Sunday => 7,
236        }
237    }
238}
239
240/// Timezone offset representation.
241#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
242#[cfg_attr(feature = "tsify", derive(tsify::Tsify))]
243#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
244pub enum Offset {
245    #[default]
246    None,
247    /// Fixed offset in seconds
248    Fixed(i32),
249}