Skip to content

NaiveDateDaysIterator (NaiveDate::iter_days) behaves unexpectedly when reversed #1757

@Deewiant

Description

@Deewiant

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.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions