Thread overview
Date range iteration
Mar 12, 2018
Jordan Wilson
Mar 12, 2018
Jonathan M Davis
Mar 12, 2018
Jordan Wilson
March 12, 2018
I wanted to iterate through a date range, so I initially tried:
iota(Date(2016,1,1),Date(2018,1,1),dur!"days"(1));

That wouldn't compile, which is fair enough I guess.

So I tried a for loop:
for (auto i = Date(2016,1,1); i < Date(2018,1,1); i+=dur!"days"(1)){}

That seemed to work fine, but I remember reading "for loops are bad" somewhere. So I looked for a range type mechanism, and I think it's this:
foreach (i; Interval!Date (Date(2016,1,1),Date(2018,1,1)).fwdRange ( (a) { return a+dur!"days"(1); })){}

My question is if the third way is the proper way of stepping through a period of time? It just seems quite complicated to me.

Thanks,

Jordan
March 11, 2018
On Monday, March 12, 2018 02:11:49 Jordan Wilson via Digitalmars-d-learn wrote:
> I wanted to iterate through a date range, so I initially tried:
> iota(Date(2016,1,1),Date(2018,1,1),dur!"days"(1));
>
> That wouldn't compile, which is fair enough I guess.

Maybe iota should be made to work, but as present, it basically wants all three of the types it's given to be the same or implicitly convertible to a single type. It can't handle the step being a completely different type.

> So I tried a for loop:
> for (auto i = Date(2016,1,1); i < Date(2018,1,1);
> i+=dur!"days"(1)){}
>
> That seemed to work fine, but I remember reading "for loops are bad" somewhere.

for loops are just fine - especially if you're just going to loop through all the values and do something to them - but if you have a range, it's a lot more flexible.

> So I looked for a range type mechanism, and I
> think it's this:
> foreach (i; Interval!Date
> (Date(2016,1,1),Date(2018,1,1)).fwdRange ( (a) { return
> a+dur!"days"(1); })){}
>
> My question is if the third way is the proper way of stepping through a period of time? It just seems quite complicated to me.

Well, honestly, the range support in std.datetime isn't very good. The core problem that overcomplicates it is that the Interval needs to know which direction you want to iterate in, and then the helper functions tend to need to know it too in order to work properly. Also, the time point type being used also tends to get duplicated a fair bit. So, you end up with annoyingly repetitive code. The helper function intended for this use case is

https://dlang.org/phobos/std_datetime_interval.html#everyDuration

So, you'd get something more like

auto interval = Interval!Date(Date(2016, 1, 1), Date(2018, 1, 1));
auto range = interval.fwdRange(everyDuration!Date(days(1)));

But if you're just going to use the range in a foreach loop, then you might as well just use a for loop. All of this extra machinery only really starts being valuable when you start feeding the ranges into range-based functions. For a simple loop, it's overkill.

- Jonathan M Davis

March 12, 2018
On Monday, 12 March 2018 at 02:49:34 UTC, Jonathan M Davis wrote:
> On Monday, March 12, 2018 02:11:49 Jordan Wilson via Digitalmars-d-learn wrote:
>> [...]
>
> Maybe iota should be made to work, but as present, it basically wants all three of the types it's given to be the same or implicitly convertible to a single type. It can't handle the step being a completely different type.
>
> [...]

Yes I agree, a for loop for simple iteration is fine, but I'm sure there are cases where a date range would be quite useful, so the everyDuration tip is great, thanks!

Jordan