Random DateTime between range - not unified output
Asked Answered
D

5

11

I implemented the below RandomDate, but I always keep getting values closed to "From" date, i probably miss something here....

public static DateTime GetRandomDate(DateTime from, DateTime to)
    {
        var range = new TimeSpan(to.Ticks - from.Ticks);

        var rnd = new Random();

        var randTimeSpan = new TimeSpan((long)(range.TotalSeconds - rnd.Next(0, (int)range.TotalSeconds))); 

        return from + randTimeSpan;
    }
Doolittle answered 24/1, 2013 at 16:20 Comment(2)
While not the (only) reason for your error, the var rnd = new Random() line will cause headaches if you intend to call this method frequently.Tucson
see this post? #768499Danica
A
31

You could change to:

static readonly Random rnd = new Random();
public static DateTime GetRandomDate(DateTime from, DateTime to)
{
    var range = to - from;

    var randTimeSpan = new TimeSpan((long)(rnd.NextDouble() * range.Ticks)); 

    return from + randTimeSpan;
}

Explanation: I used NextDouble() because it gives a number between 0.0 and 1.0. Your return value won't be a whole number of seconds in my solution. And I moved rnd out to a field on the class/struct. Because it's best to reuse one Random instance, and not create a new one every time one needs just one additional random number.

Aam answered 24/1, 2013 at 21:23 Comment(1)
took this idea, thanks. I also had the Random Member as a ThreadLocal<Random> like Alexx suggestedDoolittle
C
4

The problem is that:

var randTimeSpan = new TimeSpan((long)(range.TotalSeconds - rnd.Next(0, (int)range.TotalSeconds)));

is creating a TimeSpan from TICKS, not from SECONDS.

You need:

var randTimeSpan = TimeSpan.FromSeconds((long)(range.TotalSeconds - rnd.Next(0, (int)range.TotalSeconds)));

(Please check the cast too - it needs to be a double passed to FromSeconds)

Curtate answered 24/1, 2013 at 16:23 Comment(3)
The random should also either be a field in the class or an argument in this method, otherwise it's always created with the same seed when this method is called very fast(f.e. in a loop).Constructivism
This can causes ArgumentOutOfRangeException when range.TotalSeconds is greater Int32.MaxValueMultivibrator
@user1320170 Indeed, it will only work up to around 68 years range; however, it seems that this is enough for the OP.Curtate
C
3

This is because TimeSpan's constructor that takes a long expects ticks, not seconds.

var randTimeSpan = new TimeSpan(range.Ticks - rnd.Next(0, range.Ticks)); 
Cygnus answered 24/1, 2013 at 16:24 Comment(1)
rnd.Next() doesn't take long as an argument, only intMores
M
0

Fixed ArgumentOutOfRangeException:

public static DateTime GetRandomDateTime(DateTime? min = null, DateTime? max = null)
{
    min = min ?? new DateTime(1753, 01, 01);
    max = max ?? new DateTime(9999, 12, 31);

    var range = max.Value - min.Value;
    var randomUpperBound = (Int32) range.TotalSeconds;
    if (randomUpperBound <= 0)
        randomUpperBound = Rnd.Next(1, Int32.MaxValue);

    var randTimeSpan = TimeSpan.FromSeconds((Int64) (range.TotalSeconds - Rnd.Next(0, randomUpperBound)));
    return min.Value.Add(randTimeSpan);
}
Multivibrator answered 22/9, 2014 at 20:18 Comment(0)
G
0

My idea is we just need some random number of ticks added to start datetime to get a random date in between start and end. So my solution does not create any TimeSpan objects.

private static readonly Random random = new Random ();
private static readonly object syncLock = new object ();

public static DateTime RandomDate(DateTime from, DateTime to)
{
    lock ( syncLock )
    {
         return from.AddTicks ((long) ( random.NextDouble () * ( to.Ticks - from.Ticks ) ));
    }
 }
Gladsome answered 8/1, 2016 at 18:48 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.