Структура данных для представления календарного расписания с заданной степенью детализации времени?

Я пытался придумать чисто Java-способ представления расписания на весь календарный год. Каждое расписание рассчитано на 30-минутный интервал (получасовая гранулярность).

Это будет абстрагировано с помощью методов репозитория, таких как findByDateTime()

По сути, мне нужно моделировать временные интервалы с 30-минутными гранулятами на каждый день.

То, как я взломал это, примерно так

public Map<Integer, Map<Integer, Programme>> enumerateMapOfMapSchedules() {

    int numberOfSlots = 48; //48 half hours in a day

    Map<Integer, Map<Integer, Programme>> dayToTimeScheduleMap = new HashMap<>();

    //create a key value map for each day of the year
    for (int i = 1; i < 366; i++) {

        Map<Integer, Programme> dayProgrammeScheduleMap = new HashMap<>();

        for (int y = 1; y < numberOfSlots; y++) {
            dayProgrammeScheduleMap.put(y, null);
        }

        dayToTimeScheduleMap.put(i, dayProgrammeScheduleMap);
    }
    //creates a map with 365 days and each day having a map of 48 schedule slots
    return dayToTimeScheduleMap;
}

Я понимаю, что это решение не обрабатывает и не имеет концепции года, однако, поскольку они предназначены для макетов/тестов, я согласен с этим.

Также он не обрабатывает расписание, которое перекрывается, если программа занимает два получасовых интервала.

Мой метод запроса довольно прост для поиска того, что находится в определенном слоте расписания.

public Programme findByDateTime(LocalDateTime dateTime) {

    int scheduleSlot = dateTime.getHour() * 2;

    //if its the after 30 minute schedule slot
    if (dateTime.getMinute() > 30) {
        scheduleSlot++;
    }

    return scheduleMap.get(dateTime.getDayOfYear()).get(scheduleSlot);
}

Однако для повторения всей структуры данных, чтобы увидеть, сколько существует экземпляров конкретной программы.

Мой вопрос, есть ли более простой способ сделать это?

Я пытался сделать это с реляционной БД, но было трудно легко представить периоды времени без большого количества SQL.

Любые предложения или советы по реализации приветствуются!


person tomaytotomato    schedule 15.06.2020    source источник


Ответы (1)


Дни различаются по длине

Календарное расписание имеет смысл только в контексте часового пояса и года. Политики часто меняют смещение, используемое часовыми поясами своей юрисдикции. Это означает, что дни не всегда длятся 24 часа. Такие аномалии, как перехода на летнее время (DST), означают, что день может длиться 23 часа, 25 часов. долго или что-то еще, например, 23,5 часа.

Начните сначала, считайте с шагом в 30 минут.

Поэтому, если вы хотите разделить весь год на 30-минутные сегменты, вы должны начать с первого момента первого дня определенного года в определенном часовом поясе и добавлять по 30 минут за раз, пока не достигнете нового год.

ZoneId z = ZoneId.of( "America/Montreal" );
Year year = Year.of( 2021 );
LocalDate firstOfYear = year.atDay( 1 );
ZonedDateTime start = firstOfYear.atStartOfDay( z );
List < ZonedDateTime > zdts = new ArrayList <>();

Duration duration = Duration.ofMinutes( 30 );
ZonedDateTime zdt = start;
while ( zdt.getYear() == year.getValue() )
{
    zdts.add( zdt );
    // Setup the next loop.
    zdt = zdt.plus( duration );
}

Вернуть неизменяемую копию этого списка.

List < ZonedDateTime > slots = List.copyOf( zdts );

Когда бег. Обратите внимание на что происходит в 1 или 2 часа ночи 14 марта 2021 г. и 7 ноября 2021 г.

