Générer des dates aléatoires en Java

1. Vue d'ensemble

Dans ce tutoriel, nous allons voir comment générer des dates et des heures aléatoires de manière bornée et illimitée.

Nous verrons comment générer ces valeurs à l'aide de l'ancienne API java.util.Date ainsi que de la nouvelle bibliothèque date-heure de Java 8.

2. Date et heure aléatoires

Les dates et les heures ne sont rien de plus que des entiers de 32 bits par rapport à une époque , nous pouvons donc générer des valeurs temporelles aléatoires en suivant cet algorithme simple:

  1. Générer un nombre aléatoire de 32 bits, un entier
  2. Transmettez la valeur aléatoire générée à un constructeur de date et d'heure approprié ou à un générateur

2.1. Instantané borné

java.time.I nstant est l'un des nouveaux ajouts de date et d'heure dans Java 8. Ils représentent des points instantanés sur la chronologie.

Afin de générer un instant aléatoire entre deux autres, nous pouvons:

  1. Générer un nombre aléatoire entre les secondes d'époque des Instants donnés
  2. Créez l' Instant aléatoire en passant ce nombre aléatoire à la méthode ofEpochSecond ()
public static Instant between(Instant startInclusive, Instant endExclusive) { long startSeconds = startInclusive.getEpochSecond(); long endSeconds = endExclusive.getEpochSecond(); long random = ThreadLocalRandom .current() .nextLong(startSeconds, endSeconds); return Instant.ofEpochSecond(random); }

Afin d'obtenir plus de débit dans les environnements multithreads, nous utilisons ThreadLocalRandom pour générer nos nombres aléatoires.

Nous pouvons vérifier que l' Instant généré est toujours supérieur ou égal au premier Instant et est inférieur au deuxième Instant:

Instant hundredYearsAgo = Instant.now().minus(Duration.ofDays(100 * 365)); Instant tenDaysAgo = Instant.now().minus(Duration.ofDays(10)); Instant random = RandomDateTimes.between(hundredYearsAgo, tenDaysAgo); assertThat(random).isBetween(hundredYearsAgo, tenDaysAgo);

N'oubliez pas, bien sûr, que le test du caractère aléatoire est par nature non déterministe et n'est généralement pas recommandé dans une application réelle.

De même, il est également possible de générer un instant aléatoire après ou avant un autre:

public static Instant after(Instant startInclusive) { return between(startInclusive, Instant.MAX); } public static Instant before(Instant upperExclusive) { return between(Instant.MIN, upperExclusive); }

2.2. Date limite

L'un des constructeurs java.util.Date prend le nombre de millisecondes après l'époque. Ainsi, nous pouvons utiliser le même algorithme pour générer une Date aléatoire entre deux autres:

public static Date between(Date startInclusive, Date endExclusive) { long startMillis = startInclusive.getTime(); long endMillis = endExclusive.getTime(); long randomMillisSinceEpoch = ThreadLocalRandom .current() .nextLong(startMillis, endMillis); return new Date(randomMillisSinceEpoch); }

De même, nous devrions être en mesure de vérifier ce comportement:

long aDay = TimeUnit.DAYS.toMillis(1); long now = new Date().getTime(); Date hundredYearsAgo = new Date(now - aDay * 365 * 100); Date tenDaysAgo = new Date(now - aDay * 10); Date random = LegacyRandomDateTimes.between(hundredYearsAgo, tenDaysAgo); assertThat(random).isBetween(hundredYearsAgo, tenDaysAgo);

2.3. Instantané illimité

Afin de générer un instant totalement aléatoire , nous pouvons simplement générer un entier aléatoire et le passer à la méthode ofEpochSecond () :

public static Instant timestamp() { return Instant.ofEpochSecond(ThreadLocalRandom.current().nextInt()); }

L'utilisation de secondes de 32 bits depuis l'heure de l'époque génère des temps aléatoires plus raisonnables, nous utilisons donc ici la méthode nextInt () .

En outre, cette valeur doit toujours être comprise entre les valeurs instantanées minimales et maximales possibles que Java peut gérer:

Instant random = RandomDateTimes.timestamp(); assertThat(random).isBetween(Instant.MIN, Instant.MAX);

2.4. Date illimitée

Semblable à l'exemple borné, nous pouvons passer une valeur aléatoire au constructeur de Date pour générer une Date aléatoire :

public static Date timestamp() { return new Date(ThreadLocalRandom.current().nextInt() * 1000L); }

