chrono/traits.rs
1use crate::{IsoWeek, Weekday};
2
3/// The common set of methods for date component.
4///
5/// Methods such as [`year`], [`month`], [`day`] and [`weekday`] can be used to get basic
6/// information about the date.
7///
8/// The `with_*` methods can change the date.
9///
10/// # Warning
11///
12/// The `with_*` methods can be convenient to change a single component of a date, but they must be
13/// used with some care. Examples to watch out for:
14///
15/// - [`with_year`] changes the year component of a year-month-day value. Don't use this method if
16/// you want the ordinal to stay the same after changing the year, of if you want the week and
17/// weekday values to stay the same.
18/// - Don't combine two `with_*` methods to change two components of the date. For example to
19/// change both the year and month components of a date. This could fail because an intermediate
20/// value does not exist, while the final date would be valid.
21///
22/// For more complex changes to a date, it is best to use the methods on [`NaiveDate`] to create a
23/// new value instead of altering an existing date.
24///
25/// [`year`]: Datelike::year
26/// [`month`]: Datelike::month
27/// [`day`]: Datelike::day
28/// [`weekday`]: Datelike::weekday
29/// [`with_year`]: Datelike::with_year
30/// [`NaiveDate`]: crate::NaiveDate
31pub trait Datelike: Sized {
32 /// Returns the year number in the [calendar date](./naive/struct.NaiveDate.html#calendar-date).
33 fn year(&self) -> i32;
34
35 /// Returns the absolute year number starting from 1 with a boolean flag,
36 /// which is false when the year predates the epoch (BCE/BC) and true otherwise (CE/AD).
37 #[inline]
38 fn year_ce(&self) -> (bool, u32) {
39 let year = self.year();
40 if year < 1 { (false, (1 - year) as u32) } else { (true, year as u32) }
41 }
42
43 /// Returns the quarter number starting from 1.
44 ///
45 /// The return value ranges from 1 to 4.
46 #[inline]
47 fn quarter(&self) -> u32 {
48 (self.month() - 1).div_euclid(3) + 1
49 }
50
51 /// Returns the month number starting from 1.
52 ///
53 /// The return value ranges from 1 to 12.
54 fn month(&self) -> u32;
55
56 /// Returns the month number starting from 0.
57 ///
58 /// The return value ranges from 0 to 11.
59 fn month0(&self) -> u32;
60
61 /// Returns the day of month starting from 1.
62 ///
63 /// The return value ranges from 1 to 31. (The last day of month differs by months.)
64 fn day(&self) -> u32;
65
66 /// Returns the day of month starting from 0.
67 ///
68 /// The return value ranges from 0 to 30. (The last day of month differs by months.)
69 fn day0(&self) -> u32;
70
71 /// Returns the day of year starting from 1.
72 ///
73 /// The return value ranges from 1 to 366. (The last day of year differs by years.)
74 fn ordinal(&self) -> u32;
75
76 /// Returns the day of year starting from 0.
77 ///
78 /// The return value ranges from 0 to 365. (The last day of year differs by years.)
79 fn ordinal0(&self) -> u32;
80
81 /// Returns the day of week.
82 fn weekday(&self) -> Weekday;
83
84 /// Returns the ISO week.
85 fn iso_week(&self) -> IsoWeek;
86
87 /// Makes a new value with the year number changed, while keeping the same month and day.
88 ///
89 /// This method assumes you want to work on the date as a year-month-day value. Don't use it if
90 /// you want the ordinal to stay the same after changing the year, of if you want the week and
91 /// weekday values to stay the same.
92 ///
93 /// # Errors
94 ///
95 /// Returns `None` when:
96 ///
97 /// - The resulting date does not exist (February 29 in a non-leap year).
98 /// - The year is out of range for [`NaiveDate`].
99 /// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
100 /// transition such as from DST to standard time.
101 ///
102 /// [`NaiveDate`]: crate::NaiveDate
103 /// [`DateTime<Tz>`]: crate::DateTime
104 ///
105 /// # Examples
106 ///
107 /// ```
108 /// use chrono::{Datelike, NaiveDate};
109 ///
110 /// assert_eq!(
111 /// NaiveDate::from_ymd_opt(2020, 5, 13).unwrap().with_year(2023).unwrap(),
112 /// NaiveDate::from_ymd_opt(2023, 5, 13).unwrap()
113 /// );
114 /// // Resulting date 2023-02-29 does not exist:
115 /// assert!(NaiveDate::from_ymd_opt(2020, 2, 29).unwrap().with_year(2023).is_none());
116 ///
117 /// // Don't use `with_year` if you want the ordinal date to stay the same:
118 /// assert_ne!(
119 /// NaiveDate::from_yo_opt(2020, 100).unwrap().with_year(2023).unwrap(),
120 /// NaiveDate::from_yo_opt(2023, 100).unwrap() // result is 2023-101
121 /// );
122 /// ```
123 fn with_year(&self, year: i32) -> Option<Self>;
124
125 /// Makes a new value with the month number (starting from 1) changed.
126 ///
127 /// # Errors
128 ///
129 /// Returns `None` when:
130 ///
131 /// - The resulting date does not exist (for example `month(4)` when day of the month is 31).
132 /// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
133 /// transition such as from DST to standard time.
134 /// - The value for `month` is out of range.
135 ///
136 /// [`DateTime<Tz>`]: crate::DateTime
137 ///
138 /// # Examples
139 ///
140 /// ```
141 /// use chrono::{Datelike, NaiveDate};
142 ///
143 /// assert_eq!(
144 /// NaiveDate::from_ymd_opt(2023, 5, 12).unwrap().with_month(9).unwrap(),
145 /// NaiveDate::from_ymd_opt(2023, 9, 12).unwrap()
146 /// );
147 /// // Resulting date 2023-09-31 does not exist:
148 /// assert!(NaiveDate::from_ymd_opt(2023, 5, 31).unwrap().with_month(9).is_none());
149 /// ```
150 ///
151 /// Don't combine multiple `Datelike::with_*` methods. The intermediate value may not exist.
152 /// ```
153 /// use chrono::{Datelike, NaiveDate};
154 ///
155 /// fn with_year_month(date: NaiveDate, year: i32, month: u32) -> Option<NaiveDate> {
156 /// date.with_year(year)?.with_month(month)
157 /// }
158 /// let d = NaiveDate::from_ymd_opt(2020, 2, 29).unwrap();
159 /// assert!(with_year_month(d, 2019, 1).is_none()); // fails because of invalid intermediate value
160 ///
161 /// // Correct version:
162 /// fn with_year_month_fixed(date: NaiveDate, year: i32, month: u32) -> Option<NaiveDate> {
163 /// NaiveDate::from_ymd_opt(year, month, date.day())
164 /// }
165 /// let d = NaiveDate::from_ymd_opt(2020, 2, 29).unwrap();
166 /// assert_eq!(with_year_month_fixed(d, 2019, 1), NaiveDate::from_ymd_opt(2019, 1, 29));
167 /// ```
168 fn with_month(&self, month: u32) -> Option<Self>;
169
170 /// Makes a new value with the month number (starting from 0) changed.
171 ///
172 /// # Errors
173 ///
174 /// Returns `None` when:
175 ///
176 /// - The resulting date does not exist (for example `month0(3)` when day of the month is 31).
177 /// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
178 /// transition such as from DST to standard time.
179 /// - The value for `month0` is out of range.
180 ///
181 /// [`DateTime<Tz>`]: crate::DateTime
182 fn with_month0(&self, month0: u32) -> Option<Self>;
183
184 /// Makes a new value with the day of month (starting from 1) changed.
185 ///
186 /// # Errors
187 ///
188 /// Returns `None` when:
189 ///
190 /// - The resulting date does not exist (for example `day(31)` in April).
191 /// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
192 /// transition such as from DST to standard time.
193 /// - The value for `day` is out of range.
194 ///
195 /// [`DateTime<Tz>`]: crate::DateTime
196 fn with_day(&self, day: u32) -> Option<Self>;
197
198 /// Makes a new value with the day of month (starting from 0) changed.
199 ///
200 /// # Errors
201 ///
202 /// Returns `None` when:
203 ///
204 /// - The resulting date does not exist (for example `day0(30)` in April).
205 /// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
206 /// transition such as from DST to standard time.
207 /// - The value for `day0` is out of range.
208 ///
209 /// [`DateTime<Tz>`]: crate::DateTime
210 fn with_day0(&self, day0: u32) -> Option<Self>;
211
212 /// Makes a new value with the day of year (starting from 1) changed.
213 ///
214 /// # Errors
215 ///
216 /// Returns `None` when:
217 ///
218 /// - The resulting date does not exist (`with_ordinal(366)` in a non-leap year).
219 /// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
220 /// transition such as from DST to standard time.
221 /// - The value for `ordinal` is out of range.
222 ///
223 /// [`DateTime<Tz>`]: crate::DateTime
224 fn with_ordinal(&self, ordinal: u32) -> Option<Self>;
225
226 /// Makes a new value with the day of year (starting from 0) changed.
227 ///
228 /// # Errors
229 ///
230 /// Returns `None` when:
231 ///
232 /// - The resulting date does not exist (`with_ordinal0(365)` in a non-leap year).
233 /// - In case of [`DateTime<Tz>`] if the resulting date and time fall within a timezone
234 /// transition such as from DST to standard time.
235 /// - The value for `ordinal0` is out of range.
236 ///
237 /// [`DateTime<Tz>`]: crate::DateTime
238 fn with_ordinal0(&self, ordinal0: u32) -> Option<Self>;
239
240 /// Counts the days in the proleptic Gregorian calendar, with January 1, Year 1 (CE) as day 1.
241 ///
242 /// # Examples
243 ///
244 /// ```
245 /// use chrono::{Datelike, NaiveDate};
246 ///
247 /// assert_eq!(NaiveDate::from_ymd_opt(1970, 1, 1).unwrap().num_days_from_ce(), 719_163);
248 /// assert_eq!(NaiveDate::from_ymd_opt(2, 1, 1).unwrap().num_days_from_ce(), 366);
249 /// assert_eq!(NaiveDate::from_ymd_opt(1, 1, 1).unwrap().num_days_from_ce(), 1);
250 /// assert_eq!(NaiveDate::from_ymd_opt(0, 1, 1).unwrap().num_days_from_ce(), -365);
251 /// ```
252 fn num_days_from_ce(&self) -> i32 {
253 // See test_num_days_from_ce_against_alternative_impl below for a more straightforward
254 // implementation.
255
256 // we know this wouldn't overflow since year is limited to 1/2^13 of i32's full range.
257 let mut year = self.year() - 1;
258 let mut ndays = 0;
259 if year < 0 {
260 let excess = 1 + (-year) / 400;
261 year += excess * 400;
262 ndays -= excess * 146_097;
263 }
264 let div_100 = year / 100;
265 ndays += ((year * 1461) >> 2) - div_100 + (div_100 >> 2);
266 ndays + self.ordinal() as i32
267 }
268}
269
270/// The common set of methods for time component.
271pub trait Timelike: Sized {
272 /// Returns the hour number from 0 to 23.
273 fn hour(&self) -> u32;
274
275 /// Returns the hour number from 1 to 12 with a boolean flag,
276 /// which is false for AM and true for PM.
277 #[inline]
278 fn hour12(&self) -> (bool, u32) {
279 let hour = self.hour();
280 let mut hour12 = hour % 12;
281 if hour12 == 0 {
282 hour12 = 12;
283 }
284 (hour >= 12, hour12)
285 }
286
287 /// Returns the minute number from 0 to 59.
288 fn minute(&self) -> u32;
289
290 /// Returns the second number from 0 to 59.
291 fn second(&self) -> u32;
292
293 /// Returns the number of nanoseconds since the whole non-leap second.
294 /// The range from 1,000,000,000 to 1,999,999,999 represents
295 /// the [leap second](./naive/struct.NaiveTime.html#leap-second-handling).
296 fn nanosecond(&self) -> u32;
297
298 /// Makes a new value with the hour number changed.
299 ///
300 /// Returns `None` when the resulting value would be invalid.
301 fn with_hour(&self, hour: u32) -> Option<Self>;
302
303 /// Makes a new value with the minute number changed.
304 ///
305 /// Returns `None` when the resulting value would be invalid.
306 fn with_minute(&self, min: u32) -> Option<Self>;
307
308 /// Makes a new value with the second number changed.
309 ///
310 /// Returns `None` when the resulting value would be invalid.
311 /// As with the [`second`](#tymethod.second) method,
312 /// the input range is restricted to 0 through 59.
313 fn with_second(&self, sec: u32) -> Option<Self>;
314
315 /// Makes a new value with nanoseconds since the whole non-leap second changed.
316 ///
317 /// Returns `None` when the resulting value would be invalid.
318 /// As with the [`nanosecond`](#tymethod.nanosecond) method,
319 /// the input range can exceed 1,000,000,000 for leap seconds.
320 fn with_nanosecond(&self, nano: u32) -> Option<Self>;
321
322 /// Returns the number of non-leap seconds past the last midnight.
323 ///
324 /// Every value in 00:00:00-23:59:59 maps to an integer in 0-86399.
325 ///
326 /// This method is not intended to provide the real number of seconds since midnight on a given
327 /// day. It does not take things like DST transitions into account.
328 #[inline]
329 fn num_seconds_from_midnight(&self) -> u32 {
330 self.hour() * 3600 + self.minute() * 60 + self.second()
331 }
332}
333
334#[cfg(test)]
335mod tests {
336 use super::Datelike;
337 use crate::{Days, NaiveDate};
338
339 /// Tests `Datelike::num_days_from_ce` against an alternative implementation.
340 ///
341 /// The alternative implementation is not as short as the current one but it is simpler to
342 /// understand, with less unexplained magic constants.
343 #[test]
344 fn test_num_days_from_ce_against_alternative_impl() {
345 /// Returns the number of multiples of `div` in the range `start..end`.
346 ///
347 /// If the range `start..end` is back-to-front, i.e. `start` is greater than `end`, the
348 /// behaviour is defined by the following equation:
349 /// `in_between(start, end, div) == - in_between(end, start, div)`.
350 ///
351 /// When `div` is 1, this is equivalent to `end - start`, i.e. the length of `start..end`.
352 ///
353 /// # Panics
354 ///
355 /// Panics if `div` is not positive.
356 fn in_between(start: i32, end: i32, div: i32) -> i32 {
357 assert!(div > 0, "in_between: nonpositive div = {}", div);
358 let start = (start.div_euclid(div), start.rem_euclid(div));
359 let end = (end.div_euclid(div), end.rem_euclid(div));
360 // The lowest multiple of `div` greater than or equal to `start`, divided.
361 let start = start.0 + (start.1 != 0) as i32;
362 // The lowest multiple of `div` greater than or equal to `end`, divided.
363 let end = end.0 + (end.1 != 0) as i32;
364 end - start
365 }
366
367 /// Alternative implementation to `Datelike::num_days_from_ce`
368 fn num_days_from_ce<Date: Datelike>(date: &Date) -> i32 {
369 let year = date.year();
370 let diff = move |div| in_between(1, year, div);
371 // 365 days a year, one more in leap years. In the gregorian calendar, leap years are all
372 // the multiples of 4 except multiples of 100 but including multiples of 400.
373 date.ordinal() as i32 + 365 * diff(1) + diff(4) - diff(100) + diff(400)
374 }
375
376 for year in NaiveDate::MIN.year()..=NaiveDate::MAX.year() {
377 let jan1_year = NaiveDate::from_ymd_opt(year, 1, 1).unwrap();
378 assert_eq!(
379 jan1_year.num_days_from_ce(),
380 num_days_from_ce(&jan1_year),
381 "on {:?}",
382 jan1_year
383 );
384 let mid_year = jan1_year + Days::new(133);
385 assert_eq!(
386 mid_year.num_days_from_ce(),
387 num_days_from_ce(&mid_year),
388 "on {:?}",
389 mid_year
390 );
391 }
392 }
393}