public long getTimeBefore(long time) {
// It would be nice to get rid of the Calendar class for speed, but it's a lot of work...
// We create this
Calendar cal = new GregorianCalendar();
cal.setTimeInMillis(time);
int minute = cal.get(Calendar.MINUTE);
int hour = cal.get(Calendar.HOUR_OF_DAY);
int dayOfMonth = cal.get(Calendar.DAY_OF_MONTH);
int month = cal.get(Calendar.MONTH) + 1; // Calendar is 0-based for this field, and we are 1-based
int year = cal.get(Calendar.YEAR);
long validMinutes = lookup[MINUTE];
long validHours = lookup[HOUR];
long validDaysOfMonth = lookup[DAY_OF_MONTH];
long validMonths = lookup[MONTH];
long validDaysOfWeek = lookup[DAY_OF_WEEK];
// Find out if we have a Day of Week or Day of Month field
boolean haveDOM = validDaysOfMonth != Long.MAX_VALUE;
boolean haveDOW = validDaysOfWeek != Long.MAX_VALUE;
boolean skippedNonLeapYear = false;
while (true) {
boolean retry = false;
// Clean up the month if it was wrapped in a previous iteration
if (month < 1) {
month += 12;
year--;
}
// get month...................................................
boolean found = false;
if (validMonths != Long.MAX_VALUE) {
for (int i = month + 11; i > (month - 1); i--) {
int testMonth = (i % 12) + 1;
// Check if the month is valid
if (((1L < < (testMonth - 1)) & validMonths) != 0) {
if ((testMonth > month) || skippedNonLeapYear) {
year--;
}
// Check there are enough days in this month (catches non leap-years trying to match the 29th Feb)
int numDays = numberOfDaysInMonth(testMonth, year);
if (!haveDOM || (numDays >= lookupMin[DAY_OF_MONTH])) {
if ((month != testMonth) || skippedNonLeapYear) {
// New DOM = min(maxDOM, prevDays); ie, the highest valid value
dayOfMonth = (numDays < = lookupMax[DAY_OF_MONTH]) ? numDays : lookupMax[DAY_OF_MONTH];
hour = lookupMax[HOUR];
minute = lookupMax[MINUTE];
month = testMonth;
}
found = true;
break;
}
}
}
skippedNonLeapYear = false;
if (!found) {
// The only time we drop out here is when we're searching for the 29th of February and no other date!
skippedNonLeapYear = true;
continue;
}
}
// Clean up if the dayOfMonth was wrapped. This takes leap years into account.
if (dayOfMonth < 1) {
month--;
dayOfMonth += numberOfDaysInMonth(month, year);
hour = lookupMax[HOUR];
continue;
}
// get day...................................................
if (haveDOM && !haveDOW) { // get day using just the DAY_OF_MONTH token
int daysInThisMonth = numberOfDaysInMonth(month, year);
int daysInPreviousMonth = numberOfDaysInMonth(month - 1, year);
// Find the highest valid day that is below the current day
for (int i = dayOfMonth + 30; i > (dayOfMonth - 1); i--) {
int testDayOfMonth = (i % 31) + 1;
// Skip over any days that don't actually exist (eg 31st April)
if ((testDayOfMonth < = dayOfMonth) && (testDayOfMonth > daysInThisMonth)) {
continue;
}
if ((testDayOfMonth > dayOfMonth) && (testDayOfMonth > daysInPreviousMonth)) {
continue;
}
if (((1L < < (testDayOfMonth - 1)) & validDaysOfMonth) != 0) {
if (testDayOfMonth > dayOfMonth) {
// We've found a valid day, but we had to move back a month
month--;
retry = true;
}
if (dayOfMonth != testDayOfMonth) {
hour = lookupMax[HOUR];
minute = lookupMax[MINUTE];
}
dayOfMonth = testDayOfMonth;
break;
}
}
if (retry) {
continue;
}
} else if (haveDOW && !haveDOM) { // get day using just the DAY_OF_WEEK token
int daysLost = 0;
int currentDOW = dayOfWeek(dayOfMonth, month, year);
for (int i = currentDOW + 7; i > currentDOW; i--) {
int testDOW = i % 7;
if (((1L < < testDOW) & validDaysOfWeek) != 0) {
dayOfMonth -= daysLost;
if (dayOfMonth < 1) {
// We've wrapped back a month
month--;
dayOfMonth += numberOfDaysInMonth(month, year);
retry = true;
}
if (currentDOW != testDOW) {
hour = lookupMax[HOUR];
minute = lookupMax[MINUTE];
}
break;
}
daysLost++;
}
if (retry) {
continue;
}
}
// Clean up if the hour has been wrapped
if (hour < 0) {
hour += 24;
dayOfMonth--;
continue;
}
// get hour...................................................
if (validHours != Long.MAX_VALUE) {
// Find the highest valid hour that is below the current hour
for (int i = hour + 24; i > hour; i--) {
int testHour = i % 24;
if (((1L < < testHour) & validHours) != 0) {
if (testHour > hour) {
// We've found an hour, but we had to move back a day
dayOfMonth--;
retry = true;
}
if (hour != testHour) {
minute = lookupMax[MINUTE];
}
hour = testHour;
break;
}
}
if (retry) {
continue;
}
}
// get minute.................................................
if (validMinutes != Long.MAX_VALUE) {
// Find the highest valid minute that is below the current minute
for (int i = minute + 60; i > minute; i--) {
int testMinute = i % 60;
if (((1L < < testMinute) & validMinutes) != 0) {
if (testMinute > minute) {
// We've found a minute, but we had to move back an hour
hour--;
retry = true;
}
minute = testMinute;
break;
}
}
if (retry) {
continue;
}
}
break;
}
// OK, all done. Return the adjusted time value (adjusting this is faster than creating a new Calendar object)
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month - 1); // Calendar is 0-based for this field, and we are 1-based
cal.set(Calendar.DAY_OF_MONTH, dayOfMonth);
cal.set(Calendar.HOUR_OF_DAY, hour);
cal.set(Calendar.MINUTE, minute);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal.getTime().getTime();
}
Find the most recent time that matches this cron expression. This time will
always be in the past, ie a lower value than the supplied time. |