Issue503Test.java
/*
* Copyright 2015 jmrozanec Licensed under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law
* or agreed to in writing, software distributed under the License is
* distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the specific language
* governing permissions and limitations under the License.
*/
package com.cronutils;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import com.cronutils.model.Cron;
import com.cronutils.model.definition.CronConstraintsFactory;
import com.cronutils.model.definition.CronDefinition;
import com.cronutils.model.definition.CronDefinitionBuilder;
import com.cronutils.model.time.ExecutionTime;
import com.cronutils.parser.CronParser;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.IntStream;
class Issue503Test {
/** Note: Cron-Default is 1970 */
private static final int VALID_YEAR_MIN = 1900;
private static final int VALID_YEAR_MAX = 2099;
private static final int LEAP_YEAR_DAY_COUNT = 366;
private CronParser parser;
@BeforeEach
void setUp() {
final CronDefinition cronDefinitionWithDayOfYearSupport = CronDefinitionBuilder.defineCron().withSeconds().and().withMinutes().and().withHours().and().withDayOfMonth().withValidRange(1, 31).supportsL().supportsW().supportsLW().supportsQuestionMark().and().withMonth().and().withDayOfWeek().withValidRange(1, 7).withMondayDoWValue(2).supportsHash().supportsL().supportsQuestionMark().and().withYear().withValidRange(VALID_YEAR_MIN, VALID_YEAR_MAX).optional().and().withDayOfYear().supportsQuestionMark().withValidRange(1, LEAP_YEAR_DAY_COUNT).optional().and().withCronValidation(CronConstraintsFactory.ensureEitherDayOfYearOrMonth()).withCronValidation(CronConstraintsFactory.ensureEitherDayOfWeekOrDayOfMonth()).instance();
parser = new CronParser(cronDefinitionWithDayOfYearSupport);
}
@Test
void testCustomQuarterlySchedule() {
final String customQuaterlyStartingWithDay47OfYear = "0 0 0 ? * ? * 47/91";
final LocalDateTime[] expectedExecutionTimes = IntStream.range(0, 4).mapToObj(i -> LocalDateTime.of(2017, 1, 1, 0, 0).withDayOfYear(47 + i * 91)).toArray(LocalDateTime[]::new);
final LocalDateTime[] scheduledDates = executionTimesFor(customQuaterlyStartingWithDay47OfYear, expectedExecutionTimes[0], expectedExecutionTimes[expectedExecutionTimes.length - 1].plusDays(1)).stream().toArray(LocalDateTime[]::new);
assertArrayEquals(expectedExecutionTimes, scheduledDates, "Scheduled dates should match precalculated dates.");
}
@Test
void testCustomQuarterlyScheduleForDesignatedYear2017() {
final String customQuaterlyStartingWithDay47OfYear = "0 0 0 ? * ? 2017 47/91";
final LocalDateTime[] expectedExecutionTimes = IntStream.range(0, 4).mapToObj(i -> LocalDateTime.of(2017, 1, 1, 0, 0).withDayOfYear(47 + i * 91)).toArray(LocalDateTime[]::new);
final LocalDateTime[] scheduledDates = executionTimesFor(customQuaterlyStartingWithDay47OfYear, expectedExecutionTimes[0], expectedExecutionTimes[expectedExecutionTimes.length - 1].plusDays(1)).stream().toArray(LocalDateTime[]::new);
assertArrayEquals(expectedExecutionTimes, scheduledDates, "Scheduled dates should match precalculated dates.");
}
/**
* The list of scheduled dates defined by the given cron expression included in
* the denoted interval.
*
* @param xquartzCronExpression The extended Quartz cron expression to use.
* @param start The start date to use, inclusive.
* @param end The end date to use, exclusive.
* @return the list of scheduled dates, may be empty. Never {@code null}.
*/
private List<LocalDateTime> executionTimesFor(final String xquartzCronExpression, final LocalDateTime start, final LocalDateTime end) {
//Preconditions.checkArgument(Objects.requireNonNull(start).isBefore(Objects.requireNonNull(end)));
final Cron cron = parser.parse(xquartzCronExpression);
return executionTimesFor(cron, start, end);
}
/**
* The list of scheduled dates defined by the given cron expression included in
* the denoted interval.
*
* @param cron The cron to use.
* @param start The start date to use, inclusive.
* @param end The end date to use, exclusive.
* @return the list of scheduled dates, may be empty. Never {@code null}.
*/
private List<LocalDateTime> executionTimesFor(final Cron cron, final LocalDateTime start, final LocalDateTime end) {
final ExecutionTime cronExecutionTime = ExecutionTime.forCron(cron);
final ArrayList<LocalDateTime> scheduledDates = new ArrayList<>();
ZonedDateTime nextScheduledDate = ZonedDateTime.of(start, ZoneId.systemDefault()).minusSeconds(1);
final ZonedDateTime upperBoundExclusive = ZonedDateTime.of(end, ZoneId.systemDefault());
do {
nextScheduledDate = cronExecutionTime.nextExecution(nextScheduledDate).orElse(null);
if (nextScheduledDate == null || !nextScheduledDate.isBefore(upperBoundExclusive))
break;
scheduledDates.add(nextScheduledDate.toLocalDateTime());
} while (true);
return Collections.unmodifiableList(scheduledDates);
}
}