Generación de fechas aleatorias en Java

1. Información general

En este tutorial, veremos cómo generar fechas y horas aleatorias de manera limitada y sin límites.

Veremos cómo generar estos valores utilizando la API heredada java.util.Date y también la nueva biblioteca de fecha y hora de Java 8.

2. Fecha y hora aleatorias

Las fechas y horas no son más que enteros de 32 bits en comparación con un tiempo de época , por lo que podemos generar valores temporales aleatorios siguiendo este sencillo algoritmo:

  1. Genere un número aleatorio de 32 bits, un int
  2. Pase el valor aleatorio generado a un constructor o constructor de fecha y hora apropiado

2.1. Instantáneo limitado

java.time.I nstant es una de las nuevas adiciones de fecha y hora en Java 8. Representan puntos instantáneos en la línea de tiempo.

Para generar un Instant aleatorio entre otros dos, podemos:

  1. Generar un número aleatorio entre los segundos época de los dados Instantes
  2. Cree el Instant aleatorio pasando ese número aleatorio al método 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); }

Para lograr un mayor rendimiento en entornos de subprocesos múltiples, estamos usando ThreadLocalRandom para generar nuestros números aleatorios.

Podemos verificar que el Instant generado es siempre mayor o igual que el primer Instant y es menor que el segundo 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);

Recuerde, por supuesto, que probar la aleatoriedad es intrínsecamente no determinista y generalmente no se recomienda en una aplicación real.

Del mismo modo, también es posible generar un Instant aleatorio antes o después de otro:

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

2.2. Fecha acotada

Uno de los constructores java.util.Date toma el número de milisegundos después de la época. Entonces, podemos usar el mismo algoritmo para generar una Fecha aleatoria entre otros dos:

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

Del mismo modo, deberíamos poder verificar este comportamiento:

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. Instantáneo ilimitado

Para generar un Instant totalmente aleatorio , simplemente podemos generar un número entero aleatorio y pasarlo al método ofEpochSecond () :

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

El uso de segundos de 32 bits desde el tiempo de época genera tiempos aleatorios más razonables, por lo que estamos usando el método nextInt () aquí .

Además, este valor debe estar aún entre los valores instantáneos mínimos y máximos posibles que Java puede manejar:

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

2.4. Fecha ilimitada

De manera similar al ejemplo acotado, podemos pasar un valor aleatorio al constructor de Date para generar una Fecha aleatoria :

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

Desde elLa unidad de tiempo del constructor es milisegundos, estamos convirtiendo los segundos de época de 32 bits a milisegundos multiplicándolos por 1000.

Ciertamente, este valor todavía se encuentra entre los valores de fecha mínimo y máximo posibles :

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. Fecha aleatoria

Hasta ahora, generamos temporales aleatorios que contienen componentes de fecha y hora. De manera similar, podemos usar el concepto de días de época para generar temporales aleatorios con solo componentes de fecha.

Un día de época es igual al número de días desde el 1 de enero de 1970. Entonces, para generar una fecha aleatoria, solo tenemos que generar un número aleatorio y usar ese número como el día de época.

3.1. Encerrado

Necesitamos una abstracción temporal que contenga solo componentes de fecha, por lo que java.time.LocalDate parece un buen candidato:

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

Aquí estamos usando el método toEpochDay () para convertir cada LocalDate en su día de época correspondiente. Del mismo modo, podemos verificar que este enfoque es correcto:

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. Ilimitado

Para generar fechas aleatorias independientemente de cualquier rango, simplemente podemos generar un día de época aleatorio:

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

Nuestro generador de fecha aleatoria elige un día aleatorio de 100 años antes y después de la época. Nuevamente, la razón detrás de esto es generar valores de fecha razonables:

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

4. Tiempo aleatorio

De manera similar a lo que hicimos con las fechas, podemos generar temporales aleatorios con solo componentes de tiempo. Para hacer eso, podemos usar el concepto del segundo del día. Es decir, un tiempo aleatorio es igual a un número aleatorio que representa los segundos desde el comienzo del día.

4.1. Encerrado

La clase java.time.LocalTime es una abstracción temporal que encapsula nada más que componentes de tiempo:

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

Para generar un tiempo aleatorio entre otros dos, podemos:

  1. Genera un número aleatorio entre el segundo del día de las horas dadas
  2. Crea un tiempo aleatorio usando ese número aleatorio

Podemos verificar fácilmente el comportamiento de este algoritmo de generación de tiempo aleatorio:

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. Ilimitado

Incluso los valores de tiempo ilimitados deben estar en el rango de 00:00:00 hasta las 23:59:59, por lo que simplemente podemos implementar esta lógica por delegación:

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

5. Conclusión

En este tutorial, redujimos la definición de fechas y horas aleatorias a números aleatorios. Luego, vimos cómo esta reducción nos ayudó a generar valores temporales aleatorios que se comportan como marcas de tiempo, fechas u horas.

Como de costumbre, el código de muestra está disponible en GitHub.