Skip to content

Commit

Permalink
Improve and extend DateTime/DateTimeOffset generation (#252)
Browse files Browse the repository at this point in the history
* Improve correctness of Gen.dateTime docs

* Add Gen.dateTimeOffset

* Add Gen.dateTimeRanged and Gen.dateTimeOffsetRanged

* Implement dateTime and dateTimeOffset in terms of their ranged variants

* Cleanup

* Fix overflow bug in arithmetic

* Use linear range for offset

* Remove non-ranged dateTime/dateTimeOffset

* Update src/Hedgehog/Gen.fs

Co-authored-by: Nikos Baxevanis <nikos.baxevanis@gmail.com>

Co-authored-by: Nikos Baxevanis <nikos.baxevanis@gmail.com>
  • Loading branch information
cmeeren and moodmosaic authored Jan 6, 2021
1 parent 4e4f5c9 commit a687653
Show file tree
Hide file tree
Showing 2 changed files with 31 additions and 13 deletions.
36 changes: 26 additions & 10 deletions src/Hedgehog/Gen.fs
Original file line number Diff line number Diff line change
Expand Up @@ -515,21 +515,37 @@ module Gen =
return System.Guid bs
}

/// Generates a random instant in time expressed as a date and time of day.
/// Generates a random DateTime using the specified range.
/// For example:
/// let range =
/// Range.constantFrom
/// (System.DateTime (2000, 1, 1)) System.DateTime.MinValue System.DateTime.MaxValue
/// Gen.dateTime range
[<CompiledName("DateTime")>]
let dateTime : Gen<System.DateTime> =
let minTicks =
System.DateTime.MinValue.Ticks
let maxTicks =
System.DateTime.MaxValue.Ticks
let dateTime (range : Range<DateTime>) : Gen<System.DateTime> =
gen {
let! ticks =
Range.constantFrom
(System.DateTime (2000, 1, 1)).Ticks minTicks maxTicks
|> integral
let! ticks = range |> Range.map (fun dt -> dt.Ticks) |> integral
return System.DateTime ticks
}

/// Generates a random DateTimeOffset using the specified range.
[<CompiledName("DateTimeOffset")>]
let dateTimeOffset (range : Range<DateTimeOffset>) : Gen<System.DateTimeOffset> =
gen {
let! ticks = range |> Range.map (fun dt -> dt.Ticks) |> integral
// Ensure there is no overflow near the edges when adding the offset
let minOffsetMinutes =
max
(-14L * 60L)
((DateTimeOffset.MaxValue.Ticks - ticks) / TimeSpan.TicksPerMinute * -1L)
let maxOffsetMinutes =
min
(14L * 60L)
((ticks - DateTimeOffset.MinValue.Ticks) / TimeSpan.TicksPerMinute)
let! offsetMinutes = int (Range.linearFrom 0 (Operators.int minOffsetMinutes) (Operators.int maxOffsetMinutes))
return System.DateTimeOffset(ticks, TimeSpan.FromMinutes (Operators.float offsetMinutes))
}

//
// Sampling
//
Expand Down
8 changes: 5 additions & 3 deletions tests/Hedgehog.Tests/GenTests.fs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ open Xunit
[<InlineData(256)>]
[<InlineData(512)>]
let ``dateTime creates System.DateTime instances`` count =
let actual = Gen.dateTime |> Gen.sample 0 count
let actual = Gen.dateTime (Range.constant System.DateTime.MinValue System.DateTime.MaxValue) |> Gen.sample 0 count
actual
|> List.distinct
|> List.length
Expand Down Expand Up @@ -45,7 +45,7 @@ let ``dateTime randomly generates value between max and min ticks`` () =
|> Random.run seed1 0
let expected = System.DateTime ticks

let actual = Gen.dateTime
let actual = Gen.dateTime (Range.constant System.DateTime.MinValue System.DateTime.MaxValue)

let result = actual |> Gen.toRandom |> Random.run seed0 0 |> Tree.outcome
expected =! result
Expand All @@ -54,7 +54,9 @@ let ``dateTime randomly generates value between max and min ticks`` () =
let ``dateTime shrinks to correct mid-value`` () =
let result =
property {
let! actual = Gen.dateTime
let! actual =
Range.constantFrom (System.DateTime (2000, 1, 1)) System.DateTime.MinValue System.DateTime.MaxValue
|> Gen.dateTime
System.DateTime.Now =! actual
}
|> Property.report
Expand Down

0 comments on commit a687653

Please sign in to comment.