1 | //! Utility functions. |
2 | |
3 | pub use time_core::util::{days_in_year, is_leap_year, weeks_in_year}; |
4 | |
5 | use crate::Month; |
6 | |
7 | /// Whether to adjust the date, and in which direction. Useful when implementing arithmetic. |
8 | pub(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 | /// ``` |
23 | pub 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. |
35 | pub 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::SeqCst); |
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::SeqCst) { |
95 | false => Soundness::Unsound, |
96 | true => Soundness::Sound, |
97 | } |
98 | } |
99 | } |
100 | |