Skip to main content

leap_sec/
error.rs

1//! Error types for leap-second conversions.
2
3use core::fmt;
4
5/// Errors that can occur during leap-second conversions or table construction.
6///
7/// All conversion methods on [`LeapSeconds`](crate::LeapSeconds) return
8/// `Result<T, Error>`. Pattern-match to handle specific cases:
9///
10/// # Example
11///
12/// ```
13/// use leap_sec::prelude::*;
14///
15/// let leaps = LeapSeconds::known();
16///
17/// match leaps.utc_to_tai(UtcUnixSeconds(0)) {
18///     Ok(tai) => println!("TAI: {tai}"),
19///     Err(Error::OutOfRange { requested, valid_start, .. }) => {
20///         println!("{requested} is before {valid_start}");
21///     }
22///     Err(e) => println!("other error: {e}"),
23/// }
24/// ```
25#[derive(Debug, Clone, PartialEq, Eq)]
26pub enum Error {
27    /// The requested timestamp is before the first entry in the leap-second table.
28    ///
29    /// Returned by [`LeapSeconds::utc_to_tai`](crate::LeapSeconds::utc_to_tai),
30    /// [`LeapSeconds::tai_to_utc`](crate::LeapSeconds::tai_to_utc), and all
31    /// other conversion and offset methods when the input is outside the table's
32    /// valid range.
33    OutOfRange {
34        /// The timestamp that was requested.
35        requested: i64,
36        /// The earliest valid timestamp in the table.
37        valid_start: i64,
38        /// The latest entry timestamp in the table.
39        valid_end: i64,
40    },
41    /// The leap-second table has expired.
42    ///
43    /// Reserved for future use when tables parsed from IERS files carry
44    /// expiration timestamps. The built-in [`LeapSeconds::known()`](crate::LeapSeconds::known)
45    /// table never produces this error.
46    TableExpired {
47        /// When the table expires, as a UTC Unix timestamp.
48        expires_at: i64,
49    },
50    /// The table data is invalid (used during builder validation).
51    ///
52    /// Returned by [`LeapSecondsBuilder::build()`](crate::LeapSecondsBuilder::build)
53    /// when the table is empty or timestamps are not monotonically increasing.
54    InvalidTable {
55        /// A description of what is wrong with the table.
56        detail: &'static str,
57    },
58}
59
60/// Formats the error with a human-readable message.
61///
62/// - `OutOfRange`: `"timestamp {requested} is outside the leap-second table range [{start}, {end}]"`
63/// - `TableExpired`: `"leap-second table expired at {expires_at}; load a newer table or update the crate"`
64/// - `InvalidTable`: `"invalid leap-second table: {detail}"`
65impl fmt::Display for Error {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match self {
68            Self::OutOfRange {
69                requested,
70                valid_start,
71                valid_end,
72            } => write!(
73                f,
74                "timestamp {requested} is outside the leap-second table range \
75                 [{valid_start}, {valid_end}]"
76            ),
77            Self::TableExpired { expires_at } => {
78                write!(
79                    f,
80                    "leap-second table expired at {expires_at}; \
81                     load a newer table or update the crate"
82                )
83            }
84            Self::InvalidTable { detail } => {
85                write!(f, "invalid leap-second table: {detail}")
86            }
87        }
88    }
89}
90
91/// Enables use as `Box<dyn std::error::Error>` and in error-handling chains.
92///
93/// Available only with the `std` feature (enabled by default).
94#[cfg(feature = "std")]
95impl std::error::Error for Error {}