The iterator returned by NaiveDate::iter_days behaves unexpectedly when iterated in reverse. Consider:
#[test]
fn test() {
let start = chrono::NaiveDate::from_ymd_opt(2025, 10, 10).unwrap();
let mut next_seven_rev1: Vec<_> = start.iter_days().take(7).collect();
next_seven_rev1.reverse();
let next_seven_rev2: Vec<_> = start.iter_days().take(7).rev().collect();
assert_eq!(next_seven_rev1, next_seven_rev2);
}
I would expect the assertion here to pass, but it does not:
assertion `left == right` failed
left: [2025-10-16, 2025-10-15, 2025-10-14, 2025-10-13, 2025-10-12, 2025-10-11, 2025-10-10]
right: [-258092-07-28]
My brief analysis of this follows. Take it with a grain of salt though, as I might be misunderstanding exactly how the various iterator traits are expected to interact.
From what I can tell, I think this behaviour is due to NaiveDateDaysIterator implementing DoubleEndedIterator and ExactSizeIterator, which the Take iterator takes advantage of, but:
start.iter_days().len() reports the number of days until NaiveDate::MAX
start.iter_days().next_back() is not NaiveDate::MAX
And in particular, the latter seems to be the source of the issue. Per the docs on DoubleEndedIterator:
It is important to note that both back and forth work on the same range, and do not cross: iteration is over when they meet in the middle.
So start.iter_days().next_back() should be equivalent to start.iter_days().last(), but this is not the case.
If I'm right, then the fix would seem to be to remember the starting date in NaiveDateDaysIterator and wrap around there instead of at NaiveDate::MIN, but that would break any current uses of NaiveDate::iter_days().rev(). So it might also necessitate adding some kind of NaiveDate::iter_days_backwards() API. In any case, this would be a backwards-incompatible change.
The iterator returned by
NaiveDate::iter_daysbehaves unexpectedly when iterated in reverse. Consider:I would expect the assertion here to pass, but it does not:
My brief analysis of this follows. Take it with a grain of salt though, as I might be misunderstanding exactly how the various iterator traits are expected to interact.
From what I can tell, I think this behaviour is due to
NaiveDateDaysIteratorimplementingDoubleEndedIteratorandExactSizeIterator, which theTakeiterator takes advantage of, but:start.iter_days().len()reports the number of days untilNaiveDate::MAXstart.iter_days().next_back()is notNaiveDate::MAXAnd in particular, the latter seems to be the source of the issue. Per the docs on
DoubleEndedIterator:So
start.iter_days().next_back()should be equivalent tostart.iter_days().last(), but this is not the case.If I'm right, then the fix would seem to be to remember the starting date in
NaiveDateDaysIteratorand wrap around there instead of atNaiveDate::MIN, but that would break any current uses ofNaiveDate::iter_days().rev(). So it might also necessitate adding some kind ofNaiveDate::iter_days_backwards()API. In any case, this would be a backwards-incompatible change.