schedule.java

来自「数据仓库展示程序」· Java 代码 · 共 834 行 · 第 1/3 页

JAVA
834
字号
     * Returns the next occurrence of this schedule after a given date. If
     * <code>after</code> is null, returns the first occurrence. If there are
     * no further occurrences, returns null.
     *
     * @param after if not null, returns the first occurrence after this
     *    point in time; if null, returns the first occurrence ever.
     * @param strict If <code>after</code> is an occurrence,
     *     <code>strict</code> determines whether this method returns it, or
     *     the next occurrence. If <code>strict</code> is true, the value
     *     returned is strictly greater than <code>after</code>.
     */
    public Date nextOccurrence(Date after, boolean strict) {
        if (after == null ||
                begin != null && begin.after(after)) {
            after = begin;
            strict = false;
        }
        if (after == null) {
            after = new Date(0);
        }
        Date next = nextOccurrence0(after, strict);
        // if there is an upper bound, and this is not STRICTLY before it,
        // there's no next occurrence
        if (next != null &&
                end != null &&
                !next.before(end)) {
            next = null;
        }
        return next;
    }

    private Date nextOccurrence0(Date after, boolean strict) {
        Calendar next = ScheduleUtil.createCalendar(after);
        if (tz == null || tz.getID().equals("GMT")) {
            return nextOccurrence1(next, strict);
        } else {
            int offset;
            if (next == null) {
                offset = tz.getRawOffset();
            } else {
                offset = ScheduleUtil.timezoneOffset(tz, next);
            }
            // Add the offset to the calendar, so that the calendar looks like
            // the local time (even though it is still in GMT). Suppose an
            // event runs at 12:00 JST each day. At 02:00 GMT they ask for the
            // next event. We convert this to local time, 11:00 JST, by adding
            // the 9 hour offset. We will convert the result back to GMT by
            // subtracting the offset.
            next.add(Calendar.MILLISECOND, offset);
            Date result = nextOccurrence1(next, strict);
            if (result == null) {
                return null;
            }
            Calendar resultCalendar = ScheduleUtil.createCalendar(result);
            int offset2 = ScheduleUtil.timezoneOffset(tz, resultCalendar);
            // Shift the result back again.
            resultCalendar.add(Calendar.MILLISECOND, -offset2);
            return resultCalendar.getTime();
        }
    }

    private Date nextOccurrence1(Calendar earliest, boolean strict) {
        Calendar earliestDay = ScheduleUtil.floor(earliest);
        Calendar earliestTime = ScheduleUtil.getTime(earliest);
        // first, try a later time on the same day
        Calendar nextDay = dateSchedule.nextOccurrence(earliestDay, false);
        Calendar nextTime = timeSchedule.nextOccurrence(earliestTime, strict);
        if (nextTime == null) {
            // next, try the first time on a later day
            nextDay = dateSchedule.nextOccurrence(earliestDay, true);
            nextTime = timeSchedule.nextOccurrence(ScheduleUtil.midnightTime, false);
        }
        if (nextDay == null || nextTime == null) {
            return null;
        }
        nextDay.set(Calendar.HOUR_OF_DAY, nextTime.get(Calendar.HOUR_OF_DAY));
        nextDay.set(Calendar.MINUTE, nextTime.get(Calendar.MINUTE));
        nextDay.set(Calendar.SECOND, nextTime.get(Calendar.SECOND));
        nextDay.set(Calendar.MILLISECOND, nextTime.get(Calendar.MILLISECOND));
        return nextDay.getTime();
    }
}

/**
 * A <code>TimeSchedule</code> generates a series of times within a day.
 */
interface TimeSchedule {
    /**
     * Returns the next occurrence at or after <code>after</code>. If
     * <code>after</code> is null, returns the first occurrence. If there are
     * no further occurrences, returns null.
     *
     * @param strict if true, return time must be after <code>after</code>, not
     *     equal to it
     */
    Calendar nextOccurrence(Calendar earliest, boolean strict);
}

/**
 * A <code>OnceTimeSchedule</code> fires at one and only one time.
 */
class OnceTimeSchedule implements TimeSchedule {
    Calendar time;
    OnceTimeSchedule(Calendar time) {
        ScheduleUtil.assertTrue(time != null);
        ScheduleUtil.assertTrue(ScheduleUtil.isTime(time));
        this.time = time;
    }
    public Calendar nextOccurrence(Calendar after, boolean strict) {
        if (after == null) {
            return time;
        }
        if (time.after(after)) {
            return time;
        }
        if (!strict && time.equals(after)) {
            return time;
        }
        return null;
    }
}

/**
 * A <code>DateSchedule</code> returns a series of dates.
 */
interface DateSchedule {
    /**
     * Returns the next date when this schedule fires.
     *
     * @pre earliest != null
     */
    Calendar nextOccurrence(Calendar earliest, boolean strict);
};

/**
 * A <code>DailyDateSchedule</code> fires every day.
 */
class DailyDateSchedule implements DateSchedule {
    int period;
    int beginOrdinal;
    DailyDateSchedule(Calendar begin, int period) {
        this.period = period;
        ScheduleUtil.assertTrue(period > 0, "period must be positive");
        this.beginOrdinal = ScheduleUtil.julianDay(
                begin == null ? ScheduleUtil.epochDay : begin);
    }

