Random date in C#

Viewed 122174

I'm looking for some succinct, modern C# code to generate a random date between Jan 1 1995 and the current date.

I'm thinking some solution that utilizes Enumerable.Range somehow may make this more succinct.

10 Answers
private Random gen = new Random();
DateTime RandomDay()
{
    DateTime start = new DateTime(1995, 1, 1);
    int range = (DateTime.Today - start).Days;           
    return start.AddDays(gen.Next(range));
}

For better performance if this will be called repeatedly, create the start and gen (and maybe even range) variables outside of the function.

This is in slight response to Joel's comment about making a slighly more optimized version. Instead of returning a random date directly, why not return a generator function which can be called repeatedly to create a random date.

Func<DateTime> RandomDayFunc()
{
    DateTime start = new DateTime(1995, 1, 1); 
    Random gen = new Random(); 
    int range = ((TimeSpan)(DateTime.Today - start)).Days; 
    return () => start.AddDays(gen.Next(range));
}

Well, if you gonna present alternate optimization, we can also go for an iterator:

 static IEnumerable<DateTime> RandomDay()
 {
    DateTime start = new DateTime(1995, 1, 1);
    Random gen = new Random();
    int range = ((TimeSpan)(DateTime.Today - start)).Days;
    while (true)
        yield return  start.AddDays(gen.Next(range));        
}

you could use it like this:

int i=0;
foreach(DateTime dt in RandomDay())
{
    Console.WriteLine(dt);
    if (++i == 10)
        break;
}

Start with a fixed date object (Jan 1, 1995), and add a random number of days with AddDays (obviusly, pay attention not surpassing the current date).

Random rnd = new Random();
DateTime datetoday = DateTime.Now;

int rndYear = rnd.Next(1995, datetoday.Year);
int rndMonth = rnd.Next(1, 12);
int rndDay = rnd.Next(1, 31);

DateTime generateDate = new DateTime(rndYear, rndMonth, rndDay);
Console.WriteLine(generateDate);

//this maybe is not the best method but is fast and easy to understand

I am a bit late in to the game, but here is one solution which works fine:

    void Main()
    {
        var dateResult = GetRandomDates(new DateTime(1995, 1, 1), DateTime.UtcNow, 100);
        foreach (var r in dateResult)
            Console.WriteLine(r);
    }

    public static IList<DateTime> GetRandomDates(DateTime startDate, DateTime maxDate, int range)
    {
        var randomResult = GetRandomNumbers(range).ToArray();

        var calculationValue = maxDate.Subtract(startDate).TotalMinutes / int.MaxValue;
        var dateResults = randomResult.Select(s => startDate.AddMinutes(s * calculationValue)).ToList();
        return dateResults;
    }

    public static IEnumerable<int> GetRandomNumbers(int size)
    {
        var data = new byte[4];
        using (var rng = new System.Security.Cryptography.RNGCryptoServiceProvider(data))
        {
            for (int i = 0; i < size; i++)
            {
                rng.GetBytes(data);

                var value = BitConverter.ToInt32(data, 0);
                yield return value < 0 ? value * -1 : value;
            }
        }
    }

Small method that returns a random date as string, based on some simple input parameters. Built based on variations from the above answers:

public string RandomDate(int startYear = 1960, string outputDateFormat = "yyyy-MM-dd")
{
   DateTime start = new DateTime(startYear, 1, 1);
   Random gen = new Random(Guid.NewGuid().GetHashCode());
   int range = (DateTime.Today - start).Days;
   return start.AddDays(gen.Next(range)).ToString(outputDateFormat);
}

One more solution to the problem, this time a class to which you provide a range you want the dates in. Its down to random minutes in the results.

/// <summary>
/// A random date/time class that provides random dates within a given range
/// </summary>
public class RandomDateTime
{
    private readonly Random rng = new Random();
    private readonly int totalMinutes;
    private readonly DateTime startDateTime;

    /// <summary>
    /// Initializes a new instance of the <see cref="RandomDateTime"/> class.
    /// </summary>
    /// <param name="startDate">The start date.</param>
    /// <param name="endDate">The end date.</param>
    public RandomDateTime(DateTime startDate, DateTime endDate)
    {
        this.startDateTime = startDate;
        TimeSpan timeSpan = endDate - startDate;
        this.totalMinutes = (int)timeSpan.TotalMinutes;
    }

    /// <summary>
    /// Gets the next random datetime object within the range of startDate and endDate provided in the ctor
    /// </summary>
    /// <returns>A DateTime.</returns>
    public DateTime NextDateTime
    {
        get
        {
            TimeSpan newSpan = new TimeSpan(0, rng.Next(0, this.totalMinutes), 0);
            return this.startDateTime + newSpan;
        }
    }
}

Use it like this to spit out 5 random dates between january 1st 2020 and december 31 2022:

RandomDateTime rdt = new RandomDateTime(DateTime.Parse("01/01/2020"), DateTime.Parse("31/12/2022"));

for (int i = 0; i < 5; i++)
    Debug.WriteLine(rdt.NextDateTime);

Useful extension based of @Jeremy Thompson's solution

public static class RandomExtensions
{
    public static DateTime Next(this Random random, DateTime start, DateTime? end = null)
    {
        end ??= new DateTime();
        int range = (end.Value - start).Days;
        return start.AddDays(random.Next(range));
    }
}
Related