[AccessD] Create a random date/time

Gustav Brock gustav at cactus.dk
Sun Aug 30 04:26:03 CDT 2015


Hi Bill

You are correct. The reason is - as Stuart mentions - rounding or rather missing rounding.

It's a good example of when you think you are smart you may be too smart. So I have expanded and modified my smart function to take care of the flaw. While at it, I've added the option to include or not the date, time, or millisecond part in the output:

<code>
Public Function DateRandom( _
    Optional ByVal UpperDate As Date = #12/31/9999#, _
    Optional ByVal LowerDate As Date = #1/1/100#, _
    Optional ByVal DatePart As Boolean = True, _
    Optional ByVal TimePart As Boolean = True, _
    Optional ByVal MilliSecondPart As Boolean = False) _
    As Date
    
'   Generates a random date/time - optionally within the range of LowerDate and/or UpperDate.
'   Optionally, return value can be set to include date and/or time and/or milliseconds.
'
'   2015-08-28. Gustav Brock, Cactus Data ApS, CPH.
'   2015-08-29. Modified for uniform distribution as suggested by Stuart McLachlan by
'               combining a random date and a random time.
'   2015-08-30  Modified to return selectable and rounded value parts for
'               Date, Time, and Milliseconds.
    
    Const SecondsPerDay As Long = 60& * 60& * 24&
    
    Dim DateValue   As Date
    Dim TimeValue   As Date
    Dim MSecValue   As Date
    
    ' If all parts are deselected, select date and time.
    If Not DatePart And Not TimePart And Not MilliSecondPart = True Then
        DatePart = True
        TimePart = True
    End If
    If DatePart = True Then
        ' Remove time parts from UpperDate and LowerDate as well from the result value.
        ' Add 1 to include LowerDate as a possible return value.
        DateValue = CDate(Int((Int(UpperDate) - Int(LowerDate) + 1) * Rnd) + Int(LowerDate))
    End If
    If TimePart = True Then
        ' Calculate a time value rounded to the second.
        TimeValue = CDate(Int(SecondsPerDay * Rnd) / SecondsPerDay)
    End If
    If MilliSecondPart = True Then
        ' Calculate a millisecond value rounded to the millisecond.
        MSecValue = CDate(Int(1000 * Rnd) / 1000 / SecondsPerDay)
    End If
    
    DateRandom = DateValue + TimeValue + MSecValue

End Function
</code>

Note that Access will - for display - round milliseconds to the nearest second. That means that for example 23.59:59.777 rounds to 00:00:00 of the following day, thus you had a minimum and maximum date with a difference of one day.

Adjusting your test function to use the modified DateRandom:

<code>
Sub testformula()
Dim dt As Date
Dim i As Long
Dim dttemp As Date
dt = Date
Dim dtMax As Date
Dim dtMin As Date
dtMin = CDate("1/1/2100")
dtMax = CDate("1/1/1900")
For i = 1 To 1000000#
     dttemp = DateRandom(dt, dt, , False)
     If dttemp > dtMax Then dtMax = dttemp
     If dttemp < dtMin Then dtMin = dttemp
 Next
Debug.Print dtMin, dtMax
End Sub
</code>

it now returns:

2015-08-30    2015-08-30

and if TimePart is True:

2015-08-30    2015-08-30 23:59:59

/gustav

________________________________________
Fra: AccessD <accessd-bounces at databaseadvisors.com> på vegne af Bill Benson <bensonforums at gmail.com>
Sendt: 29. august 2015 20:55
Til: Access Developers discussion and problem solving
Emne: Re: [AccessD] Create a random date/time

There is a flaw in that function. Consider this for the current date

Sub testformula()
Dim dt As Date
Dim i As Long
Dim dttemp As Date
dt = Date
Dim dtMax As Date
Dim dtMin As Date
dtMin = CDate("1/1/2100")
dtMax = CDate("1/1/1900")
For i = 1 To 1000000#
    dttemp = CDate((CLng(dt) - CLng(dt)) * Rnd + CLng(dt) + Rnd)
    If dttemp > dtMax Then dtMax = dttemp
    If dttemp < dtMin Then dtMin = dttemp
Next
Debug.Print dtMin, dtMax
End Sub

I got an answer of

8/29/2015     8/30/2015


On Sat, Aug 29, 2015 at 2:10 PM, Gustav Brock <gustav at cactus.dk> wrote:

> Hi Stuart
>
> Thanks for the link - you are absolutely right - why didn't I think about
> this?
> While I believe - for the purpose - one could live with this limitation,
> the modification needed is really really simple:
>
> Initial method:
>
>     RandomDate = CDate((CLng(UpperDate) - CLng(LowerDate)) * Rnd * Rnd +
> CLng(LowerDate))
>
> Modified as to your suggestion:
>
>     RandomDate = CDate((CLng(UpperDate) - CLng(LowerDate)) * Rnd + Rnd +
> CLng(LowerDate))
>
> This is possible because Rnd returns values >= 0 and < 1, exactly matching
> the numeric value range of Time.
>
> The corrected function:
>
> <code>
> Public Function DateRandom( _
>     Optional ByVal UpperDate As Date = #12/31/9999#, _
>     Optional ByVal LowerDate As Date = #1/1/100#) _
>     As Date
>
> '   Generates a random date/time - optionally within the range of
> LowerDate and/or UpperDate.
> '
> '   2015-08-28. Gustav Brock, Cactus Data ApS, CPH.
> '   2015-08-29. Modified for uniform distribution as suggested by Stuart
> McLachlan by
> '               combining a random date and a random time.
>
>     Dim RandomDate  As Date
>
>     ' Random date: (CLng(UpperDate) - CLng(LowerDate)) * Rnd +
> CLng(LowerDate)
>     ' Random time: Rnd
>     RandomDate = CDate((CLng(UpperDate) - CLng(LowerDate)) * Rnd +
> CLng(LowerDate) + Rnd)
>
>     DateRandom = RandomDate
>
> End Function
> </code>
>
> /gustav


More information about the AccessD mailing list