Loop over date range
Asked Answered
B

3

11

In Python3, I can loop over a range of dates like this

import datetime

dt0 = datetime.datetime(2017, 1, 1, 0, 0, 0)
dt1 = datetime.datetime(2017, 1, 5, 0, 0, 0)
dt = dt0
while dt <= dt1:
    print(dt.strftime("%Y-%m-%d %H:%M:%S"))
    dt += datetime.timedelta(days=1)

Is there a similar way to loop over dates in Rust? I know that I could write a nested loop over the months then the days of the month. Like this:

let days = [1, 2, 3, 4, 5, 6, 7,
            8, 9, 10, 11, 12, 13, 14, 
            15, 16, 17, 18, 19, 20, 21,
            22, 23, 24, 25, 26, 27, 28, 29, 30, 31];
let months = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12];
let months_30_days = [4, 6, 9, 11];

for month in months.iter() {
    for day in days.iter() {
        if month == &2 {
            if is_leap_year(year) {
                if day > &29 {
                    continue;
                }
            } else if day > &28 {
                continue;
            }
        } else if months_30_days.contains(&month) && day > &30 {
            continue;
        }

        print!("{:04}-{:02}-{:02} ", year, month, day);
    }
}

fn is_leap_year(year: i32) -> bool {
    if year % 100 == 0 {
        return year % 400 == 0;
    } else {
        return year % 4 == 0;
    }
}

Is there a more Rustic way to do it?

Bolick answered 16/1, 2017 at 14:59 Comment(3)
If you have code that is working but you want it to be improved, that's a better question for Code Review.SE.Meacham
You seem to have missed the outer year loop.Antihistamine
This is a snippet. The year was declared earlier in the main function. But you are right, a year loop could be added as an outer loop if the date range included several years.Bolick
P
25

You could use the chrono crate for that:

extern crate chrono; // 0.4.6

use chrono::{Duration, TimeZone, Utc};

fn main() {
    let dt0 = Utc.ymd(2017, 1, 1);
    let dt1 = Utc.ymd(2017, 1, 5);

    let mut dt = dt0;
    while dt <= dt1 {
        println!("{:?}", dt);
        dt += Duration::days(1);
    }
}

This can also be wrapped into an iterator:

extern crate chrono; // 0.4.6

use chrono::{Date, Duration, TimeZone, Utc};
use std::mem;

struct DateRange(Date<Utc>, Date<Utc>);

impl Iterator for DateRange {
    type Item = Date<Utc>;
    fn next(&mut self) -> Option<Self::Item> {
        if self.0 <= self.1 {
            let next = self.0 + Duration::days(1);
            Some(mem::replace(&mut self.0, next))
        } else {
            None
        }
    }
}

fn main() {
    let dt0 = Utc.ymd(2017, 1, 1);
    let dt1 = Utc.ymd(2017, 1, 5);

    for dt in DateRange(dt0, dt1) {
        println!("{:?}", dt);
    }
}
Peshitta answered 16/1, 2017 at 16:32 Comment(2)
mstlr This works very well. I did not know how to add a day to a datetime. I still don't see what the ".expect" does but it won't run without it.Bolick
@joeschmoe2 expect has exactly the same behaviour as unwrap, except that it also lets you specify a custom message if it panics. It's necessary because checked_add returns an Option<UTC> but the value is assigned to dt which is a plain UTC.Itch
C
3

Using crate time:

use time;


/// An iterator between two `[time::Date]` in increments
pub struct DateIter {
    pub from: time::Date,
    pub to: time::Date,
    pub increment: time::Duration,
}

impl Iterator for DateIter {
    type Item = time::Date;

    fn next(&mut self) -> Option<Self::Item> {
        if self.from >= self.to {
            return None;
        }
        let maybe_next = self.from.saturating_add(self.increment);
        self.from = maybe_next;
        (maybe_next < self.to).then_some(maybe_next)
    }
}
Carlynne answered 19/11, 2023 at 3:35 Comment(0)
A
-2

There might be a crate that already provides such a functionality, but if you would like to implement this on your own, you could introduce a new data type and implement Iterator - that would be the Rust-y way to do it.

struct MyDate {
    year: usize, // or allow negatives for B.C.
    month: u8, // or a dedicated Month type limited to 12
    day: u8  // or a dedicated Day type limited to 31
}

impl Iterator for Date {
    type Item = Date;

    fn next(&mut self) -> Option<Date> {
        // conditions for incrementing day, month and year
    }
}

Then you would be able to increment it in a loop using next().

Antihistamine answered 16/1, 2017 at 15:20 Comment(1)
Without the code to do that, your answer is worth basically nothing (and even with it not much).Tango

© 2022 - 2024 — McMap. All rights reserved.