Fir a hobby project I had to generate a sequence of DateTime (a.k.a. IEnumerable<DateTime>.
Generating a sequence from simple types is easy:
let seqOfInt (start: int) (end: int) =
seq { start .. end }
But we cannot use range operator with DateOnly:
// Not valid code!
let badSeqOfDateTime (startDate: DateOnly) (endDate: DateOnly) =
seq { startDate .. endDate }
In my first attempt at this (and current code as of today), I ended up with a while loop and a mutable:
let getDateSequence (startDate: DateOnly) (endDate: DateOnly) =
seq {
let mutable date = startDate
while (date <= endDate) do
yield date
date <- date.AddDays(1)
}
Then I suddenly remembered the Seq.unfold function, which I haven’t used other than in Advent of Code:
let getDateSequenceUsingUnfoldGenerator (startDate: DateOnly) (endDate: DateOnly) =
let generator state =
if state > endDate then
None
else
Some(state, state.AddDays(1))
startDate |> Seq.unfold generator
The generator take the current state as input, and returns None if the sequence shoud be stopped. Otherwise it returns Some tuple of a new value and the new state.
We can even inline the generator (and use pattern matching instead of if-expression:
let getDateSequenceUsingUnfoldGenerator2 (startDate: DateOnly) (endDate: DateOnly) =
startDate |> Seq.unfold (function
| date when date > endDate -> None
| date -> Some(date, date.AddDays(1)))
Then we can always be “clever”, and use the range operator anyway:
let getDateSequenceClever (startDate: DateOnly) (endDate: DateOnly) =
{ 0 .. (endDate.DayNumber - startDate.DayNumber) }
|> Seq.map startDate.AddDays