Fecha aleatoria en C #

141

Estoy buscando un código breve y moderno de C # para generar una fecha aleatoria entre el 1 de enero de 1995 y la fecha actual.

Estoy pensando en alguna solución que utilice Enumerable. El rango de alguna manera puede hacer que esto sea más conciso.

Judá Gabriel Himango
fuente
Respuesta en Random DateTime entre rango: la salida no unificada tiene un método auxiliar con parámetros From / To date
Michael Freidgeim,

Respuestas:

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

Para un mejor rendimiento si esto se llama repetidamente, cree las variables starty gen(y tal vez incluso range) fuera de la función.

Joel Coehoorn
fuente
1
Aleatorio es solo seudoaleatorio. Si necesita algo realmente aleatorio, intente usar RNGCryptoServiceProvider del espacio de nombres System.Security.Cryptography.
tvanfosson
Gracias tvanfosson. Pseudoaleatorio es suficiente para este problema.
Judá Gabriel Himango
55
En realidad, Random ni siquiera es particularmente pseudoaleatorio, a menos que mantenga la instancia durante un tiempo y siga obteniendo valores.
David Mitchell
2
Es por eso que esto es solo una muestra, en lugar de un código de producción.
Joel Coehoorn el
1
Sí, eso funciona para mí; mi código del mundo real tendrá la instancia Aleatoria fuera del método en sí.
Judah Gabriel Himango
25

Esto es una ligera respuesta al comentario de Joel sobre hacer una versión ligeramente más optimizada. En lugar de devolver una fecha aleatoria directamente, ¿por qué no devolver una función generadora a la que se pueda llamar repetidamente para crear una fecha aleatoria?

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));
}
JaredPar
fuente
¿Puedes explicar cómo esto es beneficioso? ¿No podrían comenzar, gen y range ser miembros de la clase?
Mark A. Nicolosi el
Podrían y en este caso lo son. Debajo del capó, esto generará un cierre léxico que es un clrass que contiene start, gen y range como miembros. Esto es solo más conciso.
JaredPar
Buena función, solo espero que nadie la use como:for (int i = 0; i < 100; i++) { array[i].DateProp = RandomDayFunc()(); }
Aidiakapi
2
¿Cómo se usa esta función? ¿Puede alguien explicarme? Quiero decir, ¿cómo puedo llamarlo?
Burak Karakuş
2
@ BurakKarakuş: Primero obtienes una fábrica: var getRandomDate = RandomDayFunc();luego la llamas para obtener fechas aleatorias: var randomDate = getRandomDate();recuerda que debes reutilizar getRandomDate para que esto sea más útil que la respuesta de Joel.
Şafak Gür
8

Tomé la respuesta de @Joel Coehoorn e hice los cambios que aconsejó: eliminar la variable del método y poner todo en clase. Además, ahora el tiempo también es aleatorio. Aquí está el resultado.

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

Y un ejemplo de cómo usar para escribir 100 DateTimes al azar en la consola:

RandomDateTime date = new RandomDateTime();
for (int i = 0; i < 100; i++)
{
    Console.WriteLine(date.Next());
}
prespic
fuente
¿Por qué creas Random () dos veces? ¿Una vez en la clase variable declaración de gen y otra vez en el c-tor?
píxel
Sí, una vez es suficiente. Lo arreglé.
prespic
1
Es aproximadamente cuatro veces más rápido generar solo un número aleatorio de segundos y agregarlo a su fecha de inicio: range = (int)(DateTime.Today - start).TotalSeconds;y return start.AddSeconds(gen.Next(range));.
Jurgy
5

Bueno, si va a presentar una optimización alternativa, también podemos buscar un iterador:

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

podrías usarlo así:

int i=0;
foreach(DateTime dt in RandomDay())
{
    Console.WriteLine(dt);
    if (++i == 10)
        break;
}
James Curran
fuente
1
Una cosa a considerar entre un iterador frente a una función generadora es que la solución del iterador producirá un valor IDisposable. Esto obliga a la persona que llama a disponer o pagar el precio de tener un finalizador en vivo en el GC. El generador no necesita deshacerse
JaredPar
2
@JaredPar, eso no está del todo bien. El hecho de que un tipo implemente IDisposable no significa que sea finalizable.
Drew Noakes
3

Comience con un objeto de fecha fija (1 de enero de 1995) y agregue un número aleatorio de días con AddDays (obviamente, preste atención sin superar la fecha actual).

Gabriele D'Antona
fuente
Gracias Friol Iba a preguntar cómo limitar el número pasado al azar. Joel ha publicado un ejemplo con código de muestra, por lo que marcaré su respuesta como la respuesta.
Judá Gabriel Himango
0

Llego un poco tarde al juego, pero aquí hay una solución que funciona bien:

    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;
            }
        }
    }
Hamit Gündogdu
fuente
0

Pequeño método que devuelve una fecha aleatoria como cadena, basada en algunos parámetros de entrada simples. Construido en base a las variaciones de las respuestas anteriores:

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