/rust/registry/src/index.crates.io-6f17d22bba15001f/time-0.3.13/src/date.rs
Line | Count | Source (jump to first uncovered line) |
1 | | //! The [`Date`] struct and its associated `impl`s. |
2 | | |
3 | | use core::fmt; |
4 | | use core::ops::{Add, Sub}; |
5 | | use core::time::Duration as StdDuration; |
6 | | #[cfg(feature = "formatting")] |
7 | | use std::io; |
8 | | |
9 | | #[cfg(feature = "formatting")] |
10 | | use crate::formatting::Formattable; |
11 | | #[cfg(feature = "parsing")] |
12 | | use crate::parsing::Parsable; |
13 | | use crate::util::{days_in_year, days_in_year_month, is_leap_year, weeks_in_year}; |
14 | | use crate::{error, Duration, Month, PrimitiveDateTime, Time, Weekday}; |
15 | | |
16 | | /// The minimum valid year. |
17 | | pub(crate) const MIN_YEAR: i32 = if cfg!(feature = "large-dates") { |
18 | | -999_999 |
19 | | } else { |
20 | | -9999 |
21 | | }; |
22 | | /// The maximum valid year. |
23 | | pub(crate) const MAX_YEAR: i32 = if cfg!(feature = "large-dates") { |
24 | | 999_999 |
25 | | } else { |
26 | | 9999 |
27 | | }; |
28 | | |
29 | | /// Date in the proleptic Gregorian calendar. |
30 | | /// |
31 | | /// By default, years between ±9999 inclusive are representable. This can be expanded to ±999,999 |
32 | | /// inclusive by enabling the `large-dates` crate feature. Doing so has performance implications |
33 | | /// and introduces some ambiguities when parsing. |
34 | | #[derive(Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)] |
35 | | pub struct Date { |
36 | | /// Bitpacked field containing both the year and ordinal. |
37 | | // | xx | xxxxxxxxxxxxxxxxxxxxx | xxxxxxxxx | |
38 | | // | 2 bits | 21 bits | 9 bits | |
39 | | // | unassigned | year | ordinal | |
40 | | // The year is 15 bits when `large-dates` is not enabled. |
41 | | value: i32, |
42 | | } |
43 | | |
44 | | impl fmt::Debug for Date { |
45 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { |
46 | 0 | f.debug_struct("Date") |
47 | 0 | .field("year", &self.year()) |
48 | 0 | .field("ordinal", &self.ordinal()) |
49 | 0 | .finish() |
50 | 0 | } |
51 | | } |
52 | | |
53 | | impl Date { |
54 | | /// The minimum valid `Date`. |
55 | | /// |
56 | | /// The value of this may vary depending on the feature flags enabled. |
57 | | pub const MIN: Self = Self::__from_ordinal_date_unchecked(MIN_YEAR, 1); |
58 | | |
59 | | /// The maximum valid `Date`. |
60 | | /// |
61 | | /// The value of this may vary depending on the feature flags enabled. |
62 | | pub const MAX: Self = Self::__from_ordinal_date_unchecked(MAX_YEAR, days_in_year(MAX_YEAR)); |
63 | | |
64 | | // region: constructors |
65 | | /// Construct a `Date` from the year and ordinal values, the validity of which must be |
66 | | /// guaranteed by the caller. |
67 | | #[doc(hidden)] |
68 | 19.0k | pub const fn __from_ordinal_date_unchecked(year: i32, ordinal: u16) -> Self { |
69 | 19.0k | debug_assert!(year >= MIN_YEAR); |
70 | 19.0k | debug_assert!(year <= MAX_YEAR); |
71 | 19.0k | debug_assert!(ordinal != 0); |
72 | 19.0k | debug_assert!(ordinal <= days_in_year(year)); |
73 | | |
74 | 19.0k | Self { |
75 | 19.0k | value: (year << 9) | ordinal as i32, |
76 | 19.0k | } |
77 | 19.0k | } |
78 | | |
79 | | /// Attempt to create a `Date` from the year, month, and day. |
80 | | /// |
81 | | /// ```rust |
82 | | /// # use time::{Date, Month}; |
83 | | /// assert!(Date::from_calendar_date(2019, Month::January, 1).is_ok()); |
84 | | /// assert!(Date::from_calendar_date(2019, Month::December, 31).is_ok()); |
85 | | /// ``` |
86 | | /// |
87 | | /// ```rust |
88 | | /// # use time::{Date, Month}; |
89 | | /// assert!(Date::from_calendar_date(2019, Month::February, 29).is_err()); // 2019 isn't a leap year. |
90 | | /// ``` |
91 | 13.1k | pub const fn from_calendar_date( |
92 | 13.1k | year: i32, |
93 | 13.1k | month: Month, |
94 | 13.1k | day: u8, |
95 | 13.1k | ) -> Result<Self, error::ComponentRange> { |
96 | | /// Cumulative days through the beginning of a month in both common and leap years. |
97 | | const DAYS_CUMULATIVE_COMMON_LEAP: [[u16; 12]; 2] = [ |
98 | | [0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], |
99 | | [0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], |
100 | | ]; |
101 | | |
102 | 13.1k | ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); |
103 | 13.1k | ensure_value_in_range!(day conditionally in 1 => days_in_year_month(year, month)); |
104 | | |
105 | 12.9k | Ok(Self::__from_ordinal_date_unchecked( |
106 | 12.9k | year, |
107 | 12.9k | DAYS_CUMULATIVE_COMMON_LEAP[is_leap_year(year) as usize][month as usize - 1] |
108 | 12.9k | + day as u16, |
109 | 12.9k | )) |
110 | 13.1k | } |
111 | | |
112 | | /// Attempt to create a `Date` from the year and ordinal day number. |
113 | | /// |
114 | | /// ```rust |
115 | | /// # use time::Date; |
116 | | /// assert!(Date::from_ordinal_date(2019, 1).is_ok()); |
117 | | /// assert!(Date::from_ordinal_date(2019, 365).is_ok()); |
118 | | /// ``` |
119 | | /// |
120 | | /// ```rust |
121 | | /// # use time::Date; |
122 | | /// assert!(Date::from_ordinal_date(2019, 366).is_err()); // 2019 isn't a leap year. |
123 | | /// ``` |
124 | 0 | pub const fn from_ordinal_date(year: i32, ordinal: u16) -> Result<Self, error::ComponentRange> { |
125 | 0 | ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); |
126 | 0 | ensure_value_in_range!(ordinal conditionally in 1 => days_in_year(year)); |
127 | 0 | Ok(Self::__from_ordinal_date_unchecked(year, ordinal)) |
128 | 0 | } |
129 | | |
130 | | /// Attempt to create a `Date` from the ISO year, week, and weekday. |
131 | | /// |
132 | | /// ```rust |
133 | | /// # use time::{Date, Weekday::*}; |
134 | | /// assert!(Date::from_iso_week_date(2019, 1, Monday).is_ok()); |
135 | | /// assert!(Date::from_iso_week_date(2019, 1, Tuesday).is_ok()); |
136 | | /// assert!(Date::from_iso_week_date(2020, 53, Friday).is_ok()); |
137 | | /// ``` |
138 | | /// |
139 | | /// ```rust |
140 | | /// # use time::{Date, Weekday::*}; |
141 | | /// assert!(Date::from_iso_week_date(2019, 53, Monday).is_err()); // 2019 doesn't have 53 weeks. |
142 | | /// ``` |
143 | 0 | pub const fn from_iso_week_date( |
144 | 0 | year: i32, |
145 | 0 | week: u8, |
146 | 0 | weekday: Weekday, |
147 | 0 | ) -> Result<Self, error::ComponentRange> { |
148 | 0 | ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); |
149 | 0 | ensure_value_in_range!(week conditionally in 1 => weeks_in_year(year)); |
150 | | |
151 | 0 | let adj_year = year - 1; |
152 | 0 | let raw = 365 * adj_year + div_floor!(adj_year, 4) - div_floor!(adj_year, 100) |
153 | 0 | + div_floor!(adj_year, 400); |
154 | 0 | let jan_4 = match (raw % 7) as i8 { |
155 | 0 | -6 | 1 => 8, |
156 | 0 | -5 | 2 => 9, |
157 | 0 | -4 | 3 => 10, |
158 | 0 | -3 | 4 => 4, |
159 | 0 | -2 | 5 => 5, |
160 | 0 | -1 | 6 => 6, |
161 | 0 | _ => 7, |
162 | | }; |
163 | 0 | let ordinal = week as i16 * 7 + weekday.number_from_monday() as i16 - jan_4; |
164 | 0 |
|
165 | 0 | Ok(if ordinal <= 0 { |
166 | 0 | Self::__from_ordinal_date_unchecked( |
167 | 0 | year - 1, |
168 | 0 | (ordinal as u16).wrapping_add(days_in_year(year - 1)), |
169 | 0 | ) |
170 | 0 | } else if ordinal > days_in_year(year) as i16 { |
171 | 0 | Self::__from_ordinal_date_unchecked(year + 1, ordinal as u16 - days_in_year(year)) |
172 | | } else { |
173 | 0 | Self::__from_ordinal_date_unchecked(year, ordinal as _) |
174 | | }) |
175 | 0 | } |
176 | | |
177 | | /// Create a `Date` from the Julian day. |
178 | | /// |
179 | | /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is |
180 | | /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms). |
181 | | /// |
182 | | /// ```rust |
183 | | /// # use time::{Date, macros::date}; |
184 | | /// assert_eq!(Date::from_julian_day(0), Ok(date!(-4713 - 11 - 24))); |
185 | | /// assert_eq!(Date::from_julian_day(2_451_545), Ok(date!(2000 - 01 - 01))); |
186 | | /// assert_eq!(Date::from_julian_day(2_458_485), Ok(date!(2019 - 01 - 01))); |
187 | | /// assert_eq!(Date::from_julian_day(2_458_849), Ok(date!(2019 - 12 - 31))); |
188 | | /// ``` |
189 | | #[doc(alias = "from_julian_date")] |
190 | 0 | pub const fn from_julian_day(julian_day: i32) -> Result<Self, error::ComponentRange> { |
191 | 0 | ensure_value_in_range!( |
192 | 0 | julian_day in Self::MIN.to_julian_day() => Self::MAX.to_julian_day() |
193 | 0 | ); |
194 | 0 | Ok(Self::from_julian_day_unchecked(julian_day)) |
195 | 0 | } |
196 | | |
197 | | /// Create a `Date` from the Julian day. |
198 | | /// |
199 | | /// This does not check the validity of the provided Julian day, and as such may result in an |
200 | | /// internally invalid value. |
201 | | #[doc(alias = "from_julian_date_unchecked")] |
202 | 6.11k | pub(crate) const fn from_julian_day_unchecked(julian_day: i32) -> Self { |
203 | 6.11k | debug_assert!(julian_day >= Self::MIN.to_julian_day()); |
204 | 6.11k | debug_assert!(julian_day <= Self::MAX.to_julian_day()); |
205 | | |
206 | | // To avoid a potential overflow, the value may need to be widened for some arithmetic. |
207 | | |
208 | 6.11k | let z = julian_day - 1_721_119; |
209 | 6.11k | let (mut year, mut ordinal) = if julian_day < -19_752_948 || julian_day > 23_195_514 { |
210 | 0 | let g = 100 * z as i64 - 25; |
211 | 0 | let a = (g / 3_652_425) as i32; |
212 | 0 | let b = a - a / 4; |
213 | 0 | let year = div_floor!(100 * b as i64 + g, 36525) as i32; |
214 | 0 | let ordinal = (b + z - div_floor!(36525 * year as i64, 100) as i32) as _; |
215 | 0 | (year, ordinal) |
216 | | } else { |
217 | 6.11k | let g = 100 * z - 25; |
218 | 6.11k | let a = g / 3_652_425; |
219 | 6.11k | let b = a - a / 4; |
220 | 6.11k | let year = div_floor!(100 * b + g, 36525); |
221 | 6.11k | let ordinal = (b + z - div_floor!(36525 * year, 100)) as _; |
222 | 6.11k | (year, ordinal) |
223 | | }; |
224 | | |
225 | 6.11k | if is_leap_year(year) { |
226 | 537 | ordinal += 60; |
227 | 537 | cascade!(ordinal in 1..367 => year); |
228 | | } else { |
229 | 5.57k | ordinal += 59; |
230 | 5.57k | cascade!(ordinal in 1..366 => year); |
231 | | } |
232 | | |
233 | 6.11k | Self::__from_ordinal_date_unchecked(year, ordinal) |
234 | 6.11k | } |
235 | | // endregion constructors |
236 | | |
237 | | // region: getters |
238 | | /// Get the year of the date. |
239 | | /// |
240 | | /// ```rust |
241 | | /// # use time::macros::date; |
242 | | /// assert_eq!(date!(2019 - 01 - 01).year(), 2019); |
243 | | /// assert_eq!(date!(2019 - 12 - 31).year(), 2019); |
244 | | /// assert_eq!(date!(2020 - 01 - 01).year(), 2020); |
245 | | /// ``` |
246 | 41.0k | pub const fn year(self) -> i32 { |
247 | 41.0k | self.value >> 9 |
248 | 41.0k | } |
249 | | |
250 | | /// Get the month. |
251 | | /// |
252 | | /// ```rust |
253 | | /// # use time::{macros::date, Month}; |
254 | | /// assert_eq!(date!(2019 - 01 - 01).month(), Month::January); |
255 | | /// assert_eq!(date!(2019 - 12 - 31).month(), Month::December); |
256 | | /// ``` |
257 | 12.5k | pub const fn month(self) -> Month { |
258 | 12.5k | self.month_day().0 |
259 | 12.5k | } |
260 | | |
261 | | /// Get the day of the month. |
262 | | /// |
263 | | /// The returned value will always be in the range `1..=31`. |
264 | | /// |
265 | | /// ```rust |
266 | | /// # use time::macros::date; |
267 | | /// assert_eq!(date!(2019 - 01 - 01).day(), 1); |
268 | | /// assert_eq!(date!(2019 - 12 - 31).day(), 31); |
269 | | /// ``` |
270 | 12.5k | pub const fn day(self) -> u8 { |
271 | 12.5k | self.month_day().1 |
272 | 12.5k | } |
273 | | |
274 | | /// Get the month and day. This is more efficient than fetching the components individually. |
275 | | // For whatever reason, rustc has difficulty optimizing this function. It's significantly faster |
276 | | // to write the statements out by hand. |
277 | 25.0k | pub(crate) const fn month_day(self) -> (Month, u8) { |
278 | | /// The number of days up to and including the given month. Common years |
279 | | /// are first, followed by leap years. |
280 | | const CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP: [[u16; 11]; 2] = [ |
281 | | [31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334], |
282 | | [31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335], |
283 | | ]; |
284 | | |
285 | 25.0k | let days = CUMULATIVE_DAYS_IN_MONTH_COMMON_LEAP[is_leap_year(self.year()) as usize]; |
286 | 25.0k | let ordinal = self.ordinal(); |
287 | 25.0k | |
288 | 25.0k | if ordinal > days[10] { |
289 | 968 | (Month::December, (ordinal - days[10]) as _) |
290 | 24.0k | } else if ordinal > days[9] { |
291 | 5.99k | (Month::November, (ordinal - days[9]) as _) |
292 | 18.0k | } else if ordinal > days[8] { |
293 | 3.07k | (Month::October, (ordinal - days[8]) as _) |
294 | 14.9k | } else if ordinal > days[7] { |
295 | 114 | (Month::September, (ordinal - days[7]) as _) |
296 | 14.8k | } else if ordinal > days[6] { |
297 | 350 | (Month::August, (ordinal - days[6]) as _) |
298 | 14.5k | } else if ordinal > days[5] { |
299 | 216 | (Month::July, (ordinal - days[5]) as _) |
300 | 14.3k | } else if ordinal > days[4] { |
301 | 104 | (Month::June, (ordinal - days[4]) as _) |
302 | 14.2k | } else if ordinal > days[3] { |
303 | 2.51k | (Month::May, (ordinal - days[3]) as _) |
304 | 11.6k | } else if ordinal > days[2] { |
305 | 484 | (Month::April, (ordinal - days[2]) as _) |
306 | 11.2k | } else if ordinal > days[1] { |
307 | 1.52k | (Month::March, (ordinal - days[1]) as _) |
308 | 9.67k | } else if ordinal > days[0] { |
309 | 6.00k | (Month::February, (ordinal - days[0]) as _) |
310 | | } else { |
311 | 3.67k | (Month::January, ordinal as _) |
312 | | } |
313 | 25.0k | } |
314 | | |
315 | | /// Get the day of the year. |
316 | | /// |
317 | | /// The returned value will always be in the range `1..=366` (`1..=365` for common years). |
318 | | /// |
319 | | /// ```rust |
320 | | /// # use time::macros::date; |
321 | | /// assert_eq!(date!(2019 - 01 - 01).ordinal(), 1); |
322 | | /// assert_eq!(date!(2019 - 12 - 31).ordinal(), 365); |
323 | | /// ``` |
324 | 28.5k | pub const fn ordinal(self) -> u16 { |
325 | 28.5k | (self.value & 0x1FF) as _ |
326 | 28.5k | } |
327 | | |
328 | | /// Get the ISO 8601 year and week number. |
329 | 0 | pub(crate) const fn iso_year_week(self) -> (i32, u8) { |
330 | 0 | let (year, ordinal) = self.to_ordinal_date(); |
331 | 0 |
|
332 | 0 | match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as _ { |
333 | 0 | 0 => (year - 1, weeks_in_year(year - 1)), |
334 | 0 | 53 if weeks_in_year(year) == 52 => (year + 1, 1), |
335 | 0 | week => (year, week), |
336 | | } |
337 | 0 | } |
338 | | |
339 | | /// Get the ISO week number. |
340 | | /// |
341 | | /// The returned value will always be in the range `1..=53`. |
342 | | /// |
343 | | /// ```rust |
344 | | /// # use time::macros::date; |
345 | | /// assert_eq!(date!(2019 - 01 - 01).iso_week(), 1); |
346 | | /// assert_eq!(date!(2019 - 10 - 04).iso_week(), 40); |
347 | | /// assert_eq!(date!(2020 - 01 - 01).iso_week(), 1); |
348 | | /// assert_eq!(date!(2020 - 12 - 31).iso_week(), 53); |
349 | | /// assert_eq!(date!(2021 - 01 - 01).iso_week(), 53); |
350 | | /// ``` |
351 | 0 | pub const fn iso_week(self) -> u8 { |
352 | 0 | self.iso_year_week().1 |
353 | 0 | } |
354 | | |
355 | | /// Get the week number where week 1 begins on the first Sunday. |
356 | | /// |
357 | | /// The returned value will always be in the range `0..=53`. |
358 | | /// |
359 | | /// ```rust |
360 | | /// # use time::macros::date; |
361 | | /// assert_eq!(date!(2019 - 01 - 01).sunday_based_week(), 0); |
362 | | /// assert_eq!(date!(2020 - 01 - 01).sunday_based_week(), 0); |
363 | | /// assert_eq!(date!(2020 - 12 - 31).sunday_based_week(), 52); |
364 | | /// assert_eq!(date!(2021 - 01 - 01).sunday_based_week(), 0); |
365 | | /// ``` |
366 | 0 | pub const fn sunday_based_week(self) -> u8 { |
367 | 0 | ((self.ordinal() as i16 - self.weekday().number_days_from_sunday() as i16 + 6) / 7) as _ |
368 | 0 | } |
369 | | |
370 | | /// Get the week number where week 1 begins on the first Monday. |
371 | | /// |
372 | | /// The returned value will always be in the range `0..=53`. |
373 | | /// |
374 | | /// ```rust |
375 | | /// # use time::macros::date; |
376 | | /// assert_eq!(date!(2019 - 01 - 01).monday_based_week(), 0); |
377 | | /// assert_eq!(date!(2020 - 01 - 01).monday_based_week(), 0); |
378 | | /// assert_eq!(date!(2020 - 12 - 31).monday_based_week(), 52); |
379 | | /// assert_eq!(date!(2021 - 01 - 01).monday_based_week(), 0); |
380 | | /// ``` |
381 | 0 | pub const fn monday_based_week(self) -> u8 { |
382 | 0 | ((self.ordinal() as i16 - self.weekday().number_days_from_monday() as i16 + 6) / 7) as _ |
383 | 0 | } |
384 | | |
385 | | /// Get the year, month, and day. |
386 | | /// |
387 | | /// ```rust |
388 | | /// # use time::{macros::date, Month}; |
389 | | /// assert_eq!( |
390 | | /// date!(2019 - 01 - 01).to_calendar_date(), |
391 | | /// (2019, Month::January, 1) |
392 | | /// ); |
393 | | /// ``` |
394 | 0 | pub const fn to_calendar_date(self) -> (i32, Month, u8) { |
395 | 0 | let (month, day) = self.month_day(); |
396 | 0 | (self.year(), month, day) |
397 | 0 | } |
398 | | |
399 | | /// Get the year and ordinal day number. |
400 | | /// |
401 | | /// ```rust |
402 | | /// # use time::macros::date; |
403 | | /// assert_eq!(date!(2019 - 01 - 01).to_ordinal_date(), (2019, 1)); |
404 | | /// ``` |
405 | 0 | pub const fn to_ordinal_date(self) -> (i32, u16) { |
406 | 0 | (self.year(), self.ordinal()) |
407 | 0 | } |
408 | | |
409 | | /// Get the ISO 8601 year, week number, and weekday. |
410 | | /// |
411 | | /// ```rust |
412 | | /// # use time::{Weekday::*, macros::date}; |
413 | | /// assert_eq!(date!(2019 - 01 - 01).to_iso_week_date(), (2019, 1, Tuesday)); |
414 | | /// assert_eq!(date!(2019 - 10 - 04).to_iso_week_date(), (2019, 40, Friday)); |
415 | | /// assert_eq!( |
416 | | /// date!(2020 - 01 - 01).to_iso_week_date(), |
417 | | /// (2020, 1, Wednesday) |
418 | | /// ); |
419 | | /// assert_eq!( |
420 | | /// date!(2020 - 12 - 31).to_iso_week_date(), |
421 | | /// (2020, 53, Thursday) |
422 | | /// ); |
423 | | /// assert_eq!(date!(2021 - 01 - 01).to_iso_week_date(), (2020, 53, Friday)); |
424 | | /// ``` |
425 | 0 | pub const fn to_iso_week_date(self) -> (i32, u8, Weekday) { |
426 | 0 | let (year, ordinal) = self.to_ordinal_date(); |
427 | 0 | let weekday = self.weekday(); |
428 | 0 |
|
429 | 0 | match ((ordinal + 10 - self.weekday().number_from_monday() as u16) / 7) as _ { |
430 | 0 | 0 => (year - 1, weeks_in_year(year - 1), weekday), |
431 | 0 | 53 if weeks_in_year(year) == 52 => (year + 1, 1, weekday), |
432 | 0 | week => (year, week, weekday), |
433 | | } |
434 | 0 | } |
435 | | |
436 | | /// Get the weekday. |
437 | | /// |
438 | | /// ```rust |
439 | | /// # use time::{Weekday::*, macros::date}; |
440 | | /// assert_eq!(date!(2019 - 01 - 01).weekday(), Tuesday); |
441 | | /// assert_eq!(date!(2019 - 02 - 01).weekday(), Friday); |
442 | | /// assert_eq!(date!(2019 - 03 - 01).weekday(), Friday); |
443 | | /// assert_eq!(date!(2019 - 04 - 01).weekday(), Monday); |
444 | | /// assert_eq!(date!(2019 - 05 - 01).weekday(), Wednesday); |
445 | | /// assert_eq!(date!(2019 - 06 - 01).weekday(), Saturday); |
446 | | /// assert_eq!(date!(2019 - 07 - 01).weekday(), Monday); |
447 | | /// assert_eq!(date!(2019 - 08 - 01).weekday(), Thursday); |
448 | | /// assert_eq!(date!(2019 - 09 - 01).weekday(), Sunday); |
449 | | /// assert_eq!(date!(2019 - 10 - 01).weekday(), Tuesday); |
450 | | /// assert_eq!(date!(2019 - 11 - 01).weekday(), Friday); |
451 | | /// assert_eq!(date!(2019 - 12 - 01).weekday(), Sunday); |
452 | | /// ``` |
453 | 0 | pub const fn weekday(self) -> Weekday { |
454 | 0 | match self.to_julian_day() % 7 { |
455 | 0 | -6 | 1 => Weekday::Tuesday, |
456 | 0 | -5 | 2 => Weekday::Wednesday, |
457 | 0 | -4 | 3 => Weekday::Thursday, |
458 | 0 | -3 | 4 => Weekday::Friday, |
459 | 0 | -2 | 5 => Weekday::Saturday, |
460 | 0 | -1 | 6 => Weekday::Sunday, |
461 | 0 | val => { |
462 | 0 | debug_assert!(val == 0); |
463 | 0 | Weekday::Monday |
464 | | } |
465 | | } |
466 | 0 | } |
467 | | |
468 | | /// Get the next calendar date. |
469 | | /// |
470 | | /// ```rust |
471 | | /// # use time::{Date, macros::date}; |
472 | | /// assert_eq!( |
473 | | /// date!(2019 - 01 - 01).next_day(), |
474 | | /// Some(date!(2019 - 01 - 02)) |
475 | | /// ); |
476 | | /// assert_eq!( |
477 | | /// date!(2019 - 01 - 31).next_day(), |
478 | | /// Some(date!(2019 - 02 - 01)) |
479 | | /// ); |
480 | | /// assert_eq!( |
481 | | /// date!(2019 - 12 - 31).next_day(), |
482 | | /// Some(date!(2020 - 01 - 01)) |
483 | | /// ); |
484 | | /// assert_eq!(Date::MAX.next_day(), None); |
485 | | /// ``` |
486 | 0 | pub const fn next_day(self) -> Option<Self> { |
487 | 0 | if self.ordinal() == 366 || (self.ordinal() == 365 && !is_leap_year(self.year())) { |
488 | 0 | if self.value == Self::MAX.value { |
489 | 0 | None |
490 | | } else { |
491 | 0 | Some(Self::__from_ordinal_date_unchecked(self.year() + 1, 1)) |
492 | | } |
493 | | } else { |
494 | 0 | Some(Self { |
495 | 0 | value: self.value + 1, |
496 | 0 | }) |
497 | | } |
498 | 0 | } |
499 | | |
500 | | /// Get the previous calendar date. |
501 | | /// |
502 | | /// ```rust |
503 | | /// # use time::{Date, macros::date}; |
504 | | /// assert_eq!( |
505 | | /// date!(2019 - 01 - 02).previous_day(), |
506 | | /// Some(date!(2019 - 01 - 01)) |
507 | | /// ); |
508 | | /// assert_eq!( |
509 | | /// date!(2019 - 02 - 01).previous_day(), |
510 | | /// Some(date!(2019 - 01 - 31)) |
511 | | /// ); |
512 | | /// assert_eq!( |
513 | | /// date!(2020 - 01 - 01).previous_day(), |
514 | | /// Some(date!(2019 - 12 - 31)) |
515 | | /// ); |
516 | | /// assert_eq!(Date::MIN.previous_day(), None); |
517 | | /// ``` |
518 | 0 | pub const fn previous_day(self) -> Option<Self> { |
519 | 0 | if self.ordinal() != 1 { |
520 | 0 | Some(Self { |
521 | 0 | value: self.value - 1, |
522 | 0 | }) |
523 | 0 | } else if self.value == Self::MIN.value { |
524 | 0 | None |
525 | | } else { |
526 | 0 | Some(Self::__from_ordinal_date_unchecked( |
527 | 0 | self.year() - 1, |
528 | 0 | days_in_year(self.year() - 1), |
529 | 0 | )) |
530 | | } |
531 | 0 | } |
532 | | |
533 | | /// Get the Julian day for the date. |
534 | | /// |
535 | | /// The algorithm to perform this conversion is derived from one provided by Peter Baum; it is |
536 | | /// freely available [here](https://www.researchgate.net/publication/316558298_Date_Algorithms). |
537 | | /// |
538 | | /// ```rust |
539 | | /// # use time::macros::date; |
540 | | /// assert_eq!(date!(-4713 - 11 - 24).to_julian_day(), 0); |
541 | | /// assert_eq!(date!(2000 - 01 - 01).to_julian_day(), 2_451_545); |
542 | | /// assert_eq!(date!(2019 - 01 - 01).to_julian_day(), 2_458_485); |
543 | | /// assert_eq!(date!(2019 - 12 - 31).to_julian_day(), 2_458_849); |
544 | | /// ``` |
545 | 3.52k | pub const fn to_julian_day(self) -> i32 { |
546 | 3.52k | let year = self.year() - 1; |
547 | 3.52k | let ordinal = self.ordinal() as i32; |
548 | 3.52k | |
549 | 3.52k | ordinal + 365 * year + div_floor!(year, 4) - div_floor!(year, 100) |
550 | 3.52k | + div_floor!(year, 400) |
551 | | + 1_721_425 |
552 | 3.52k | } |
553 | | // endregion getters |
554 | | |
555 | | // region: checked arithmetic |
556 | | /// Computes `self + duration`, returning `None` if an overflow occurred. |
557 | | /// |
558 | | /// ```rust |
559 | | /// # use time::{Date, ext::NumericalDuration, macros::date}; |
560 | | /// assert_eq!(Date::MAX.checked_add(1.days()), None); |
561 | | /// assert_eq!(Date::MIN.checked_add((-2).days()), None); |
562 | | /// assert_eq!( |
563 | | /// date!(2020 - 12 - 31).checked_add(2.days()), |
564 | | /// Some(date!(2021 - 01 - 02)) |
565 | | /// ); |
566 | | /// ``` |
567 | | /// |
568 | | /// # Note |
569 | | /// |
570 | | /// This function only takes whole days into account. |
571 | | /// |
572 | | /// ```rust |
573 | | /// # use time::{Date, ext::NumericalDuration, macros::date}; |
574 | | /// assert_eq!(Date::MAX.checked_add(23.hours()), Some(Date::MAX)); |
575 | | /// assert_eq!(Date::MIN.checked_add((-23).hours()), Some(Date::MIN)); |
576 | | /// assert_eq!( |
577 | | /// date!(2020 - 12 - 31).checked_add(23.hours()), |
578 | | /// Some(date!(2020 - 12 - 31)) |
579 | | /// ); |
580 | | /// assert_eq!( |
581 | | /// date!(2020 - 12 - 31).checked_add(47.hours()), |
582 | | /// Some(date!(2021 - 01 - 01)) |
583 | | /// ); |
584 | | /// ``` |
585 | 0 | pub const fn checked_add(self, duration: Duration) -> Option<Self> { |
586 | 0 | let whole_days = duration.whole_days(); |
587 | 0 | if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 { |
588 | 0 | return None; |
589 | 0 | } |
590 | | |
591 | 0 | let julian_day = const_try_opt!(self.to_julian_day().checked_add(whole_days as _)); |
592 | 0 | if let Ok(date) = Self::from_julian_day(julian_day) { |
593 | 0 | Some(date) |
594 | | } else { |
595 | 0 | None |
596 | | } |
597 | 0 | } |
598 | | |
599 | | /// Computes `self - duration`, returning `None` if an overflow occurred. |
600 | | /// |
601 | | /// ``` |
602 | | /// # use time::{Date, ext::NumericalDuration, macros::date}; |
603 | | /// assert_eq!(Date::MAX.checked_sub((-2).days()), None); |
604 | | /// assert_eq!(Date::MIN.checked_sub(1.days()), None); |
605 | | /// assert_eq!( |
606 | | /// date!(2020 - 12 - 31).checked_sub(2.days()), |
607 | | /// Some(date!(2020 - 12 - 29)) |
608 | | /// ); |
609 | | /// ``` |
610 | | /// |
611 | | /// # Note |
612 | | /// |
613 | | /// This function only takes whole days into account. |
614 | | /// |
615 | | /// ``` |
616 | | /// # use time::{Date, ext::NumericalDuration, macros::date}; |
617 | | /// assert_eq!(Date::MAX.checked_sub((-23).hours()), Some(Date::MAX)); |
618 | | /// assert_eq!(Date::MIN.checked_sub(23.hours()), Some(Date::MIN)); |
619 | | /// assert_eq!( |
620 | | /// date!(2020 - 12 - 31).checked_sub(23.hours()), |
621 | | /// Some(date!(2020 - 12 - 31)) |
622 | | /// ); |
623 | | /// assert_eq!( |
624 | | /// date!(2020 - 12 - 31).checked_sub(47.hours()), |
625 | | /// Some(date!(2020 - 12 - 30)) |
626 | | /// ); |
627 | | /// ``` |
628 | 0 | pub const fn checked_sub(self, duration: Duration) -> Option<Self> { |
629 | 0 | let whole_days = duration.whole_days(); |
630 | 0 | if whole_days < i32::MIN as i64 || whole_days > i32::MAX as i64 { |
631 | 0 | return None; |
632 | 0 | } |
633 | | |
634 | 0 | let julian_day = const_try_opt!(self.to_julian_day().checked_sub(whole_days as _)); |
635 | 0 | if let Ok(date) = Self::from_julian_day(julian_day) { |
636 | 0 | Some(date) |
637 | | } else { |
638 | 0 | None |
639 | | } |
640 | 0 | } |
641 | | // endregion: checked arithmetic |
642 | | |
643 | | // region: saturating arithmetic |
644 | | /// Computes `self + duration`, saturating value on overflow. |
645 | | /// |
646 | | /// ```rust |
647 | | /// # use time::{Date, ext::NumericalDuration, macros::date}; |
648 | | /// assert_eq!(Date::MAX.saturating_add(1.days()), Date::MAX); |
649 | | /// assert_eq!(Date::MIN.saturating_add((-2).days()), Date::MIN); |
650 | | /// assert_eq!( |
651 | | /// date!(2020 - 12 - 31).saturating_add(2.days()), |
652 | | /// date!(2021 - 01 - 02) |
653 | | /// ); |
654 | | /// ``` |
655 | | /// |
656 | | /// # Note |
657 | | /// |
658 | | /// This function only takes whole days into account. |
659 | | /// |
660 | | /// ```rust |
661 | | /// # use time::{ext::NumericalDuration, macros::date}; |
662 | | /// assert_eq!( |
663 | | /// date!(2020 - 12 - 31).saturating_add(23.hours()), |
664 | | /// date!(2020 - 12 - 31) |
665 | | /// ); |
666 | | /// assert_eq!( |
667 | | /// date!(2020 - 12 - 31).saturating_add(47.hours()), |
668 | | /// date!(2021 - 01 - 01) |
669 | | /// ); |
670 | | /// ``` |
671 | 0 | pub const fn saturating_add(self, duration: Duration) -> Self { |
672 | 0 | if let Some(datetime) = self.checked_add(duration) { |
673 | 0 | datetime |
674 | 0 | } else if duration.is_negative() { |
675 | 0 | Self::MIN |
676 | | } else { |
677 | 0 | debug_assert!(duration.is_positive()); |
678 | 0 | Self::MAX |
679 | | } |
680 | 0 | } |
681 | | |
682 | | /// Computes `self - duration`, saturating value on overflow. |
683 | | /// |
684 | | /// ``` |
685 | | /// # use time::{Date, ext::NumericalDuration, macros::date}; |
686 | | /// assert_eq!(Date::MAX.saturating_sub((-2).days()), Date::MAX); |
687 | | /// assert_eq!(Date::MIN.saturating_sub(1.days()), Date::MIN); |
688 | | /// assert_eq!( |
689 | | /// date!(2020 - 12 - 31).saturating_sub(2.days()), |
690 | | /// date!(2020 - 12 - 29) |
691 | | /// ); |
692 | | /// ``` |
693 | | /// |
694 | | /// # Note |
695 | | /// |
696 | | /// This function only takes whole days into account. |
697 | | /// |
698 | | /// ``` |
699 | | /// # use time::{ext::NumericalDuration, macros::date}; |
700 | | /// assert_eq!( |
701 | | /// date!(2020 - 12 - 31).saturating_sub(23.hours()), |
702 | | /// date!(2020 - 12 - 31) |
703 | | /// ); |
704 | | /// assert_eq!( |
705 | | /// date!(2020 - 12 - 31).saturating_sub(47.hours()), |
706 | | /// date!(2020 - 12 - 30) |
707 | | /// ); |
708 | | /// ``` |
709 | 0 | pub const fn saturating_sub(self, duration: Duration) -> Self { |
710 | 0 | if let Some(datetime) = self.checked_sub(duration) { |
711 | 0 | datetime |
712 | 0 | } else if duration.is_negative() { |
713 | 0 | Self::MAX |
714 | | } else { |
715 | 0 | debug_assert!(duration.is_positive()); |
716 | 0 | Self::MIN |
717 | | } |
718 | 0 | } |
719 | | // region: saturating arithmetic |
720 | | |
721 | | // region: replacement |
722 | | /// Replace the year. The month and day will be unchanged. |
723 | | /// |
724 | | /// ```rust |
725 | | /// # use time::macros::date; |
726 | | /// assert_eq!( |
727 | | /// date!(2022 - 02 - 18).replace_year(2019), |
728 | | /// Ok(date!(2019 - 02 - 18)) |
729 | | /// ); |
730 | | /// assert!(date!(2022 - 02 - 18).replace_year(-1_000_000_000).is_err()); // -1_000_000_000 isn't a valid year |
731 | | /// assert!(date!(2022 - 02 - 18).replace_year(1_000_000_000).is_err()); // 1_000_000_000 isn't a valid year |
732 | | /// ``` |
733 | | #[must_use = "This method does not mutate the original `Date`."] |
734 | 0 | pub const fn replace_year(self, year: i32) -> Result<Self, error::ComponentRange> { |
735 | 0 | ensure_value_in_range!(year in MIN_YEAR => MAX_YEAR); |
736 | | |
737 | 0 | let ordinal = self.ordinal(); |
738 | 0 |
|
739 | 0 | // Dates in January and February are unaffected by leap years. |
740 | 0 | if ordinal <= 59 { |
741 | 0 | return Ok(Self::__from_ordinal_date_unchecked(year, ordinal)); |
742 | 0 | } |
743 | 0 |
|
744 | 0 | match (is_leap_year(self.year()), is_leap_year(year)) { |
745 | 0 | (false, false) | (true, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal)), |
746 | | // February 29 does not exist in common years. |
747 | 0 | (true, false) if ordinal == 60 => Err(error::ComponentRange { |
748 | 0 | name: "day", |
749 | 0 | value: 29, |
750 | 0 | minimum: 1, |
751 | 0 | maximum: 28, |
752 | 0 | conditional_range: true, |
753 | 0 | }), |
754 | | // We're going from a common year to a leap year. Shift dates in March and later by |
755 | | // one day. |
756 | 0 | (false, true) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal + 1)), |
757 | | // We're going from a leap year to a common year. Shift dates in January and |
758 | | // February by one day. |
759 | 0 | (true, false) => Ok(Self::__from_ordinal_date_unchecked(year, ordinal - 1)), |
760 | | } |
761 | 0 | } |
762 | | |
763 | | /// Replace the month of the year. |
764 | | /// |
765 | | /// ```rust |
766 | | /// # use time::macros::date; |
767 | | /// # use time::Month; |
768 | | /// assert_eq!( |
769 | | /// date!(2022 - 02 - 18).replace_month(Month::January), |
770 | | /// Ok(date!(2022 - 01 - 18)) |
771 | | /// ); |
772 | | /// assert!( |
773 | | /// date!(2022 - 01 - 30) |
774 | | /// .replace_month(Month::February) |
775 | | /// .is_err() |
776 | | /// ); // 30 isn't a valid day in February |
777 | | /// ``` |
778 | | #[must_use = "This method does not mutate the original `Date`."] |
779 | 0 | pub const fn replace_month(self, month: Month) -> Result<Self, error::ComponentRange> { |
780 | 0 | let (year, _, day) = self.to_calendar_date(); |
781 | 0 | Self::from_calendar_date(year, month, day) |
782 | 0 | } |
783 | | |
784 | | /// Replace the day of the month. |
785 | | /// |
786 | | /// ```rust |
787 | | /// # use time::macros::date; |
788 | | /// assert_eq!( |
789 | | /// date!(2022 - 02 - 18).replace_day(1), |
790 | | /// Ok(date!(2022 - 02 - 01)) |
791 | | /// ); |
792 | | /// assert!(date!(2022 - 02 - 18).replace_day(0).is_err()); // 0 isn't a valid day |
793 | | /// assert!(date!(2022 - 02 - 18).replace_day(30).is_err()); // 30 isn't a valid day in February |
794 | | /// ``` |
795 | | #[must_use = "This method does not mutate the original `Date`."] |
796 | 0 | pub const fn replace_day(self, day: u8) -> Result<Self, error::ComponentRange> { |
797 | 0 | // Days 1-28 are present in every month, so we can skip checking. |
798 | 0 | if day == 0 || day >= 29 { |
799 | 0 | ensure_value_in_range!( |
800 | 0 | day conditionally in 1 => days_in_year_month(self.year(), self.month()) |
801 | 0 | ); |
802 | 0 | } |
803 | | |
804 | 0 | Ok(Self::__from_ordinal_date_unchecked( |
805 | 0 | self.year(), |
806 | 0 | (self.ordinal() as i16 - self.day() as i16 + day as i16) as _, |
807 | 0 | )) |
808 | 0 | } |
809 | | // endregion replacement |
810 | | } |
811 | | |
812 | | // region: attach time |
813 | | /// Methods to add a [`Time`] component, resulting in a [`PrimitiveDateTime`]. |
814 | | impl Date { |
815 | | /// Create a [`PrimitiveDateTime`] using the existing date. The [`Time`] component will be set |
816 | | /// to midnight. |
817 | | /// |
818 | | /// ```rust |
819 | | /// # use time::macros::{date, datetime}; |
820 | | /// assert_eq!(date!(1970-01-01).midnight(), datetime!(1970-01-01 0:00)); |
821 | | /// ``` |
822 | 0 | pub const fn midnight(self) -> PrimitiveDateTime { |
823 | 0 | PrimitiveDateTime::new(self, Time::MIDNIGHT) |
824 | 0 | } |
825 | | |
826 | | /// Create a [`PrimitiveDateTime`] using the existing date and the provided [`Time`]. |
827 | | /// |
828 | | /// ```rust |
829 | | /// # use time::macros::{date, datetime, time}; |
830 | | /// assert_eq!( |
831 | | /// date!(1970-01-01).with_time(time!(0:00)), |
832 | | /// datetime!(1970-01-01 0:00), |
833 | | /// ); |
834 | | /// ``` |
835 | 6.39k | pub const fn with_time(self, time: Time) -> PrimitiveDateTime { |
836 | 6.39k | PrimitiveDateTime::new(self, time) |
837 | 6.39k | } |
838 | | |
839 | | /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. |
840 | | /// |
841 | | /// ```rust |
842 | | /// # use time::macros::date; |
843 | | /// assert!(date!(1970 - 01 - 01).with_hms(0, 0, 0).is_ok()); |
844 | | /// assert!(date!(1970 - 01 - 01).with_hms(24, 0, 0).is_err()); |
845 | | /// ``` |
846 | 0 | pub const fn with_hms( |
847 | 0 | self, |
848 | 0 | hour: u8, |
849 | 0 | minute: u8, |
850 | 0 | second: u8, |
851 | 0 | ) -> Result<PrimitiveDateTime, error::ComponentRange> { |
852 | 0 | Ok(PrimitiveDateTime::new( |
853 | 0 | self, |
854 | 0 | const_try!(Time::from_hms(hour, minute, second)), |
855 | | )) |
856 | 0 | } |
857 | | |
858 | | /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. |
859 | | /// |
860 | | /// ```rust |
861 | | /// # use time::macros::date; |
862 | | /// assert!(date!(1970 - 01 - 01).with_hms_milli(0, 0, 0, 0).is_ok()); |
863 | | /// assert!(date!(1970 - 01 - 01).with_hms_milli(24, 0, 0, 0).is_err()); |
864 | | /// ``` |
865 | 0 | pub const fn with_hms_milli( |
866 | 0 | self, |
867 | 0 | hour: u8, |
868 | 0 | minute: u8, |
869 | 0 | second: u8, |
870 | 0 | millisecond: u16, |
871 | 0 | ) -> Result<PrimitiveDateTime, error::ComponentRange> { |
872 | 0 | Ok(PrimitiveDateTime::new( |
873 | 0 | self, |
874 | 0 | const_try!(Time::from_hms_milli(hour, minute, second, millisecond)), |
875 | | )) |
876 | 0 | } |
877 | | |
878 | | /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. |
879 | | /// |
880 | | /// ```rust |
881 | | /// # use time::macros::date; |
882 | | /// assert!(date!(1970 - 01 - 01).with_hms_micro(0, 0, 0, 0).is_ok()); |
883 | | /// assert!(date!(1970 - 01 - 01).with_hms_micro(24, 0, 0, 0).is_err()); |
884 | | /// ``` |
885 | 0 | pub const fn with_hms_micro( |
886 | 0 | self, |
887 | 0 | hour: u8, |
888 | 0 | minute: u8, |
889 | 0 | second: u8, |
890 | 0 | microsecond: u32, |
891 | 0 | ) -> Result<PrimitiveDateTime, error::ComponentRange> { |
892 | 0 | Ok(PrimitiveDateTime::new( |
893 | 0 | self, |
894 | 0 | const_try!(Time::from_hms_micro(hour, minute, second, microsecond)), |
895 | | )) |
896 | 0 | } |
897 | | |
898 | | /// Attempt to create a [`PrimitiveDateTime`] using the existing date and the provided time. |
899 | | /// |
900 | | /// ```rust |
901 | | /// # use time::macros::date; |
902 | | /// assert!(date!(1970 - 01 - 01).with_hms_nano(0, 0, 0, 0).is_ok()); |
903 | | /// assert!(date!(1970 - 01 - 01).with_hms_nano(24, 0, 0, 0).is_err()); |
904 | | /// ``` |
905 | 0 | pub const fn with_hms_nano( |
906 | 0 | self, |
907 | 0 | hour: u8, |
908 | 0 | minute: u8, |
909 | 0 | second: u8, |
910 | 0 | nanosecond: u32, |
911 | 0 | ) -> Result<PrimitiveDateTime, error::ComponentRange> { |
912 | 0 | Ok(PrimitiveDateTime::new( |
913 | 0 | self, |
914 | 0 | const_try!(Time::from_hms_nano(hour, minute, second, nanosecond)), |
915 | | )) |
916 | 0 | } |
917 | | } |
918 | | // endregion attach time |
919 | | |
920 | | // region: formatting & parsing |
921 | | #[cfg(feature = "formatting")] |
922 | | impl Date { |
923 | | /// Format the `Date` using the provided [format description](crate::format_description). |
924 | 0 | pub fn format_into( |
925 | 0 | self, |
926 | 0 | output: &mut impl io::Write, |
927 | 0 | format: &(impl Formattable + ?Sized), |
928 | 0 | ) -> Result<usize, error::Format> { |
929 | 0 | format.format_into(output, Some(self), None, None) |
930 | 0 | } |
931 | | |
932 | | /// Format the `Date` using the provided [format description](crate::format_description). |
933 | | /// |
934 | | /// ```rust |
935 | | /// # use time::{format_description, macros::date}; |
936 | | /// let format = format_description::parse("[year]-[month]-[day]")?; |
937 | | /// assert_eq!(date!(2020 - 01 - 02).format(&format)?, "2020-01-02"); |
938 | | /// # Ok::<_, time::Error>(()) |
939 | | /// ``` |
940 | 0 | pub fn format(self, format: &(impl Formattable + ?Sized)) -> Result<String, error::Format> { |
941 | 0 | format.format(Some(self), None, None) |
942 | 0 | } |
943 | | } |
944 | | |
945 | | #[cfg(feature = "parsing")] |
946 | | impl Date { |
947 | | /// Parse a `Date` from the input using the provided [format |
948 | | /// description](crate::format_description). |
949 | | /// |
950 | | /// ```rust |
951 | | /// # use time::{format_description, macros::date, Date}; |
952 | | /// let format = format_description::parse("[year]-[month]-[day]")?; |
953 | | /// assert_eq!(Date::parse("2020-01-02", &format)?, date!(2020 - 01 - 02)); |
954 | | /// # Ok::<_, time::Error>(()) |
955 | | /// ``` |
956 | 0 | pub fn parse( |
957 | 0 | input: &str, |
958 | 0 | description: &(impl Parsable + ?Sized), |
959 | 0 | ) -> Result<Self, error::Parse> { |
960 | 0 | description.parse_date(input.as_bytes()) |
961 | 0 | } |
962 | | } |
963 | | |
964 | | impl fmt::Display for Date { |
965 | 0 | fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { |
966 | 0 | if cfg!(feature = "large-dates") && self.year().abs() >= 10_000 { |
967 | 0 | write!( |
968 | 0 | f, |
969 | 0 | "{:+}-{:02}-{:02}", |
970 | 0 | self.year(), |
971 | 0 | self.month() as u8, |
972 | 0 | self.day() |
973 | 0 | ) |
974 | | } else { |
975 | 0 | write!( |
976 | 0 | f, |
977 | 0 | "{:0width$}-{:02}-{:02}", |
978 | 0 | self.year(), |
979 | 0 | self.month() as u8, |
980 | 0 | self.day(), |
981 | 0 | width = 4 + (self.year() < 0) as usize |
982 | 0 | ) |
983 | | } |
984 | 0 | } |
985 | | } |
986 | | // endregion formatting & parsing |
987 | | |
988 | | // region: trait impls |
989 | | impl Add<Duration> for Date { |
990 | | type Output = Self; |
991 | | |
992 | 0 | fn add(self, duration: Duration) -> Self::Output { |
993 | 0 | self.checked_add(duration) |
994 | 0 | .expect("overflow adding duration to date") |
995 | 0 | } |
996 | | } |
997 | | |
998 | | impl Add<StdDuration> for Date { |
999 | | type Output = Self; |
1000 | | |
1001 | 0 | fn add(self, duration: StdDuration) -> Self::Output { |
1002 | 0 | Self::from_julian_day(self.to_julian_day() + (duration.as_secs() / 86_400) as i32) |
1003 | 0 | .expect("overflow adding duration to date") |
1004 | 0 | } |
1005 | | } |
1006 | | |
1007 | | impl_add_assign!(Date: Duration, StdDuration); |
1008 | | |
1009 | | impl Sub<Duration> for Date { |
1010 | | type Output = Self; |
1011 | | |
1012 | 0 | fn sub(self, duration: Duration) -> Self::Output { |
1013 | 0 | self.checked_sub(duration) |
1014 | 0 | .expect("overflow subtracting duration from date") |
1015 | 0 | } |
1016 | | } |
1017 | | |
1018 | | impl Sub<StdDuration> for Date { |
1019 | | type Output = Self; |
1020 | | |
1021 | 0 | fn sub(self, duration: StdDuration) -> Self::Output { |
1022 | 0 | Self::from_julian_day(self.to_julian_day() - (duration.as_secs() / 86_400) as i32) |
1023 | 0 | .expect("overflow subtracting duration from date") |
1024 | 0 | } |
1025 | | } |
1026 | | |
1027 | | impl_sub_assign!(Date: Duration, StdDuration); |
1028 | | |
1029 | | impl Sub for Date { |
1030 | | type Output = Duration; |
1031 | | |
1032 | 0 | fn sub(self, other: Self) -> Self::Output { |
1033 | 0 | Duration::days((self.to_julian_day() - other.to_julian_day()) as _) |
1034 | 0 | } |
1035 | | } |
1036 | | // endregion trait impls |