Треки
/
Java
Java
/
Вправи
/
Постійні клієнти салону краси
Постійні клієнти салону краси

Постійні клієнти салону краси

Навчальна вправа

Вступ

Коригувальники TemporalAdjusters

Коли ми працюємо з обʼєктами java.time на кшталт LocalDate, часто виникає потреба у складних коригуваннях дати, як-от пошук «другого вівторка місяця». Замість того щоб обчислювати це вручну, можна скористатися TemporalAdjusters.

Утилітний клас java.time.temporal.TemporalAdjusters (звернімо увагу на «s» в кінці) надає статичні методи для багатьох типових коригувань дати. Ці коригування застосовують через метод .with() на часовому обʼєкті (наприклад, LocalDate).

Note

Використання with(TemporalAdjuster) повертає новий екземпляр і не оновлює наявний, оскільки обʼєкти java.time є незмінними.

import java.time.LocalDate;
import java.time.Month;
import java.time.DayOfWeek;
import java.time.temporal.TemporalAdjusters;

LocalDate date = LocalDate.of(2024, Month.AUGUST, 15); // 2024-08-15 (a Thursday)

// Use a predefined adjuster: find the last day of the month
LocalDate lastDay = date.with(TemporalAdjusters.lastDayOfMonth());
// => 2024-08-31

Ось кілька ключових коригувальників, корисних для обчислень у розкладі:

Пошук порядкового дня тижня в місяці

Скористаймося dayOfWeekInMonth(int ordinal, DayOfWeek dayOfWeek), щоб знайти n-те входження дня тижня.

// Find the second Tuesday of August 2024
LocalDate secondTuesday = date.with(TemporalAdjusters.dayOfWeekInMonth(2, DayOfWeek.TUESDAY));
// => 2024-08-13

Пошук першого та останнього дня тижня в місяці

Скористаймося firstInMonth(DayOfWeek dayOfWeek) або lastInMonth(DayOfWeek dayOfWeek).

// Find the last Monday of August 2024
LocalDate lastMonday = date.with(TemporalAdjusters.lastInMonth(DayOfWeek.MONDAY));
// => 2024-08-26

Відносні коригування (наступний і попередній)

Коригувальники на кшталт next(), previous(), nextOrSame() і previousOrSame() знаходять наступне або попереднє входження заданого дня тижня відносно поточної дати.

  • next()/previous(): не враховують поточну дату, навіть якщо вона збігається.
  • nextOrSame()/previousOrSame(): враховують поточну дату, якщо вона збігається.
LocalDate thursday = LocalDate.of(2024, Month.AUGUST, 15); // A Thursday

// Find the next Thursday (exclusive)
LocalDate nextThursday = thursday.with(TemporalAdjusters.next(DayOfWeek.THURSDAY));
// => 2024-08-22

// Find the next Thursday (inclusive) - returns the same date
LocalDate nextOrSameThursday = thursday.with(TemporalAdjusters.nextOrSame(DayOfWeek.THURSDAY));
// => 2024-08-15

// Find the previous Monday (exclusive)
LocalDate previousMonday = thursday.with(TemporalAdjusters.previous(DayOfWeek.MONDAY));
// => 2024-08-12

Власні коригувальники

TemporalAdjuster - це функціональний інтерфейс, тому власний коригувальник можна написати як лямбду, що приймає часове значення для коригування і повертає скориговане. Його застосовують через .with() так само, як і визначені наперед коригувальники, і всередині він може використовувати їх.

import java.time.temporal.TemporalAdjuster;

// An adjuster that moves a weekend date to the following Monday
TemporalAdjuster skipWeekend = temporal -> {
    DayOfWeek day = DayOfWeek.from(temporal);
    if (day == DayOfWeek.SATURDAY || day == DayOfWeek.SUNDAY) {
        return temporal.with(TemporalAdjusters.next(DayOfWeek.MONDAY));
    }
    return temporal;
};

LocalDate saturday = LocalDate.of(2024, Month.AUGUST, 17);
LocalDate monday = saturday.with(skipWeekend);
// => 2024-08-19

Використання цих коригувальників із класу TemporalAdjusters може значно спростити роботу з датами.

Вказівки