Depuis leL'unité de temps du constructeur est les millisecondes, nous convertissons les secondes d'époque 32 bits en millisecondes en la multipliant par 1000.

Certes, cette valeur est toujours comprise entre les valeurs de date minimum et maximum possibles :

Date MIN_DATE = new Date(Long.MIN_VALUE); Date MAX_DATE = new Date(Long.MAX_VALUE); Date random = LegacyRandomDateTimes.timestamp(); assertThat(random).isBetween(MIN_DATE, MAX_DATE);

3. Date aléatoire

Jusqu'à présent, nous avons généré des temporels aléatoires contenant à la fois des composants de date et d'heure. De même, nous pouvons utiliser le concept de jours d'époque pour générer des temporels aléatoires avec uniquement des composants de date.

Un jour d'époque est égal au nombre de jours depuis le 1er janvier 1970. Donc, pour générer une date aléatoire, il suffit de générer un nombre aléatoire et d'utiliser ce nombre comme jour d'époque.

3.1. Délimité

Nous avons besoin d'une abstraction temporelle contenant uniquement des composants de date, donc java.time.LocalDate semble un bon candidat:

public static LocalDate between(LocalDate startInclusive, LocalDate endExclusive) { long startEpochDay = startInclusive.toEpochDay(); long endEpochDay = endExclusive.toEpochDay(); long randomDay = ThreadLocalRandom .current() .nextLong(startEpochDay, endEpochDay); return LocalDate.ofEpochDay(randomDay); }

Ici, nous utilisons la méthode toEpochDay () pour convertir chaque LocalDate en son jour d'époque correspondant. De même, nous pouvons vérifier que cette approche est correcte:

LocalDate start = LocalDate.of(1989, Month.OCTOBER, 14); LocalDate end = LocalDate.now(); LocalDate random = RandomDates.between(start, end); assertThat(random).isBetween(start, end);

3.2. Sans bornes

Afin de générer des dates aléatoires quelle que soit la plage, nous pouvons simplement générer un jour d'époque aléatoire:

public static LocalDate date() { int hundredYears = 100 * 365; return LocalDate.ofEpochDay(ThreadLocalRandom .current().nextInt(-hundredYears, hundredYears)); }

Notre générateur de dates aléatoires choisit un jour aléatoire de 100 ans avant et après l'époque. Encore une fois, la justification derrière cela est de générer des valeurs de date raisonnables:

LocalDate randomDay = RandomDates.date(); assertThat(randomDay).isBetween(LocalDate.MIN, LocalDate.MAX);

4. Temps aléatoire

Semblable à ce que nous avons fait avec les dates, nous pouvons générer des temporelles aléatoires avec juste des composants temporels. Pour ce faire, nous pouvons utiliser le deuxième concept du jour. Autrement dit, une heure aléatoire est égale à un nombre aléatoire représentant les secondes depuis le début de la journée.

4.1. Délimité

La classe java.time.LocalTime est une abstraction temporelle qui n'encapsule rien d'autre que des composants temporels:

public static LocalTime between(LocalTime startTime, LocalTime endTime) { int startSeconds = startTime.toSecondOfDay(); int endSeconds = endTime.toSecondOfDay(); int randomTime = ThreadLocalRandom .current() .nextInt(startSeconds, endSeconds); return LocalTime.ofSecondOfDay(randomTime); }

Afin de générer un temps aléatoire entre deux autres, nous pouvons:

  1. Générer un nombre aléatoire entre la seconde du jour des heures données
  2. Créez une heure aléatoire en utilisant ce nombre aléatoire

Nous pouvons facilement vérifier le comportement de cet algorithme de génération de temps aléatoire:

LocalTime morning = LocalTime.of(8, 30); LocalTime randomTime = RandomTimes.between(LocalTime.MIDNIGHT, morning); assertThat(randomTime) .isBetween(LocalTime.MIDNIGHT, morning) .isBetween(LocalTime.MIN, LocalTime.MAX);

4.2. Sans bornes

Même les valeurs de temps illimitées doivent être comprises entre 00:00:00 et 23:59:59, nous pouvons donc simplement implémenter cette logique par délégation:

public static LocalTime time() { return between(LocalTime.MIN, LocalTime.MAX); }

5. Conclusion

Dans ce didacticiel, nous avons réduit la définition des dates et heures aléatoires à des nombres aléatoires. Ensuite, nous avons vu comment cette réduction nous a aidés à générer des valeurs temporelles aléatoires se comportant comme des horodatages, des dates ou des heures.

Comme d'habitude, l'exemple de code est disponible over sur GitHub.