Menghasilkan Tanggal Acak di Jawa

1. Ikhtisar

Dalam tutorial ini, kita akan melihat bagaimana menghasilkan tanggal dan waktu acak dalam mode terbatas dan tidak terbatas.

Kita akan melihat bagaimana menghasilkan nilai-nilai ini menggunakan API java.util.Date lama dan juga perpustakaan tanggal-waktu baru dari Java 8.

2. Tanggal dan Waktu Acak

Tanggal dan waktu tidak lebih dari bilangan bulat 32-bit dibandingkan dengan waktu epoch , jadi kita dapat menghasilkan nilai temporal acak dengan mengikuti algoritme sederhana ini:

  1. Hasilkan nomor 32-bit acak, sebuah int
  2. Meneruskan nilai acak yang dihasilkan ke konstruktor atau pembuat tanggal dan waktu yang sesuai

2.1. Terikat Instan

java.time.I nstant adalah salah satu tanggal dan waktu tambahan baru di Jawa 8. Mereka mewakili poin sesaat pada waktu-line.

Untuk menghasilkan Sekejap acak di antara dua yang lainnya, kita dapat:

  1. Hasilkan nomor acak antara waktu detik dari Instan tertentu
  2. Buat acak Instan dengan melewati nomor acak ke ofEpochSecond () metode
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); }

Untuk mencapai lebih banyak throughput dalam lingkungan multi-utas, kami menggunakan ThreadLocalRandom untuk menghasilkan angka acak kami.

Kami dapat memverifikasi bahwa Sekejap yang dihasilkan selalu lebih besar dari atau sama dengan Sekejap pertama dan kurang dari Sekejap kedua :

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

Ingat, tentu saja, pengujian keacakan secara inheren non-deterministik dan umumnya tidak direkomendasikan dalam aplikasi nyata.

Demikian pula, dimungkinkan juga untuk menghasilkan Sekejap acak setelah atau sebelum yang lain:

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

2.2. Tanggal Terikat

Salah satu konstruktor java.util.Date menggunakan jumlah milidetik setelah epoch. Jadi, kita dapat menggunakan algoritme yang sama untuk menghasilkan Tanggal acak di antara dua lainnya:

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

Demikian pula, kami harus dapat memverifikasi perilaku ini:

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. Instan Tanpa Batas

Untuk menghasilkan Instan yang benar-benar acak , kita cukup menghasilkan integer acak dan meneruskannya ke metode ofEpochSecond () :

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

Menggunakan 32-bit detik sejak waktu epoch menghasilkan waktu acak yang lebih masuk akal, oleh karena itu kami menggunakan metode nextInt () di sini .

Selain itu, nilai ini harus tetap berada di antara nilai Instan minimum dan maksimum yang mungkin dapat ditangani oleh Java:

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

2.4. Tanggal Tidak Terbatas

Mirip dengan contoh yang dibatasi, kita dapat meneruskan nilai acak ke konstruktor Date untuk menghasilkan Tanggal acak :

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

Sejaksatuan waktu konstruktor adalah milidetik, kita mengonversi epoch detik 32-bit menjadi milidetik dengan mengalikannya dengan 1000.

Tentu saja, nilai ini masih berada di antara nilai Tanggal minimum dan maksimum yang mungkin :

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. Tanggal Acak

Hingga saat ini, kami membuat temporal acak yang berisi komponen tanggal dan waktu. Demikian pula, kita dapat menggunakan konsep epoch days untuk menghasilkan temporal acak dengan hanya komponen tanggal.

Hari epoch sama dengan jumlah hari sejak 1 Januari 1970. Jadi untuk menghasilkan tanggal acak, kita hanya perlu membuat nomor acak dan menggunakan nomor itu sebagai hari epoch.

3.1. Terikat

Kita membutuhkan abstraksi temporal yang hanya berisi komponen tanggal, jadi java.time.LocalDate tampaknya merupakan kandidat yang baik:

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

Di sini kami menggunakan metode toEpochDay () untuk mengonversi setiap LocalDate ke hari epoch yang sesuai. Demikian pula, kami dapat memverifikasi bahwa pendekatan ini benar:

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. Tak terbatas

Untuk menghasilkan tanggal acak terlepas dari rentang apa pun, kami dapat membuat hari epoch acak:

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

Generator tanggal acak kami memilih hari acak dari 100 tahun sebelum dan sesudah periode. Sekali lagi, alasan di balik ini adalah untuk menghasilkan nilai tanggal yang masuk akal:

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

4. Waktu Acak

Mirip dengan apa yang kami lakukan dengan tanggal, kami dapat menghasilkan temporal acak hanya dengan komponen waktu. Untuk melakukan itu, kita bisa menggunakan konsep hari kedua. Artinya, waktu acak sama dengan angka acak yang mewakili detik sejak permulaan hari.

4.1. Terikat

Kelas java.time.LocalTime adalah abstraksi temporal yang merangkum apa pun kecuali komponen waktu:

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

Untuk menghasilkan waktu acak di antara dua waktu lainnya, kita dapat:

  1. Hasilkan nomor acak antara hari kedua pada waktu yang ditentukan
  2. Buat waktu acak menggunakan nomor acak itu

Kami dapat dengan mudah memverifikasi perilaku algoritma generasi waktu acak ini:

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. Tak terbatas

Bahkan nilai waktu tak terbatas harus dalam rentang 00:00:00 hingga 23:59:59, jadi kita cukup mengimplementasikan logika ini dengan delegasi:

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

5. Kesimpulan

Dalam tutorial ini, kami mengurangi definisi tanggal dan waktu acak menjadi angka acak. Kemudian, kami melihat bagaimana pengurangan ini membantu kami menghasilkan nilai temporal acak yang berperilaku seperti stempel waktu, tanggal, atau waktu.

Seperti biasa, kode sampel tersedia di GitHub.