TimeUtil { public static Instant offsetInstant(Instant origin, Schedule schedule, int offset) { Preconditions.checkArgument(isAligned(origin, schedule), "unaligned origin"); return schedule.wellKnown().unit() .map(unit -> { try { return origin.plus(offset, unit); } catch (UnsupportedTemporalTypeException ignored) { return null; } }) .orElseGet(() -> { final ExecutionTime executionTime = ExecutionTime.forCron(cron(schedule)); ZonedDateTime time = origin.atZone(UTC); for (int i = 0; i < Math.abs(offset); i++) { final Optional<ZonedDateTime> execution = offset <= 0 ? executionTime.lastExecution(time) : executionTime.nextExecution(time); time = execution .orElseThrow(AssertionError::new); } return time.toInstant(); }); } private  TimeUtil(); static Instant lastInstant(Instant instant, Schedule schedule); static Instant previousInstant(Instant instant, Schedule schedule); static Instant nextInstant(Instant instant, Schedule schedule); static List<Instant> instantsInRange(Instant firstInstant, Instant lastInstant,
                                              Schedule schedule); static List<Instant> instantsInReversedRange(Instant firstInstant, Instant lastInstant,
                                                      Schedule schedule); static boolean isAligned(Instant instant, Schedule schedule); static ZonedDateTime addOffset(ZonedDateTime time, String offset); static ZonedDateTime subtractOffset(ZonedDateTime time, String offset); static Cron cron(Schedule schedule); static Instant offsetInstant(Instant origin, Schedule schedule, int offset);  }
@Test @Parameters({ "2018-01-19T09:00:00.00Z, hours, 2018-01-19T07:00:00.00Z", "2018-01-19T00:00:00.00Z, days, 2018-01-17T00:00:00.00Z", "2018-01-15T00:00:00.00Z, weeks, 2018-01-01T00:00:00Z", "2018-01-01T00:00:00.00Z, months, 2017-11-01T00:00:00.00Z", "2018-01-01T00:00:00.00Z, years, 2016-01-01T00:00:00.00Z", }) public void shouldGetCorrectInstantWithNegativeOffset(String origin, String schedule, String expected) { assertThat(offsetInstant(parse(origin), Schedule.parse(schedule), -2), is(parse(expected))); }
@Test @Parameters({ "2018-01-19T09:00:00.00Z, hours, 2018-01-19T11:00:00.00Z", "2018-01-19T00:00:00.00Z, days, 2018-01-21T00:00:00.00Z", "2018-01-15T00:00:00.00Z, weeks, 2018-01-29T00:00:00Z", "2018-01-01T00:00:00.00Z, months, 2018-03-01T00:00:00.00Z", "2018-01-01T00:00:00.00Z, years, 2020-01-01T00:00:00.00Z", }) public void shouldGetCorrectInstantWithPositiveOffset(String origin, String schedule, String expected) { assertThat(offsetInstant(parse(origin), Schedule.parse(schedule), 2), is(parse(expected))); }
@Test @Parameters({ "2018-01-19T09:00:00.00Z, hours, 2018-01-19T09:00:00.00Z", "2018-01-19T00:00:00.00Z, days, 2018-01-19T00:00:00.00Z", "2018-01-15T00:00:00.00Z, weeks, 2018-01-15T00:00:00Z", "2018-01-01T00:00:00.00Z, months, 2018-01-01T00:00:00.00Z", "2018-01-01T00:00:00.00Z, years, 2018-01-01T00:00:00.00Z", }) public void shouldGetCorrectInstantWithZeroOffset(String origin, String schedule, String expected) { assertThat(offsetInstant(parse(origin), Schedule.parse(schedule), 0), is(parse(expected))); }
@Test public void shouldGetCorrectInstantWithNegativeOffsetForCronSchedule() { assertThat(offsetInstant(parse("2018-01-19T09:00:00.00Z"), EVERY_5_MINUTES, -2), is(parse("2018-01-19T08:50:00.00Z"))); }
@Test public void shouldGetCorrectInstantWithPositiveOffsetForCronSchedule() { assertThat(offsetInstant(parse("2018-01-19T09:00:00.00Z"), EVERY_5_MINUTES, 2), is(parse("2018-01-19T09:10:00.00Z"))); }
@Test public void shouldGetCorrectInstantWithZeroOffsetForCronSchedule() { assertThat(offsetInstant(parse("2018-01-19T09:00:00.00Z"), EVERY_5_MINUTES, 0), is(parse("2018-01-19T09:00:00.00Z"))); }
@Test public void shouldGetExceptionIfReferenceInstantIsNotAlignedWithSchedule() { expect.expect(IllegalArgumentException.class); expect.expectMessage("unaligned origin"); offsetInstant(parse("2016-01-19T09:10:00.00Z"), Schedule.HOURS, 0); }