time/
util.rs

1//! Utility functions.
2
3pub use time_core::util::{days_in_year, is_leap_year, weeks_in_year};
4
5use crate::Month;
6
7/// Whether to adjust the date, and in which direction. Useful when implementing arithmetic.
8pub(crate) enum DateAdjustment {
9    /// The previous day should be used.
10    Previous,
11    /// The next day should be used.
12    Next,
13    /// The date should be used as-is.
14    None,
15}
16
17/// Get the number of days in the month of a given year.
18///
19/// ```rust
20/// # use time::{Month, util};
21/// assert_eq!(util::days_in_year_month(2020, Month::February), 29);
22/// ```
23pub const fn days_in_year_month(year: i32, month: Month) -> u8 {
24    use Month::*;
25    match month {
26        January | March | May | July | August | October | December => 31,
27        April | June | September | November => 30,
28        February if is_leap_year(year) => 29,
29        February => 28,
30    }
31}
32
33#[cfg(feature = "local-offset")]
34/// Utility functions relating to the local UTC offset.
35pub mod local_offset {
36    use core::sync::atomic::{AtomicBool, Ordering};
37
38    /// Whether obtaining the local UTC offset is required to be sound.
39    static LOCAL_OFFSET_IS_SOUND: AtomicBool = AtomicBool::new(true);
40
41    /// The soundness of obtaining the local UTC offset.
42    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
43    pub enum Soundness {
44        /// Obtaining the local UTC offset is required to be sound. Undefined behavior will never
45        /// occur. This is the default.
46        Sound,
47        /// Obtaining the local UTC offset is allowed to invoke undefined behavior. **Setting this
48        /// value is strongly discouraged.** To do so, you must comply with the safety requirements
49        /// of [`time::local_offset::set_soundness`](set_soundness).
50        Unsound,
51    }
52
53    /// Set whether obtaining the local UTC offset is allowed to invoke undefined behavior. **Use of
54    /// this function is heavily discouraged.**
55    ///
56    /// # Safety
57    ///
58    /// If this method is called with [`Soundness::Sound`], the call is always sound. If this method
59    /// is called with [`Soundness::Unsound`], the following conditions apply.
60    ///
61    /// - If the operating system provides a thread-safe environment, the call is sound.
62    /// - If the process is single-threaded, the call is sound.
63    /// - If the process is multi-threaded, no other thread may mutate the environment in any way at
64    ///   the same time a call to a method that obtains the local UTC offset. This includes adding,
65    ///   removing, or modifying an environment variable.
66    ///
67    /// The first two conditions are automatically checked by `time`, such that you do not need to
68    /// declare your code unsound. Currently, the only known operating systems that does _not_
69    /// provide a thread-safe environment are some Unix-like OS's. All other operating systems
70    /// should succeed when attempting to obtain the local UTC offset.
71    ///
72    /// Note that you must not only verify this safety condition for your code, but for **all** code
73    /// that will be included in the final binary. Notably, it applies to both direct and transitive
74    /// dependencies and to both Rust and non-Rust code. **For this reason it is not possible to
75    /// soundly pass [`Soundness::Unsound`] to this method if you are writing a library that may
76    /// used by others.**
77    ///
78    /// If using this method is absolutely necessary, it is recommended to keep the time between
79    /// setting the soundness to [`Soundness::Unsound`] and setting it back to [`Soundness::Sound`]
80    /// as short as possible.
81    ///
82    /// The following methods currently obtain the local UTC offset:
83    ///
84    /// - [`OffsetDateTime::now_local`](crate::OffsetDateTime::now_local)
85    /// - [`UtcOffset::local_offset_at`](crate::UtcOffset::local_offset_at)
86    /// - [`UtcOffset::current_local_offset`](crate::UtcOffset::current_local_offset)
87    pub unsafe fn set_soundness(soundness: Soundness) {
88        LOCAL_OFFSET_IS_SOUND.store(soundness == Soundness::Sound, Ordering::Release);
89    }
90
91    /// Obtains the soundness of obtaining the local UTC offset. If it is [`Soundness::Unsound`],
92    /// it is allowed to invoke undefined behavior when obtaining the local UTC offset.
93    pub fn get_soundness() -> Soundness {
94        match LOCAL_OFFSET_IS_SOUND.load(Ordering::Acquire) {
95            false => Soundness::Unsound,
96            true => Soundness::Sound,
97        }
98    }
99}