diff --git a/CHANGELOG.md b/CHANGELOG.md index 1159d0790a..030158b93e 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,7 +10,11 @@ Versions with only mechanical changes will be omitted from the following list. ## 0.4.14 (unreleased) -## Improvements +### Features + +* Added day and week iterators for `NaiveDate` (@gnzlbg & @robyoung) + +### Improvements * Added MIN and MAX values for `NaiveTime`, `NaiveDateTime` and `DateTime<Utc>`. diff --git a/src/naive/date.rs b/src/naive/date.rs index 4906fbac9c..df7f70c34f 100644 --- a/src/naive/date.rs +++ b/src/naive/date.rs @@ -1048,6 +1048,58 @@ impl NaiveDate { pub fn format<'a>(&self, fmt: &'a str) -> DelayedFormat<StrftimeItems<'a>> { self.format_with_items(StrftimeItems::new(fmt)) } + + /// Returns an iterator that steps by days until the last representable date. + /// + /// # Example + /// + /// ``` + /// # use chrono::NaiveDate; + /// + /// let expected = [ + /// NaiveDate::from_ymd(2016, 2, 27), + /// NaiveDate::from_ymd(2016, 2, 28), + /// NaiveDate::from_ymd(2016, 2, 29), + /// NaiveDate::from_ymd(2016, 3, 1), + /// ]; + /// + /// let mut count = 0; + /// for (idx, d) in NaiveDate::from_ymd(2016, 2, 27).iter_days().take(4).enumerate() { + /// assert_eq!(d, expected[idx]); + /// count += 1; + /// } + /// assert_eq!(count, 4); + /// ``` + #[inline] + pub fn iter_days(&self) -> NaiveDateDaysIterator { + NaiveDateDaysIterator { value: *self } + } + + /// Returns an iterator that steps by weeks until the last representable date. + /// + /// # Example + /// + /// ``` + /// # use chrono::NaiveDate; + /// + /// let expected = [ + /// NaiveDate::from_ymd(2016, 2, 27), + /// NaiveDate::from_ymd(2016, 3, 5), + /// NaiveDate::from_ymd(2016, 3, 12), + /// NaiveDate::from_ymd(2016, 3, 19), + /// ]; + /// + /// let mut count = 0; + /// for (idx, d) in NaiveDate::from_ymd(2016, 2, 27).iter_weeks().take(4).enumerate() { + /// assert_eq!(d, expected[idx]); + /// count += 1; + /// } + /// assert_eq!(count, 4); + /// ``` + #[inline] + pub fn iter_weeks(&self) -> NaiveDateWeeksIterator { + NaiveDateWeeksIterator { value: *self } + } } impl Datelike for NaiveDate { @@ -1511,6 +1563,63 @@ impl Sub<NaiveDate> for NaiveDate { } } +/// Iterator over `NaiveDate` with a step size of one day. +#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] +pub struct NaiveDateDaysIterator { + value: NaiveDate, +} + +impl Iterator for NaiveDateDaysIterator { + type Item = NaiveDate; + + fn next(&mut self) -> Option<Self::Item> { + if self.value == MAX_DATE { + return None; + } + // current < MAX_DATE from here on: + let current = self.value; + // This can't panic because current is < MAX_DATE: + self.value = current.succ(); + Some(current) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let exact_size = MAX_DATE.signed_duration_since(self.value).num_days(); + (exact_size as usize, Some(exact_size as usize)) + } +} + +impl ExactSizeIterator for NaiveDateDaysIterator {} + +#[derive(Debug, Copy, Clone, Hash, PartialEq, PartialOrd, Eq, Ord)] +pub struct NaiveDateWeeksIterator { + value: NaiveDate, +} + +impl Iterator for NaiveDateWeeksIterator { + type Item = NaiveDate; + + fn next(&mut self) -> Option<Self::Item> { + if MAX_DATE - self.value < OldDuration::weeks(1) { + return None; + } + let current = self.value; + self.value = current + OldDuration::weeks(1); + Some(current) + } + + fn size_hint(&self) -> (usize, Option<usize>) { + let exact_size = MAX_DATE.signed_duration_since(self.value).num_weeks(); + (exact_size as usize, Some(exact_size as usize)) + } +} + +impl ExactSizeIterator for NaiveDateWeeksIterator {} + +// TODO: NaiveDateDaysIterator and NaiveDateWeeksIterator should implement FusedIterator, +// TrustedLen, and Step once they becomes stable. +// See: https://github.com/chronotope/chrono/issues/208 + /// The `Debug` output of the naive date `d` is the same as /// [`d.format("%Y-%m-%d")`](../format/strftime/index.html). /// @@ -2270,4 +2379,20 @@ mod tests { "2009,09,01,00,53" ); } + + #[test] + fn test_day_iterator_limit() { + assert_eq!( + NaiveDate::from_ymd(262143, 12, 29).iter_days().take(4).collect::<Vec<_>>().len(), + 2 + ); + } + + #[test] + fn test_week_iterator_limit() { + assert_eq!( + NaiveDate::from_ymd(262143, 12, 12).iter_weeks().take(4).collect::<Vec<_>>().len(), + 2 + ); + } }