C# – How to get a random time between Now and some previous time point (eg. 1 hour ago)


I'm trying to make an extension method that allows me to create a random time between Now and some user requested historical time point in the form of a TimeSpan.

For example : a random time between now and 1 hour ago.

So, I came up with the following Random(..) extension method.

I thought to make the random seed NOT static, but if i call this method a LOT and QUICKLY (eg. in a loop), then I thought the seed (which is based on time) isn't really random for really fast calls. is that true? (it seems to be, when i check my results)

public static DateTimeOffset Random(this DateTimeOffset value, TimeSpan timeSpan)
    var random = new Random();
    DateTimeOffset minDateTime = value.Subtract(timeSpan);
    int range = ((DateTime.Today - minDateTime)).Hours;
    return minDateTime.AddHours(random.Next(range));

Best Solution

As others have said, the problem is that new Random() uses the current time to form the seed, and you're getting the same one lots of times.

Basically you want to create relatively few instances. As Random isn't thread-safe, you need ThreadStatic or ThreadLocal<T> - the latter is new to .NET 4.0. Here's a sample StaticRandom class (using .NET 4.0) which lets you use the Instance property to get a valid instance for this thread. Note that on type initialization, a counter is set from the current time. This is then used for successive seeds.

using System;
using System.Threading;

public static class StaticRandom
    private static int seed;

    private static ThreadLocal<Random> threadLocal = new ThreadLocal<Random>
        (() => new Random(Interlocked.Increment(ref seed)));

    static StaticRandom()
        seed = Environment.TickCount;

    public static Random Instance { get { return threadLocal.Value; } }

Then you can just use StaticRandom.Instance whenever you need an instance of Random.

Now to get back to the original question, it's not entirely clear what your current extension method is doing. Why are you using DateTime.Today at all? I suspect you want something like:

public static DateTimeOffset Random(this DateTimeOffset value, TimeSpan timeSpan)
    double seconds = timeSpan.TotalSeconds * StaticRandom.Instance.NextDouble();

    // Alternatively: return value.AddSeconds(-seconds);
    TimeSpan span = TimeSpan.FromSeconds(seconds);
    return value - span;

However, that will give you a completely random time - it's almost bound to be part way through a millisecond, for instance. Is that okay, or do you effectively want it to be an exact number of seconds (or minutes, or hours) based on the original timespan?