Салон краси в Нью-Йорку, з яким ми допомагали у вправі «Запис на красу», процвітає, і багато його клієнтів стали постійними. Замість того щоб записуватися на кожен візит окремо, постійний клієнт має сталу домовленість: «кожен другий вівторок», «остання пʼятниця місяця» тощо. Власниця попросила нас зробити помічник, який обчислює дати, що стоять за цими домовленостями, а також дати щомісячної рутини самого салону.

1. Знайти регулярний запис клієнта

Реалізуйте метод SalonCalendar.standingAppointment(), який приймає рік, місяць, номер повторення (1 для першого, 2 для другого тощо) і день тижня. Він повертає дату відповідного повторення цього дня тижня в межах місяця:

SalonCalendar calendar = new SalonCalendar();
calendar.standingAppointment(2024, Month.AUGUST, 2, DayOfWeek.TUESDAY);
// => LocalDate.of(2024, 8, 13)

2. Знайти останній у місяці запис клієнта

Деяким постійним клієнтам до вподоби останній такий день тижня в місяці. Реалізуйте метод SalonCalendar.lastAppointment(), який приймає рік, місяць і день тижня та повертає дату останнього повторення цього дня тижня в межах місяця:

SalonCalendar calendar = new SalonCalendar();
calendar.lastAppointment(2024, Month.AUGUST, DayOfWeek.FRIDAY);
// => LocalDate.of(2024, 8, 30)

3. Знайти найближчий вільний день для клієнта без запису

Клієнт на рецепції просить «найближчу суботу». Якщо день, у який він звертається, уже субота, то йдеться саме про цей день. Реалізуйте метод SalonCalendar.nextAvailable(), який приймає дату й день тижня та повертає першу дату, що не раніше за задану і припадає на цей день тижня:

SalonCalendar calendar = new SalonCalendar();
calendar.nextAvailable(LocalDate.of(2024, 8, 15), DayOfWeek.SATURDAY);
// => LocalDate.of(2024, 8, 17)
calendar.nextAvailable(LocalDate.of(2024, 8, 17), DayOfWeek.SATURDAY);
// => LocalDate.of(2024, 8, 17)

4. Знайти день закриття обліку

Салон закриває облік в останній день кожного місяця. Проте на вихідних салон не працює, тож якщо останній день місяця припадає на суботу чи неділю, облік закривають у пʼятницю перед цим. Реалізуйте метод SalonCalendar.bookkeepingDay(), який приймає рік і місяць та повертає день закриття обліку в цьому місяці:

SalonCalendar calendar = new SalonCalendar();
calendar.bookkeepingDay(2024, Month.SEPTEMBER);
// => LocalDate.of(2024, 9, 30)
calendar.bookkeepingDay(2024, Month.AUGUST);
// => LocalDate.of(2024, 8, 30)

5. Використати правило вихідних ще й для зарплати

Працівники отримують зарплату 15-го числа кожного місяця, і діє те саме правило вихідних: коли 15-те припадає на суботу чи неділю, зарплату виплачують у пʼятницю перед цим. Замість того щоб писати це правило вдруге, зробімо з нього повторно використовуваний коригувальник.

Реалізуйте метод SalonCalendar.precedingWorkday(), який повертає TemporalAdjuster: він залишає будній день без змін, а суботу чи неділю переносить на попередню пʼятницю. Потім реалізуйте метод SalonCalendar.payday(), який приймає рік і місяць, використовуючи цей коригувальник:

SalonCalendar calendar = new SalonCalendar();
LocalDate.of(2024, 6, 15).with(calendar.precedingWorkday());
// => LocalDate.of(2024, 6, 14)
calendar.payday(2024, Month.JUNE);
// => LocalDate.of(2024, 6, 14)
calendar.payday(2024, Month.AUGUST);
// => LocalDate.of(2024, 8, 15)

Цілком можна повернутися й використати коригувальник так само і в bookkeepingDay().

Редагувати через GitHub Посилання відкривається в новому вікні або вкладці
Java Exercism

Час розпочати Постійні клієнти салону краси?

Зареєструйтеся на Exercism, щоб вивчати й опановувати Java, а також 27 концепцій159 вправ та справжнє наставництво від людей, і все це безкоштовно.