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;
    }
user1025852
  • 2,684
  • 11
  • 36
  • 58
  • 1
    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. – Austin Salonen Jan 24 '13 at 16:25
  • see this post? http://stackoverflow.com/questions/767999/random-number-generator-only-generating-one-random-number – Alexxx Jan 24 '13 at 16:25

5 Answers5

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.

Jeppe Stig Nielsen
  • 60,409
  • 11
  • 110
  • 181
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)

Matthew Watson
  • 104,400
  • 10
  • 158
  • 276
  • 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). – Tim Schmelter Jan 24 '13 at 16:26
  • 1
    This can causes ArgumentOutOfRangeException when range.TotalSeconds is greater Int32.MaxValue – musium Sep 22 '14 at 20:16
  • @user1320170 Indeed, it will only work up to around 68 years range; however, it seems that this is enough for the OP. – Matthew Watson Sep 23 '14 at 08:11
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)); 
Sergey Kalinichenko
  • 714,442
  • 84
  • 1,110
  • 1,523
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);
}
musium
  • 2,942
  • 3
  • 34
  • 67
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 ) ));
    }
 }
Adarsha
  • 2,267
  • 22
  • 29