time_core/util.rs
1//! Utility functions.
2
3use crate::hint;
4
5/// Versions of functions that are optimized for when the year has already been range-validated.
6///
7/// The implementations of these functions depend on whether the `large-dates` feature is enabled.
8///
9/// Note: This module is not exposed by the `time` crate. It is an implementation detail.
10pub mod range_validated {
11 /// Returns if the provided year is a leap year in the proleptic Gregorian calendar, assuming
12 /// the year has already been range-validated.
13 ///
14 /// Behavior is unspecified for years outside the valid range.
15 ///
16 /// Note: This function is not exposed by the `time` crate. It is an implementation detail.
17 #[inline]
18 #[track_caller]
19 pub const fn is_leap_year(year: i32) -> bool {
20 #[cfg(feature = "large-dates")]
21 {
22 super::is_leap_year(year)
23 }
24 #[cfg(not(feature = "large-dates"))]
25 {
26 debug_assert!(year >= -9999);
27 debug_assert!(year <= 9999);
28 year.unsigned_abs().wrapping_mul(0x20003D7) & 0x6007C0F <= 0x7C00
29 }
30 }
31
32 /// Get the number of calendar days in a given year, assuming the year has already been
33 /// range-validated.
34 ///
35 /// Behavior is unspecified for years outside the valid range.
36 ///
37 /// Note: This function is not exposed by the `time` crate. It is an implementation detail.
38 #[inline]
39 #[track_caller]
40 pub const fn days_in_year(year: i32) -> u16 {
41 #[cfg(feature = "large-dates")]
42 {
43 super::days_in_year(year)
44 }
45 #[cfg(not(feature = "large-dates"))]
46 {
47 if is_leap_year(year) { 366 } else { 365 }
48 }
49 }
50
51 /// Get the number of days in the month of a given year, assuming the year has already been
52 /// range-validated.
53 ///
54 /// Note: This function is not exposed by the `time` crate. It is an implementation detail.
55 #[inline]
56 #[track_caller]
57 pub const fn days_in_month(month: u8, year: i32) -> u8 {
58 #[cfg(feature = "large-dates")]
59 {
60 super::days_in_month(month, year)
61 }
62 #[cfg(not(feature = "large-dates"))]
63 {
64 super::days_in_month_leap(month, is_leap_year(year))
65 }
66 }
67}
68
69/// Returns if the provided year is a leap year in the proleptic Gregorian calendar. Uses
70/// [astronomical year numbering](https://en.wikipedia.org/wiki/Astronomical_year_numbering).
71///
72/// ```rust
73/// # use time::util::is_leap_year;
74/// assert!(!is_leap_year(1900));
75/// assert!(is_leap_year(2000));
76/// assert!(is_leap_year(2004));
77/// assert!(!is_leap_year(2005));
78/// assert!(!is_leap_year(2100));
79/// ```
80// https://hueffner.de/falk/blog/a-leap-year-check-in-three-instructions.html
81#[inline]
82pub const fn is_leap_year(year: i32) -> bool {
83 (year as i64)
84 .unsigned_abs()
85 .wrapping_mul(0x4000_0000_28F5_C28F)
86 & 0xC000_000F_8000_000F
87 <= 0xF_8000_0000
88}
89
90/// Get the number of calendar days in a given year.
91///
92/// The returned value will always be either 365 or 366.
93///
94/// ```rust
95/// # use time::util::days_in_year;
96/// assert_eq!(days_in_year(1900), 365);
97/// assert_eq!(days_in_year(2000), 366);
98/// assert_eq!(days_in_year(2004), 366);
99/// assert_eq!(days_in_year(2005), 365);
100/// assert_eq!(days_in_year(2100), 365);
101/// ```
102#[inline]
103pub const fn days_in_year(year: i32) -> u16 {
104 if is_leap_year(year) { 366 } else { 365 }
105}
106
107/// Get the number of weeks in the ISO year.
108///
109/// The returned value will always be either 52 or 53.
110///
111/// ```rust
112/// # use time::util::weeks_in_year;
113/// assert_eq!(weeks_in_year(2019), 52);
114/// assert_eq!(weeks_in_year(2020), 53);
115/// ```
116#[inline]
117pub const fn weeks_in_year(year: i32) -> u8 {
118 match year % 400 {
119 -396 | -391 | -385 | -380 | -374 | -368 | -363 | -357 | -352 | -346 | -340 | -335
120 | -329 | -324 | -318 | -312 | -307 | -301 | -295 | -289 | -284 | -278 | -272 | -267
121 | -261 | -256 | -250 | -244 | -239 | -233 | -228 | -222 | -216 | -211 | -205 | -199
122 | -193 | -188 | -182 | -176 | -171 | -165 | -160 | -154 | -148 | -143 | -137 | -132
123 | -126 | -120 | -115 | -109 | -104 | -97 | -92 | -86 | -80 | -75 | -69 | -64 | -58
124 | -52 | -47 | -41 | -36 | -30 | -24 | -19 | -13 | -8 | -2 | 4 | 9 | 15 | 20 | 26 | 32
125 | 37 | 43 | 48 | 54 | 60 | 65 | 71 | 76 | 82 | 88 | 93 | 99 | 105 | 111 | 116 | 122
126 | 128 | 133 | 139 | 144 | 150 | 156 | 161 | 167 | 172 | 178 | 184 | 189 | 195 | 201
127 | 207 | 212 | 218 | 224 | 229 | 235 | 240 | 246 | 252 | 257 | 263 | 268 | 274 | 280
128 | 285 | 291 | 296 | 303 | 308 | 314 | 320 | 325 | 331 | 336 | 342 | 348 | 353 | 359
129 | 364 | 370 | 376 | 381 | 387 | 392 | 398 => 53,
130 _ => 52,
131 }
132}
133
134/// Get the number of days in the month of a given year.
135///
136/// ```rust
137/// # use time_core::util::days_in_month;
138/// assert_eq!(days_in_month(2, 2020), 29);
139/// ```
140///
141/// Note: This function is not exposed by the `time` crate. It is an implementation detail.
142#[inline]
143#[track_caller]
144pub const fn days_in_month(month: u8, year: i32) -> u8 {
145 days_in_month_leap(month, is_leap_year(year))
146}
147
148/// Get the number of days in the month. The year does not need to be known, but whether the
149/// year is a leap year does.
150///
151/// Note: This function is not exposed by the `time` crate. It is an implementation detail.
152#[inline]
153#[track_caller]
154pub const fn days_in_month_leap(month: u8, is_leap_year: bool) -> u8 {
155 debug_assert!(month >= 1);
156 debug_assert!(month <= 12);
157
158 if hint::unlikely(month == 2) {
159 if is_leap_year { 29 } else { 28 }
160 } else {
161 30 | month ^ (month >> 3)
162 }
163}