Генериране на случайни дати в Java

1. Общ преглед

В този урок ще видим как да генерираме произволни дати и часове по ограничен и неограничен начин.

Ще разгледаме как да генерираме тези стойности, използвайки наследения API java.util.Date, а също и новата библиотека за дата и час от Java 8.

2. Случайни дата и час

Датите и часовете не са нищо повече от 32-битови цели числа в сравнение с епоха , така че можем да генерираме произволни временни стойности, като следваме този прост алгоритъм:

  1. Генерирайте произволно 32-битово число, int
  2. Предайте генерираната произволна стойност на подходящ конструктор на дата и час или конструктор

2.1. Ограничено незабавно

java.time.I nstant е едно от новите допълнения за дата и час в Java 8. Те представляват моментални точки на времевата линия.

За да генерираме случаен Мигновено между две други, можем:

  1. Генерирайте произволно число между епохалните секунди на дадените моменти
  2. Създаване на случаен Instant с другото, че на случайни числа на 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. Без ограничения Незабавно

За да генерираме напълно произволен Instant , можем просто да генерираме произволно цяло число и да го предадем на метода 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.