Generate a sequence of DateOnly in F#

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

Leave a comment

Your email address will not be published. Required fields are marked *