slots = [2021-01-01T00:00-05:00[Америка/Монреаль], 2021-01-01T00:30-05:00[Америка/Монреаль], 2021-01-01T01:00-05:00[Америка /Монреаль], 2021-01-01T01:30-05:00[Америка/Монреаль], 2021-01-01T02:00-05:00[Америка/Монреаль],

2021-03-14T01:00-05:00[Америка/Монреаль], 2021-03-14T01:30-05:00[Америка/Монреаль], 2021-03-14T03:00-04:00[Америка/Монреаль] ,

2021-11-07T00:30-04:00[Америка/Монреаль], 2021-11-07T01:00-04:00[Америка/Монреаль], 2021-11-07T01:30-04:00[Америка/Монреаль] , 2021-11-07T01:00-05:00[Америка/Монреаль], 2021-11-07T01:30-05:00[Америка/Монреаль], 2021-11-07T02:00-05:00[Америка/Монреаль ],

2021-12-31T22:00-05:00[Америка/Монреаль], 2021-12-31T22:30-05:00[Америка/Монреаль], 2021-12-31T23:00-05:00[Америка/Монреаль] , 2021-12-31T23:30-05:00[Америка/Монреаль]]

Прогнозы на будущее ненадежны!

Но будьте осторожны: политики часто меняют смещение, используемое в зоне! Это происходит гораздо чаще, чем вы, вероятно, думаете. Политики даже ухудшились из-за этого, сократив свое предупреждение с нескольких лет до нескольких месяцев или даже нескольких недель, как это недавно наблюдалось в Турции и Марокко, и даже вообще без предупреждения, как это наблюдается в Северной Корее.

Таким образом, вы не можете надежно прогнозировать будущее, используя описанный выше подход.

Слот математика

Я полагаю, вы могли бы подойти к проблеме временных интервалов по-другому. Подсчитайте таким образом количество целых слотов в течение года.

ZoneId z = ZoneId.of( "America/Montreal" );
Year year = Year.of( 2021 );
LocalDate firstOfYear = year.atDay( 1 );
ZonedDateTime start = firstOfYear.atStartOfDay( z );
ZonedDateTime end = start.plusYears( 1 );

Duration slotLength = Duration.ofMinutes( 30 );
long wholeSlotsInYear = Duration.between( start , end ).dividedBy( slotLength );

Затем вы можете перейти к точке в году, умножив продолжительность и добавив результат к началу года.

int slotNumber = 22;
Duration jump = slotLength.multipliedBy( slotNumber - 1 );  // Subtract one to change an ordinal number into a zero-based index. 
ZonedDateTime slot22 = start.plus( jump );

Отслеживание книги назначений

Если вы записываетесь на прием, например, в парикмахерскую или стоматологическую клинику, обычный подход заключается в отслеживании года-месяца-дня в определенное время суток. Но отслеживайте часовой пояс отдельно. Поэтому используйте LocalDateTime с отдельным ZoneId в вашей модели Java. В таблице базы данных используйте пару столбцов, один из которых имеет тип, аналогичный стандартному SQL-типу TIMESTAMP WITHOUT TIME ZONE, а другой столбец текстового типа, содержащий имя часового пояса, например America/Montreal или Africa/Tunis.

При построении графика применяйте зону для определения момента. В Java это означает применение ZoneId к LocalDateTime для получения ZonedDateTime.

Вам нужно четко усвоить основную идею о том, что LocalDateTime объект не представляет момент. В нашем примере 15:00 23 числа следующего года может означать 15:00 в Токио, Япония, или 15:00 в Толедо, штат Огайо, США, два совершенно разных момента с разницей в несколько часов. LocalDateTime по своей сути неоднозначен. Таким образом, необходимо также хранить часовой пояс, но отдельно.

LocalDateTime ldt = LocalDateTime.of( 2021 , 1 , 23 , 15 , 0 , 0 , 0 ) ;
ZoneId z = ZoneId.of( "America/Montreal" ) ;
ZonedDateTime zdt = ldt.atZone( z ) ;  // Determine a moment.

Посмотрите тот же момент в формате UTC, извлекая файл Instant.

Instant instant = zdt.toInstant() ;
person Basil Bourque    schedule 16.06.2020