Skip to content

Commit

Permalink
Take into account weekdays on weekly scheduled actions
Browse files Browse the repository at this point in the history
The actions were run just once per week without taking into account the
weekdays set by the user.

Fixes codinguser#641
  • Loading branch information
rivaldi8 committed Apr 25, 2017
1 parent 5630786 commit a74311a
Show file tree
Hide file tree
Showing 2 changed files with 85 additions and 1 deletion.
40 changes: 39 additions & 1 deletion app/src/main/java/org/gnucash/android/model/ScheduledAction.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@

import java.sql.Timestamp;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
Expand Down Expand Up @@ -224,7 +225,7 @@ private long computeNextScheduledExecutionTimeStartingAt(long startTime) {
nextScheduledExecution = nextScheduledExecution.plusDays(multiplier);
break;
case WEEK:
nextScheduledExecution = nextScheduledExecution.plusWeeks(multiplier);
nextScheduledExecution = computeNextWeeklyExecutionStartingAt(nextScheduledExecution);
break;
case MONTH:
nextScheduledExecution = nextScheduledExecution.plusMonths(multiplier);
Expand All @@ -236,6 +237,43 @@ private long computeNextScheduledExecutionTimeStartingAt(long startTime) {
return nextScheduledExecution.toDate().getTime();
}

/**
* Computes the next time that this weekly scheduled action is supposed to be
* executed starting at startTime.
*
* @param startTime LocalDateTime to use as start to compute the next schedule.
*
* @return Next run time as a LocalDateTime
*/
@NonNull
private LocalDateTime computeNextWeeklyExecutionStartingAt(LocalDateTime startTime) {
// Look into the week of startTime for another scheduled weekday
for (int weekDay : mRecurrence.getByDays() ) {
int jodaWeekDay = convertCalendarWeekdayToJoda(weekDay);
LocalDateTime candidateNextDueTime = startTime.withDayOfWeek(jodaWeekDay);
if (candidateNextDueTime.isAfter(startTime))
return candidateNextDueTime;
}

// Return the first scheduled weekday from the next due week
int firstScheduledWeekday = convertCalendarWeekdayToJoda(mRecurrence.getByDays().get(0));
return startTime.plusWeeks(mRecurrence.getMultiplier())
.withDayOfWeek(firstScheduledWeekday);
}

/**
* Converts a java.util.Calendar weekday constant to the
* org.joda.time.DateTimeConstants equivalent.
*
* @param calendarWeekday weekday constant from java.util.Calendar
* @return weekday constant equivalent from org.joda.time.DateTimeConstants
*/
private int convertCalendarWeekdayToJoda(int calendarWeekday) {
Calendar cal = Calendar.getInstance();
cal.set(Calendar.DAY_OF_WEEK, calendarWeekday);
return LocalDateTime.fromCalendarFields(cal).getDayOfWeek();
}

/**
* Set time of last execution of the scheduled action
* @param nextRun Timestamp in milliseconds since Epoch
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,9 +23,13 @@
import org.junit.Test;

import java.sql.Timestamp;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collections;

import static org.assertj.core.api.Assertions.assertThat;


/**
* Test scheduled actions
*/
Expand Down Expand Up @@ -130,6 +134,48 @@ public void testComputingTimeOfLastSchedule(){

}

/**
* Weekly actions scheduled to run on multiple weekdays should be due
* in each of them in the same week.
*
* For an action scheduled on Mondays and Thursdays, we test that, if
* the last run was on Monday, the next should be due on the Thursday
* of the same week instead of the following week.
*/
@Test
public void multiWeekdayWeeklyActions_shouldBeDueOnEachWeekdaySet() {
ScheduledAction scheduledAction = new ScheduledAction(ScheduledAction.ActionType.BACKUP);
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
recurrence.setByDays(Arrays.asList(Calendar.MONDAY, Calendar.THURSDAY));
scheduledAction.setRecurrence(recurrence);
scheduledAction.setStartTime(new DateTime(2016, 6, 6, 9, 0).getMillis());
scheduledAction.setLastRun(new DateTime(2017, 4, 17, 9, 0).getMillis()); // Monday

long expectedNextDueDate = new DateTime(2017, 4, 20, 9, 0).getMillis(); // Thursday
assertThat(scheduledAction.computeNextTimeBasedScheduledExecutionTime())
.isEqualTo(expectedNextDueDate);
}

/**
* Weekly actions scheduled with multiplier should skip intermediate
* weeks and be due in the specified weekday.
*/
@Test
public void weeklyActionsWithMultiplier_shouldBeDueOnTheWeekdaySet() {
ScheduledAction scheduledAction = new ScheduledAction(ScheduledAction.ActionType.BACKUP);
Recurrence recurrence = new Recurrence(PeriodType.WEEK);
recurrence.setMultiplier(2);
recurrence.setByDays(Collections.singletonList(Calendar.WEDNESDAY));
scheduledAction.setRecurrence(recurrence);
scheduledAction.setStartTime(new DateTime(2016, 6, 6, 9, 0).getMillis());
scheduledAction.setLastRun(new DateTime(2017, 4, 12, 9, 0).getMillis()); // Wednesday

// Wednesday, 2 weeks after the last run
long expectedNextDueDate = new DateTime(2017, 4, 26, 9, 0).getMillis();
assertThat(scheduledAction.computeNextTimeBasedScheduledExecutionTime())
.isEqualTo(expectedNextDueDate);
}

private long getTimeInMillis(int year, int month, int day) {
Calendar calendar = Calendar.getInstance();
calendar.set(year, month, day);
Expand Down

0 comments on commit a74311a

Please sign in to comment.