Генерація випадкових дат у Java

1. Огляд

У цьому підручнику ми побачимо, як генерувати випадкові дати та час у обмеженому та необмеженому способі.

Ми розглянемо, як згенерувати ці значення за допомогою застарілого API java.util.Date, а також нової бібліотеки дати та часу з Java 8.

2. Випадкові дата та час

Дати та час - це не що інше, як 32-розрядні цілі числа порівняно з часом епохи , тому ми можемо генерувати випадкові часові значення, дотримуючись цього простого алгоритму:

  1. Створіть випадкове 32-розрядне число, цілеспрямоване число
  2. Передайте згенероване випадкове значення відповідному конструктору дати та часу або конструктору

2.1. Обмежений миттєво

java.time.I nstant - одне з нових доповнень дати та часу в Java 8. Вони представляють миттєві точки на часовій лінії.

Для того, щоб генерувати випадковий Миттєвий проміжок часу між двома іншими, ми можемо:

  1. Створіть випадкове число між епохальними секундами заданих моментів
  2. Створіть випадковий миттєвий пошук , передавши це випадкове число методу 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); }

Для того, щоб досягти більшої пропускної здатності в багатопотокових середовищах, ми використовуємо ThreadLocalRandom для генерації наших випадкових чисел.

Ми можемо перевірити, що згенерований Миттєвий пошук завжди більше або дорівнює першому Миттєвому пошуку і менший за другий Миттєвий:

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

Звичайно, пам’ятайте, що тестування випадковості за своєю суттю не є детермінованим і, як правило, не рекомендується у реальному застосуванні.

Подібним чином можна також генерувати випадковий Миттєвий пошук після або перед іншим:

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

2.2. Дата обмеження

Один із конструкторів java.util.Date приймає кількість мілісекунд після епохи. Отже, ми можемо використовувати той самий алгоритм для генерації випадкової дати між двома іншими:

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

Аналогічним чином ми повинні мати можливість перевірити таку поведінку:

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. Без обмежень Миттєво

Для того, щоб сформувати абсолютно випадкове Миттєве ми можемо просто сформувати випадкове ціле число і передати його методу ofEpochSecond () :

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

Використовуючи 32-бітні секунди, оскільки час епохи генерує більш розумні випадкові часи, отже, ми використовуємо метод nextInt () тут .

Крім того, це значення повинно знаходитись між мінімальним і максимально можливим значенням миттєвих значень, які Java може обробити:

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

2.4. Необмежена дата

Подібно до обмеженого прикладу, ми можемо передати випадкове значення конструктору Date для генерації випадкової дати:

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

Так якодиниця часу конструктора - мілісекунди, ми перетворюємо 32-бітові епохи в мілісекунди, помноживши їх на 1000.

Звичайно, це значення по - , як і раніше між мінімальним і максимальним можливим Дата значень:

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. Випадкова дата

До цього часу ми генерували випадкові часові графіки, що містять як дату, так і компоненти часу. Подібним чином, ми можемо використовувати концепцію днів епохи, щоб генерувати випадкові часові позначення з компонентами лише дати.

День епохи дорівнює кількості днів з 1 січня 1970 року. Отже, щоб сформувати випадкову дату, нам просто потрібно сформувати випадкове число і використовувати це число як день епохи.

3.1. Обмежений

Нам потрібна тимчасова абстракція, що містить лише компоненти дати, тому java.time.LocalDate видається хорошим кандидатом:

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

Тут ми використовуємо метод toEpochDay () для перетворення кожного LocalDate у відповідний день епохи. Подібним чином ми можемо перевірити правильність такого підходу:

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. Без обмежень

Для того, щоб генерувати випадкові дати незалежно від будь-якого діапазону, ми можемо просто генерувати випадкові дні епохи:

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

Наш генератор випадкових дат вибирає випадковий день із 100 років до та після епохи. Знову ж, обгрунтування цього полягає у формуванні розумних значень дати:

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

4. Випадковий час

Подібно до того, що ми робили з датами, ми можемо генерувати випадкові тимчасові часові компоненти. Для цього ми можемо використовувати концепцію другого дня. Тобто випадковий час дорівнює випадковому числу, що представляє секунди з початку дня.

4.1. Обмежений

Клас java.time.LocalTime - це тимчасова абстракція, яка не інкапсулює нічого, крім компонентів часу:

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

Для того, щоб генерувати випадковий час між двома іншими, ми можемо:

  1. Створіть випадкове число між другим днем ​​заданого часу
  2. Створіть випадковий час, використовуючи це випадкове число

Ми можемо легко перевірити поведінку цього алгоритму генерації випадкового часу:

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. Без обмежень

Навіть необмежені значення часу повинні знаходитися в діапазоні 00:00:00 до 23:59:59, тому ми можемо просто реалізувати цю логіку шляхом делегування:

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

5. Висновок

У цьому посібнику ми звели визначення випадкових дат та часу до випадкових чисел. Потім ми побачили, як це скорочення допомогло нам генерувати випадкові часові значення, що поводяться як позначки часу, дати чи час.

Як зазвичай, зразок коду доступний на GitHub.