    public Calendar nextOccurrence(Calendar day, boolean strict) {
        day = (Calendar) day.clone();
        if (strict) {
            day.add(Calendar.DATE, 1);
        }
        while (true) {
            int ordinal = ScheduleUtil.julianDay(day);
            if ((ordinal - beginOrdinal) % period == 0) {
                return day;
            }
            day.add(Calendar.DATE, 1);
        }
    }
}

/**
 * A <code>WeeklyDateSchedule</code> fires every week. A bitmap indicates
 * which days of the week it fires.
 */
class WeeklyDateSchedule implements DateSchedule {
    int period;
    int beginOrdinal;
    int daysOfWeekBitmap;

    WeeklyDateSchedule(Calendar begin, int period, int daysOfWeekBitmap) {
        this.period = period;
        ScheduleUtil.assertTrue(period > 0, "period must be positive");
        this.beginOrdinal = ScheduleUtil.julianDay(
                begin == null ? ScheduleUtil.epochDay : begin) / 7;
        this.daysOfWeekBitmap = daysOfWeekBitmap;
        ScheduleUtil.assertTrue(
                (daysOfWeekBitmap & Schedule.allDaysOfWeekBitmap) != 0,
                "weekly schedule must have at least one day set");
        ScheduleUtil.assertTrue(
                (daysOfWeekBitmap & Schedule.allDaysOfWeekBitmap) == daysOfWeekBitmap,
                "weekly schedule has bad bits set: " + daysOfWeekBitmap);
    }

    public Calendar nextOccurrence(Calendar earliest, boolean strict) {
        earliest = (Calendar) earliest.clone();
        if (strict) {
            earliest.add(Calendar.DATE, 1);
        }
        int i = 7 + period; // should be enough
        while (i-- > 0) {
            int dayOfWeek = earliest.get(Calendar.DAY_OF_WEEK);
            if ((daysOfWeekBitmap & (1 << dayOfWeek)) != 0) {
                int ordinal = ScheduleUtil.julianDay(earliest) / 7;
                if ((ordinal - beginOrdinal) % period == 0) {
                    return earliest;
                }
            }
            earliest.add(Calendar.DATE, 1);
        }
        throw ScheduleUtil.newInternal(
                "weekly date schedule is looping -- maybe the " +
                "bitmap is empty: " + daysOfWeekBitmap);
    }
}

/**
 * A <code>MonthlyByDayDateSchedule</code> fires on a particular set of days
 * every month.
 */
class MonthlyByDayDateSchedule implements DateSchedule {
    int period;
    int beginMonth;
    int daysOfMonthBitmap;

    MonthlyByDayDateSchedule(
            Calendar begin, int period, int daysOfMonthBitmap) {
        this.period = period;
        ScheduleUtil.assertTrue(period > 0, "period must be positive");
        this.beginMonth = begin == null ? 0 : monthOrdinal(begin);
        this.daysOfMonthBitmap = daysOfMonthBitmap;
        ScheduleUtil.assertTrue(
                (daysOfMonthBitmap & Schedule.allDaysOfMonthBitmap) != 0,
                "monthly day schedule must have at least one day set");
        ScheduleUtil.assertTrue(
                (daysOfMonthBitmap & Schedule.allDaysOfMonthBitmap) ==
                daysOfMonthBitmap,
                "monthly schedule has bad bits set: " + daysOfMonthBitmap);
    }

    public Calendar nextOccurrence(Calendar earliest, boolean strict) {
        earliest = (Calendar) earliest.clone();
        if (strict) {
            earliest.add(Calendar.DATE, 1);
        }
        int i = 31 + period; // should be enough
        while (i-- > 0) {
            int month = monthOrdinal(earliest);
            if ((month - beginMonth) % period != 0) {
                // not this month! move to first of next month
                earliest.set(Calendar.DAY_OF_MONTH, 1);
                earliest.add(Calendar.MONTH, 1);
                continue;
            }
            int dayOfMonth = earliest.get(Calendar.DAY_OF_MONTH);
            if ((daysOfMonthBitmap & (1 << dayOfMonth)) != 0) {
                return earliest;
            }
            earliest.add(Calendar.DATE, 1);
            if ((daysOfMonthBitmap & (1 << Schedule.LAST_DAY_OF_MONTH)) != 0 &&
                    earliest.get(Calendar.DAY_OF_MONTH) == 1) {
                // They want us to fire on the last day of the month, and
                // now we're at the first day of the month, so we must have
                // been at the last. Backtrack and return it.
                earliest.add(Calendar.DATE, -1);
                return earliest;
            }
        }
        throw ScheduleUtil.newInternal(
                "monthly-by-day date schedule is looping -- maybe " +
                "the bitmap is empty: " + daysOfMonthBitmap);
    }

    private static int monthOrdinal(Calendar earliest) {
        return earliest.get(Calendar.YEAR) * 12 +
            earliest.get(Calendar.MONTH);
    }
}

/**
 * A <code>MonthlyByWeekDateSchedule</code> fires on particular days of
 * particular weeks of a month.
 */
class MonthlyByWeekDateSchedule implements DateSchedule {
    int period;
    int beginMonth;
    int daysOfWeekBitmap;

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?