在C#中生成随机日期

164

我正在寻找一些简洁、现代化的C#代码,用于生成1995年1月1日至今天的随机日期。

我认为利用Enumerable.Range的某些解决方案可能会使这个过程更加简洁。


[2022-06-15 14:23:47] 在stackoverflow上的回答中,有一个带有From/To日期参数的辅助方法。 - Michael Freidgeim
10个回答

287
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));
}

如果该函数需要重复调用,为了获得更好的性能,请在函数外创建startgen变量(甚至可以创建range变量)。


1
随机数只是伪随机。如果你需要真正的随机数,请尝试使用System.Security.Cryptography命名空间中的RNGCryptoServiceProvider。 - tvanfosson
6
实际上,除非您将随机实例保留一段时间并继续从中获取值,否则其并不特别伪随机。 - Amanda Mitchell
3
因此,这只是样例代码而不是生产代码。 - Joel Coehoorn
1
没问题,这对我可行;我的真实代码将会让随机实例在方法外部。 - Judah Gabriel Himango
1
@ShadowWizard 我错了。我认为天数不是TimeSpan中最重要的部分,因此Days和TotalDays之间的差异会非常明显,就像分钟一样。 - Ronnie Overby
显示剩余6条评论

29

这是对Joel关于创建稍微更优化版本的略微回应。不要直接返回随机日期,为什么不返回一个生成器函数,可以反复调用以创建随机日期。

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));
}

你能解释一下这有什么好处吗?不能将start、gen和range作为类成员吗? - Mark A. Nicolosi
它们可以并且在这种情况下它们是。在底层,这将生成一个词法闭包,其中包含start、gen和range作为成员的类。这只是更加简洁。 - JaredPar
3
不错的功能,只希望没有人会像这样使用它:for (int i = 0; i < 100; i++) { array[i].DateProp = RandomDayFunc()(); } - Aidiakapi
2
这个函数怎么使用?有人可以解释一下吗?我的意思是我该如何调用它? - Burak Karakuş
5
首先需要定义一个函数 getRandomDate 来获取随机日期,然后调用该函数来获取随机日期,方法是写成 var randomDate = getRandomDate();。请注意,需要重复使用 getRandomDate 函数才能比 Joel 的答案更有用。 - Şafak Gür

15

我采用了@Joel Coehoorn的答案并进行了他建议的更改——将变量从方法中取出并放入类中。此外,现在时间也是随机的。这是结果。

class RandomDateTime
{
    DateTime start;
    Random gen;
    int range;

    public RandomDateTime()
    {
        start = new DateTime(1995, 1, 1);
        gen = new Random();
        range = (DateTime.Today - start).Days;
    }

    public DateTime Next()
    {
        return start.AddDays(gen.Next(range)).AddHours(gen.Next(0,24)).AddMinutes(gen.Next(0,60)).AddSeconds(gen.Next(0,60));
    }
}

一个示例,如何使用来将100个随机日期时间写入控制台:

RandomDateTime date = new RandomDateTime();
for (int i = 0; i < 100; i++)
{
    Console.WriteLine(date.Next());
}

你为什么要两次创建Random()?一次在类变量gen声明中,另一次在构造函数中? - pixel
是的,一次就够了。我已经修好了。 - prespic
2
生成一个随机秒数并将其添加到起始日期中,然后返回结果,这种方法比生成多个随机日期要快大约四倍:range = (int)(DateTime.Today - start).TotalSeconds;return start.AddSeconds(gen.Next(range)); - Jurgy

5

如果你想提供替代优化方案,我们也可以选择使用迭代器:

 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));        
}

你可以像这样使用它:
int i=0;
foreach(DateTime dt in RandomDay())
{
    Console.WriteLine(dt);
    if (++i == 10)
        break;
}

1
在迭代器和生成器函数之间需要考虑的一件事是,迭代器解决方案将产生一个IDisposable值。这会强制调用者处理或支付拥有终结器的GC的代价。生成器不需要处理。 - JaredPar
2
@JaredPar,那不完全正确。仅仅因为一个类型实现了IDisposable并不意味着它是可终结的。 - Drew Noakes

3

从一个固定的日期对象(1995年1月1日)开始,使用AddDays方法添加随机天数(当然要注意不超过当前日期)。


谢谢Friol。我本来想问如何限制传入 random 的数字数量。Joel 发布了一个带有代码示例的示例,所以我将把他的回答标记为答案。 - Judah Gabriel Himango

2
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);

//这可能不是最好的方法,但它快速且易于理解


1

这是解决问题的另一种方法,这次是提供一个日期范围的类。结果中包含随机分钟。

/// <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;
        }
    }
}

如需在2020年1月1日至2022年12月31日之间随机生成5个日期,请按如下方式使用:

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);

可以添加一些功能,比如粒度、仅时间/仅日期部分的随机化、仅特定工作日等。 - Hefaistos68

0

我有点晚入门,但这是一个很好的解决方案:

    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;
            }
        }
    }

0

这是一个小方法,根据一些简单的输入参数返回一个随机日期字符串。它是基于上面答案的变化构建的:

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);
}

-2

基于@Jeremy Thompson的解决方案的有用扩展

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));
    }
}

new DateTime()01/01/0001 00:00:00。你确定吗? - Theodor Zoulias

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接