HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

is there a more functionally idiomatic way of generating valid dates in f#?

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
datesmoreidiomaticwaygeneratingvalidtherefunctionally

Problem

open System

type Date = System.DateTime

type SafeDate =
    | WorkingDate of Date
    | InvalidDate of Exception

let dateAttempt (x: int, y:int, z:int) =
    try 
        let returnDate = Date(z,y,x)
        WorkingDate returnDate
    with
        ex -> InvalidDate ex

let rnd = System.Random(4)

let randomBirthYear() =
    rnd.Next(1904,1954)
let randomBirthMonth() =
    rnd.Next(1,13)
let randomBirthDay() =
    rnd.Next(1,32)

let generateDate() =
    (randomBirthDay(), randomBirthMonth(), randomBirthYear()) 
    |> dateAttempt

let chooseValidDate d =
    match d with
    | WorkingDate d -> Some d
    | _ -> None

let sampleDobs = [for i in 1 .. 1000 -> generateDate()]

let cleanDates = Seq.choose chooseValidDate sampleDobs

Solution

One way to work around invalid dates is not to generate them in the first place: find the number of days between the boundary dates, generate a random integer from this range and then add that number of days to the start date:

let generateDate (min : Date) max =
    let totalDays = int (max - min).TotalDays
    let generatedDays = rnd.Next(0, totalDays)
    let generatedDate = min.AddDays(float generatedDays)
    generatedDate

let generateDateByYear minYear maxYear =
    generateDate (Date(minYear, 1, 1)) (Date(maxYear, 1, 1))

let generateBirthDate() = generateDateByYear 1904 1954

let sampleDobs = [for i in 1 .. 1000 -> generateBirthDate()]

Code Snippets

let generateDate (min : Date) max =
    let totalDays = int (max - min).TotalDays
    let generatedDays = rnd.Next(0, totalDays)
    let generatedDate = min.AddDays(float generatedDays)
    generatedDate

let generateDateByYear minYear maxYear =
    generateDate (Date(minYear, 1, 1)) (Date(maxYear, 1, 1))

let generateBirthDate() = generateDateByYear 1904 1954

let sampleDobs = [for i in 1 .. 1000 -> generateBirthDate()]

Context

StackExchange Code Review Q#36521, answer score: 6

Revisions (0)

No revisions yet.