开发者

How can I 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.

F开发者_如何学运维or 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));
}


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?


Use a Random object that's created/initialised once and not every time the method is called. Another option is to pass a Random instance into the method when you call it.

You could also create overloads that allow you to do either of the above options:

public static DateTimeOffset Random(this DateTimeOffset value, TimeSpan timeSpan)
{
    if (_threadStaticRng == null)
        _threadStaticRng = new Random();

    return value.Random(timeSpan, _threadStaticRng);
}

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

[ThreadStatic]
private static Random _threadStaticRng;


You have to seed the Random number generator. A good practice would be to do the following:

var random = new Random((int)DateTime.Now.Ticks);

This should make it more random for you.

You could also create the Random generator as a static class variable so you don't have to instantiate it every time.

0

上一篇:

下一篇:

精彩评论

暂无评论...
验证码 换一张
取 消

最新问答

问答排行榜