1//! Date and time types unconcerned with timezones.
2//!
3//! They are primarily building blocks for other types
4//! (e.g. [`TimeZone`](../offset/trait.TimeZone.html)),
5//! but can be also used for the simpler date and time handling.
6
7use core::ops::RangeInclusive;
8
9use crate::expect;
10use crate::Weekday;
11
12pub(crate) mod date;
13pub(crate) mod datetime;
14mod internals;
15pub(crate) mod isoweek;
16pub(crate) mod time;
17
18pub use self::date::{NaiveDate, NaiveDateDaysIterator, NaiveDateWeeksIterator};
19#[allow(deprecated)]
20pub use self::date::{MAX_DATE, MIN_DATE};
21#[cfg(feature = "rustc-serialize")]
22#[allow(deprecated)]
23pub use self::datetime::rustc_serialize::TsSeconds;
24#[allow(deprecated)]
25pub use self::datetime::{NaiveDateTime, MAX_DATETIME, MIN_DATETIME};
26pub use self::isoweek::IsoWeek;
27pub use self::time::NaiveTime;
28
29#[cfg(feature = "__internal_bench")]
30#[doc(hidden)]
31pub use self::internals::YearFlags as __BenchYearFlags;
32
33/// A week represented by a [`NaiveDate`] and a [`Weekday`] which is the first
34/// day of the week.
35#[derive(Debug)]
36pub struct NaiveWeek {
37 date: NaiveDate,
38 start: Weekday,
39}
40
41impl NaiveWeek {
42 /// Create a new `NaiveWeek`
43 pub(crate) const fn new(date: NaiveDate, start: Weekday) -> Self {
44 Self { date, start }
45 }
46
47 /// Returns a date representing the first day of the week.
48 ///
49 /// # Panics
50 ///
51 /// Panics if the first day of the week happens to fall just out of range of `NaiveDate`
52 /// (more than ca. 262,000 years away from common era).
53 ///
54 /// # Examples
55 ///
56 /// ```
57 /// use chrono::{NaiveDate, Weekday};
58 ///
59 /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
60 /// let week = date.week(Weekday::Mon);
61 /// assert!(week.first_day() <= date);
62 /// ```
63 #[inline]
64 #[must_use]
65 pub const fn first_day(&self) -> NaiveDate {
66 let start = self.start.num_days_from_monday() as i32;
67 let ref_day = self.date.weekday().num_days_from_monday() as i32;
68 // Calculate the number of days to subtract from `self.date`.
69 // Do not construct an intermediate date beyond `self.date`, because that may be out of
70 // range if `date` is close to `NaiveDate::MAX`.
71 let days = start - ref_day - if start > ref_day { 7 } else { 0 };
72 expect!(self.date.add_days(days), "first weekday out of range for `NaiveDate`")
73 }
74
75 /// Returns a date representing the last day of the week.
76 ///
77 /// # Panics
78 ///
79 /// Panics if the last day of the week happens to fall just out of range of `NaiveDate`
80 /// (more than ca. 262,000 years away from common era).
81 ///
82 /// # Examples
83 ///
84 /// ```
85 /// use chrono::{NaiveDate, Weekday};
86 ///
87 /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
88 /// let week = date.week(Weekday::Mon);
89 /// assert!(week.last_day() >= date);
90 /// ```
91 #[inline]
92 #[must_use]
93 pub const fn last_day(&self) -> NaiveDate {
94 let end = self.start.pred().num_days_from_monday() as i32;
95 let ref_day = self.date.weekday().num_days_from_monday() as i32;
96 // Calculate the number of days to add to `self.date`.
97 // Do not construct an intermediate date before `self.date` (like with `first_day()`),
98 // because that may be out of range if `date` is close to `NaiveDate::MIN`.
99 let days = end - ref_day + if end < ref_day { 7 } else { 0 };
100 expect!(self.date.add_days(days), "last weekday out of range for `NaiveDate`")
101 }
102
103 /// Returns a [`RangeInclusive<T>`] representing the whole week bounded by
104 /// [first_day](NaiveWeek::first_day) and [last_day](NaiveWeek::last_day) functions.
105 ///
106 /// # Panics
107 ///
108 /// Panics if the either the first or last day of the week happens to fall just out of range of
109 /// `NaiveDate` (more than ca. 262,000 years away from common era).
110 ///
111 /// # Examples
112 ///
113 /// ```
114 /// use chrono::{NaiveDate, Weekday};
115 ///
116 /// let date = NaiveDate::from_ymd_opt(2022, 4, 18).unwrap();
117 /// let week = date.week(Weekday::Mon);
118 /// let days = week.days();
119 /// assert!(days.contains(&date));
120 /// ```
121 #[inline]
122 #[must_use]
123 pub const fn days(&self) -> RangeInclusive<NaiveDate> {
124 self.first_day()..=self.last_day()
125 }
126}
127
128/// A duration in calendar days.
129///
130/// This is useful because when using `TimeDelta` it is possible that adding `TimeDelta::days(1)`
131/// doesn't increment the day value as expected due to it being a fixed number of seconds. This
132/// difference applies only when dealing with `DateTime<TimeZone>` data types and in other cases
133/// `TimeDelta::days(n)` and `Days::new(n)` are equivalent.
134#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, PartialOrd, Ord)]
135pub struct Days(pub(crate) u64);
136
137impl Days {
138 /// Construct a new `Days` from a number of days
139 pub const fn new(num: u64) -> Self {
140 Self(num)
141 }
142}
143
144/// Serialization/Deserialization of naive types in alternate formats
145///
146/// The various modules in here are intended to be used with serde's [`with`
147/// annotation][1] to serialize as something other than the default [RFC
148/// 3339][2] format.
149///
150/// [1]: https://serde.rs/attributes.html#field-attributes
151/// [2]: https://tools.ietf.org/html/rfc3339
152#[cfg(feature = "serde")]
153pub mod serde {
154 pub use super::datetime::serde::*;
155}
156
157#[cfg(test)]
158mod test {
159 use crate::{NaiveDate, Weekday};
160 #[test]
161 fn test_naiveweek() {
162 let date = NaiveDate::from_ymd_opt(2022, 5, 18).unwrap();
163 let asserts = [
164 (Weekday::Mon, "Mon 2022-05-16", "Sun 2022-05-22"),
165 (Weekday::Tue, "Tue 2022-05-17", "Mon 2022-05-23"),
166 (Weekday::Wed, "Wed 2022-05-18", "Tue 2022-05-24"),
167 (Weekday::Thu, "Thu 2022-05-12", "Wed 2022-05-18"),
168 (Weekday::Fri, "Fri 2022-05-13", "Thu 2022-05-19"),
169 (Weekday::Sat, "Sat 2022-05-14", "Fri 2022-05-20"),
170 (Weekday::Sun, "Sun 2022-05-15", "Sat 2022-05-21"),
171 ];
172 for (start, first_day, last_day) in asserts {
173 let week = date.week(start);
174 let days = week.days();
175 assert_eq!(Ok(week.first_day()), NaiveDate::parse_from_str(first_day, "%a %Y-%m-%d"));
176 assert_eq!(Ok(week.last_day()), NaiveDate::parse_from_str(last_day, "%a %Y-%m-%d"));
177 assert!(days.contains(&date));
178 }
179 }
180
181 #[test]
182 fn test_naiveweek_min_max() {
183 let date_max = NaiveDate::MAX;
184 assert!(date_max.week(Weekday::Mon).first_day() <= date_max);
185 let date_min = NaiveDate::MIN;
186 assert!(date_min.week(Weekday::Mon).last_day() >= date_min);
187 }
188}
189