Date aléatoire en C #

141

Je recherche du code C # succinct et moderne pour générer une date aléatoire entre le 1er janvier 1995 et la date actuelle.

Je pense à une solution qui utilise Enumerable.Range peut en quelque sorte rendre cela plus succinct.

Judah Gabriel Himango
la source
Réponse en date / heure aléatoire entre plage - la sortie non unifiée a une méthode d'assistance avec des paramètres de date De / À
Michael Freidgeim

Réponses:

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

Pour de meilleures performances si cela est appelé à plusieurs reprises, créez les variables startand gen(et peut-être même range) en dehors de la fonction.

Joël Coehoorn
la source
1
L'aléatoire n'est que pseudo-aléatoire. Si vous avez besoin de vraiment aléatoire, essayez d'utiliser RNGCryptoServiceProvider à partir de l'espace de noms System.Security.Cryptography.
tvanfosson
Merci tvanfosson. Le pseudo-aléatoire est suffisant pour ce problème.
Judah Gabriel Himango
5
En fait, Random n'est même pas particulièrement pseudo-aléatoire à moins que vous ne gardiez l'instance pendant un moment et que vous continuiez à en tirer des valeurs.
David Mitchell
2
C'est pourquoi il ne s'agit que d'un exemple plutôt que d'un code de production.
Joel Coehoorn
1
Oui, cela fonctionne pour moi; mon code réel aura l'instance Random en dehors de la méthode elle-même.
Judah Gabriel Himango
25

C'est en légère réponse au commentaire de Joel sur la création d'une version légèrement plus optimisée. Au lieu de renvoyer directement une date aléatoire, pourquoi ne pas renvoyer une fonction de générateur qui peut être appelée à plusieurs reprises pour créer une date aléatoire.

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
la source
Pouvez-vous expliquer en quoi cela est bénéfique? Le début, la génération et la plage ne pourraient-ils pas être des membres de la classe à la place?
Mark A. Nicolosi
Ils pourraient et dans ce cas ils le sont. Sous le capot, cela générera une fermeture lexicale qui est une classe contenant start, gen et range comme membres. C'est juste plus concis.
JaredPar
Belle fonction, j'espère juste que personne ne l'utilisera comme:for (int i = 0; i < 100; i++) { array[i].DateProp = RandomDayFunc()(); }
Aidiakapi
2
Comment cette fonction est-elle utilisée, quelqu'un peut-il expliquer? Je veux dire comment puis-je l'appeler?
Burak Karakuş
2
@ BurakKarakuş: Vous obtenez d'abord une usine: var getRandomDate = RandomDayFunc();puis vous l'appelez pour obtenir des dates aléatoires: var randomDate = getRandomDate();sachez que vous devez réutiliser getRandomDate pour que cela soit plus utile que la réponse de Joel.
Şafak Gür
8

J'ai pris la réponse de @Joel Coehoorn et apporté les modifications qu'il m'a conseillées - mettre la variable hors de la méthode et tout mettre en classe. De plus, maintenant, le temps est également aléatoire. Voici le résultat.

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

Et exemple comment utiliser pour écrire 100 DateTimes aléatoires sur la console:

RandomDateTime date = new RandomDateTime();
for (int i = 0; i < 100; i++)
{
    Console.WriteLine(date.Next());
}
prespic
la source
Pourquoi créez-vous Random () deux fois? Une fois dans la déclaration de génération de variable de classe et autre fois dans le c-tor?
pixel
Ouais, une fois suffit. Je l'ai corrigé.
prépic
1
Il est environ quatre fois plus rapide de générer un seul nombre aléatoire de secondes et de l'ajouter à votre date de début: range = (int)(DateTime.Today - start).TotalSeconds;et return start.AddSeconds(gen.Next(range));.
Jurgy
5

Eh bien, si vous voulez présenter une optimisation alternative, nous pouvons également opter pour un itérateur:

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

vous pouvez l'utiliser comme ceci:

int i=0;
foreach(DateTime dt in RandomDay())
{
    Console.WriteLine(dt);
    if (++i == 10)
        break;
}
James Curran
la source
1
Une chose à considérer entre un itérateur et une fonction de générateur est que la solution d'itérateur produira une valeur IDisposable. Cela oblige l'appelant à disposer ou à payer le prix d'avoir un finaliseur en direct dans le GC. Le générateur n'a pas besoin d'être éliminé
JaredPar
2
@JaredPar, ce n'est pas tout à fait vrai. Ce n'est pas parce qu'un type implémente IDisposable qu'il est finalisable.
Drew Noakes
3

Commencez avec un objet à date fixe (1er janvier 1995) et ajoutez un nombre aléatoire de jours avec AddDays (évidemment, faites attention à ne pas dépasser la date actuelle).

Gabriele D'Antona
la source
Merci Friol. J'allais demander comment limiter le nombre passé au hasard. Joel a publié un exemple avec un exemple de code, donc je marquerai sa réponse comme réponse.
Judah Gabriel Himango
0

Je suis un peu en retard dans le jeu, mais voici une solution qui fonctionne 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
la source
0

Petite méthode qui renvoie une date aléatoire sous forme de chaîne, basée sur quelques paramètres d'entrée simples. Construit sur la base de variations des réponses ci-dessus:

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
la source