周期(实现了每天,每周,每月,每年的间隔一定时间重复执行的周期)

import java.util.Calendar;
import java.util.Date;

/**
 * 周期<br>
 * 实现了每天,每周,每月,每年的间隔一定时间重复执行的周期<br>
 * <pre>
 * // 每天2点执行1次
 * Period.getDailyOncePeriod(2*60*60*1000);
 * // 每周3的5点执行1次
 * Period.getWeeklyOncePeriod((3*24+5)*60*60*1000);
 * // 每天隔2小时1次
 * Period.getDailyPeriod(2*60*60*1000);
 * // 每天3点-8点隔1小时1次(6表示每天仅执行6次, 6=8-3+1)
 * Period.getDailyPeriod(1*60*60*1000, 3*60*60*1000, 6);
 * </pre>
 * 输入参数计算单位的自动转换<br>
 * <pre>
 * // 每天2点执行1次(Period.HOUR=以小时为时间单位)
 * Period.HOUR.getDailyOncePeriod(2);
 * // 每天隔半小时1次(Period.MINUTE=以分钟为时间单位)
 * Period.MINUTE.getDailyPeriod(30);
 * </pre>
 * @author zhaohuihua
 */
public class Period {

    /** 一天的毫秒数 **/
    private static final long ONEDAY = 24 * 60 * 60 * 1000L;

    /** 每天的周期 **/
    public static final int DAILY = 0;
    /** 每周的周期 **/
    public static final int WEEKLY = 1;
    /** 每月的周期 **/
    public static final int MONTHLY = 2;
    /** 每年的周期 **/
    public static final int YEARLY = 3;

    /** 以天计算的时间单位, 该单位的所有方法的时间参数都以天表示 **/
    public static final Unit DAY = new Unit(ONEDAY);
    /** 以小时计算的时间单位, 该单位的所有方法的时间参数都以小时表示 **/
    public static final Unit HOUR = new Unit(60 * 60 * 1000L);
    /** 以分计算的时间单位, 该单位的所有方法的时间参数都以分表示 **/
    public static final Unit MINUTE = new Unit(60 * 1000L);
    /** 以秒计算的时间单位, 该单位的所有方法的时间参数都以秒表示 **/
    public static final Unit SECOND = new Unit(1000L);
    /** 以毫秒计算的时间单位, 该单位的所有方法的时间参数都以毫秒表示 **/
    public static final Unit MILLISECOND = new Unit(1L);

    /** 类型(0.DAILY|1.WEEKLY|2.MONTHLY|3.YEARLY) **/
    private final int type;
    /** 间隔时间 **/
    private final long interval;
    /** 偏移时间(从每个周期的什么时间开始) **/
    private final long offset;
    /** 执行次数(每个周期的最大执行次数), 0表示无限制 **/
    private final int times;

    /**
     * 计算时的参照时间点<br>
     * 如果为null, 每次计算时读取系统时间, 并计算其下一时间点<br>
     * 如果指定了时间, 则计算该固定时间点的下一时间点
     */
    private Date date;

    /** 是否输出日志(用于测试) **/
    private static boolean log = false;

    /**
     * 私有全参数构造函数
     * @param type 类型(DAILY | WEEKLY | MONTHLY | YEARLY)
     * @param interval 间隔时间
     * @param offset 偏移时间(从每个周期的什么时间开始)
     * @param times 执行次数(每个周期的最大执行次数), 0表示无限制
     * @param date 计算时的参照时间点<br>
     * 如果为null, 每次计算时读取系统时间, 并计算其下一时间点<br>
     * 如果指定了时间, 则计算该固定时间点的下一时间点
     */
    private Period(int type, long interval, long offset, int times, Date date) {
        // 参数检查
        if(type != 0 && type != 1  && type != 2  && type != 3)
            throw new IllegalArgumentException("type");
        if(interval < 0)
            throw new IllegalArgumentException("interval");
        if(offset < 0)
            throw new IllegalArgumentException("offset");
        if(times < 0)
            throw new IllegalArgumentException("times");
        
        // 赋值
        this.type = type;
        this.interval = interval;
        this.offset = offset;
        this.times = times;
        this.date = date;
    }

    /** 类型(0.DAILY|1.WEEKLY|2.MONTHLY|3.YEARLY) **/
    public int getType() {
        return type;
    }
    /** 间隔时间 **/
    public long getInterval() {
        return interval;
    }
    /** 偏移时间(从每个周期的什么时间开始) **/
    public long getOffset() {
        return offset;
    }
    /** 执行次数(每个周期的最大执行次数), 0表示无限制 **/
    public int getTimes() {
        return times;
    }
    /**
     * 计算时的参照时间点<br>
     * 如果为null, 每次计算时读取系统时间, 并计算其下一时间点<br>
     * 如果指定了时间, 则计算该固定时间点的下一时间点
     */
    public Date getPoint() {
        return date;
    }

    /**
     * 移至某个计算所依据的时间点
     * @param date 时间点
     * @return
     */
    public Period toPoint(Date date) {
        return new Period(type, interval, offset, times, date);
    }

    /**
     * 移至下个间隔时间点, 并将该点作为计算所依据的时间点
     * @return
     */
    public Period toNextTime() {
        return new Period(type, interval, offset, times, getNextTime());
    }

    /**
     * 获取周期的下一时间点
     * @return
     */
    public Date getNextTime() {
        Date date = this.date != null ? this.date : new Date();
        return new Date(date.getTime() + getNextTimeOffset(date));
    }

    /**
     * 获取到周期的下一时间点的毫秒数
     * @return
     */
    public long getNextTimeOffset() {
        return getNextTimeOffset(date != null ? date : new Date());
    }

    /**
     * 获取到周期的下一时间点的毫秒数
     * @param date 参照时间点
     * @return
     */
    private long getNextTimeOffset(final Date date) {
        log("------------------------------");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        long result = 0L;
        // 当前毫秒数
        long current = calendar.getTimeInMillis();
        log("当前时间: " + DateTools.dateToString(date));
        // 本周期的开始时间的毫秒数
        long period = getFirstTimeInPeriod(date);
        log("本周期起点: " + DateTools.dateToString(new Date(period)));
        // 起点, 本周期第一个时间点
        long starting = period + offset;
        log("第一个时间点: " + DateTools.dateToString(new Date(starting)));
        if(current < starting) { // 小于起点
            // 距起点的毫秒数
            result = starting - current;
        } else if(interval == 0L) {
            result = 0L;
        } else {
            // 本周期已经过了的毫秒数, 相对于起点
            long elapse = current - starting;
            log("已消逝时间: " + DateTools.formatInterval(elapse));
            // 本周期已经过了几个时间间隔
            long count = elapse / interval + 1;
            log("已消逝次数: " + count);
            for(long i = 0; i < count && log; i++) {
                if(i == 5 && count > 12) {
                    log("...");
                    i = count - 5;
                }
                long temp = starting + i * interval;
                log((i + 1) + ". " + DateTools.dateToString(new Date(temp)));
            }
            // 下一时间点的毫秒数
            long nextTime = starting + count * interval;
            log("下一时间点: " + DateTools.dateToString(new Date(nextTime)));
            // 下一周期的开始时间的毫秒数
            long nextPeriod = getFirstTimeInPeriod(date, 1);
            log("下一周期: " + DateTools.dateToString(new Date(nextPeriod)));
            // 下一时间点在本周期内, 并且, 未超出本周期的次数限制
            if(nextTime < nextPeriod && (times == 0 || count < times)) {
                // 距下一时间点的毫秒数
                result = nextTime - current;
            } else {
                // 距下一周期第一个时间点的毫秒数
                result = nextPeriod + offset - current;
            }
        }
        log("结果: " + DateTools.dateToString(new Date(current + result)));
        return result;
    }
    /**
     * 获取参照时间点所在的周期的开始时间点
     * @param date 参照时间点
     * @return
     */
    private long getFirstTimeInPeriod(final Date date) {
        return getFirstTimeInPeriod(date, 0);
    }
    /**
     * 获取参照时间点所在的周期的开始时间点
     * @param date 参照时间点
     * @param periodOffset 偏移几个周期<br>
     * 如0表示当前周期, 1表示下一周期, -1表示上一周期, 类推
     * @return
     */
    private long getFirstTimeInPeriod(final Date date, int periodOffset) {
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(date);
        switch(type) {
        case DAILY:
            if(periodOffset != 0) { // 周期偏移
                periodOffset += calendar.get(Calendar.DAY_OF_MONTH);
                calendar.set(Calendar.DAY_OF_MONTH, periodOffset);
            }
            break;
        case WEEKLY:
            if(periodOffset != 0) { // 周期偏移
                periodOffset += calendar.get(Calendar.WEEK_OF_YEAR);
                calendar.set(Calendar.WEEK_OF_YEAR, periodOffset);
            }
            // 日期置于周期起始位置
            calendar.set(Calendar.DAY_OF_WEEK, 1);
            break;
        case MONTHLY:
            if(periodOffset != 0) { // 周期偏移
                periodOffset += calendar.get(Calendar.MONTH);
                calendar.set(Calendar.MONTH, periodOffset);
            }
            // 日期置于周期起始位置
            calendar.set(Calendar.DAY_OF_MONTH, 1);
            break;
        case YEARLY:
            if(periodOffset != 0) { // 周期偏移
                periodOffset += calendar.get(Calendar.YEAR);
                calendar.set(Calendar.YEAR, periodOffset);
            }
            // 日期置于周期起始位置
            calendar.set(Calendar.DAY_OF_YEAR, 1);
            break;
        }
        // 时分秒置零
        calendar.set(Calendar.HOUR_OF_DAY, 0);
        calendar.set(Calendar.MINUTE, 0);
        calendar.set(Calendar.SECOND, 0);
        calendar.set(Calendar.MILLISECOND, 0);
        return calendar.getTimeInMillis();
    }
    private void log(String msg) {
        if(log) System.out.println(msg);
    }

    /**
     * 每天执行一次的周期(时间单位为毫秒)
     * @param offset 偏移时间(每天的什么时间执行)
     * @return
     */
    public static Period getDailyOncePeriod(long offset) {
        return MILLISECOND.getDailyOncePeriod(offset);
    }
    /**
     * 每周执行一次的周期(时间单位为毫秒)
     * @param offset 偏移时间(每周的什么时间执行)
     * @return
     */
    public static Period getWeeklyOncePeriod(long offset) {
        return MILLISECOND.getWeeklyOncePeriod(offset);
    }
    /**
     * 每月执行一次的周期(时间单位为毫秒)
     * @param offset 偏移时间(每月的什么时间执行)
     * @return
     */
    public static Period getMonthlyOncePeriod(long offset) {
        return MILLISECOND.getMonthlyOncePeriod(offset);
    }
    /**
     * 每年执行一次的周期(时间单位为毫秒)
     * @param offset 偏移时间(每年的什么时间执行)
     * @return
     */
    public static Period getYearlyOncePeriod(long offset) {
        return MILLISECOND.getYearlyOncePeriod(offset);
    }
    /**
     * 每天的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @return
     */
    public static Period getDailyPeriod(long interval) {
        return MILLISECOND.getDailyPeriod(interval);
    }
    /**
     * 每天的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @param offset 偏移时间(每天的什么时间执行)
     * @return
     */
    public static Period getDailyPeriod(long interval, long offset) {
        return MILLISECOND.getDailyPeriod(interval, offset);
    }
    /**
     * 每天的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @param offset 偏移时间(每天的什么时间执行)
     * @param times 执行次数(每天的最大执行次数), 0表示无限制
     * @return
     */
    public static Period getDailyPeriod(long interval, long offset,
            int times) {
        return MILLISECOND.getDailyPeriod(interval, offset, times);
    }
    
    /**
     * 每周的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @return
     */
    public static Period getWeeklyPeriod(long interval) {
        return MILLISECOND.getWeeklyPeriod(interval);
    }
    /**
     * 每周的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @param offset 偏移时间(每周的什么时间执行)
     * @return
     */
    public static Period getWeeklyPeriod(long interval, long offset) {
        return MILLISECOND.getWeeklyPeriod(interval, offset);
    }
    /**
     * 每周的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @param offset 偏移时间(每周的什么时间执行)
     * @param times 执行次数(每周的最大执行次数), 0表示无限制
     * @return
     */
    public static Period getWeeklyPeriod(long interval, long offset,
            int times) {
        return MILLISECOND.getWeeklyPeriod(interval, offset, times);
    }

    /**
     * 每月的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @return
     */
    public static Period getMonthlyPeriod(long interval) {
        return MILLISECOND.getMonthlyPeriod(interval);
    }
    /**
     * 每月的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @param offset 偏移时间(每月的什么时间执行)
     * @return
     */
    public static Period getMonthlyPeriod(long interval, long offset) {
        return MILLISECOND.getMonthlyPeriod(interval, offset);
    }
    /**
     * 每月的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @param offset 偏移时间(每月的什么时间执行)
     * @param times 执行次数(每月的最大执行次数), 0表示无限制
     * @return
     */
    public static Period getMonthlyPeriod(long interval, long offset,
            int times) {
        return MILLISECOND.getMonthlyPeriod(interval, offset, times);
    }

    /**
     * 每年的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @return
     */
    public static Period getYearlyPeriod(long interval) {
        return MILLISECOND.getYearlyPeriod(interval);
    }
    /**
     * 每年的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @param offset 偏移时间(每年的什么时间执行)
     * @return
     */
    public static Period getYearlyPeriod(long interval, long offset) {
        return MILLISECOND.getYearlyPeriod(interval, offset);
    }
    /**
     * 每年的周期(时间单位为毫秒)
     * @param interval 间隔时间
     * @param offset 偏移时间(每年的什么时间执行)
     * @param times 执行次数(每年的最大执行次数), 0表示无限制
     * @return
     */
    public static Period getYearlyPeriod(long interval, long offset,
            int times) {
        return MILLISECOND.getYearlyPeriod(interval, offset, times);
    }

    /**
     * 时间单位<br>
     * 如: Period.HOUR = 以小时作为时间单位<br>
     * 该单位的所有方法的时间参数都以小时表示<br>
     * Period.HOUR.getDailyPeriod(2); 表示每天隔2小时执行一次的周期<br>
     * 已定义的时间单位有:<br>
     * Period.DAY = 以天作为时间单位<br>
     * Period.HOUR = 以小时作为时间单位<br>
     * Period.MINUTE = 以分作为时间单位<br>
     * Period.SECOND = 以秒作为时间单位<br>
     * Period.MILLISECOND = 以毫秒作为时间单位<br>
     */
    public static class Unit {

        /** 时间单位转换为毫秒的比率**/
        private long rate;

        /**
         * 时间单位
         * @param rate 时间单位转换为毫秒的比率
         */
        private Unit(long rate) {
            this.rate = rate;
        }

        /**
         * 每天执行一次的周期
         * @param offset 偏移时间(每天的什么时间执行)
         * @return
         */
        public Period getDailyOncePeriod(long offset) {
            return new Period(DAILY, ONEDAY, offset * rate, 1, null);
        }
        /**
         * 每周执行一次的周期
         * @param offset 偏移时间(每周的什么时间执行)
         * @return
         */
        public Period getWeeklyOncePeriod(long offset) {
            return new Period(WEEKLY, 7 * ONEDAY, offset * rate, 1, null);
        }
        /**
         * 每月执行一次的周期
         * @param offset 偏移时间(每月的什么时间执行)
         * @return
         */
        public Period getMonthlyOncePeriod(long offset) {
            return new Period(MONTHLY, 28 * ONEDAY, offset * rate, 1, null);
        }
        /**
         * 每年执行一次的周期
         * @param offset 偏移时间(每年的什么时间执行)
         * @return
         */
        public Period getYearlyOncePeriod(long offset) {
            return new Period(YEARLY, 365 * ONEDAY, offset * rate, 1, null);
        }
        /**
         * 每天的周期
         * @param interval 间隔时间
         * @return
         */
        public Period getDailyPeriod(long interval) {
            return new Period(DAILY, interval * rate, 0, 0, null);
        }
        /**
         * 每天的周期
         * @param interval 间隔时间
         * @param offset 偏移时间(每天的什么时间执行)
         * @return
         */
        public Period getDailyPeriod(long interval, long offset) {
            return new Period(DAILY, interval * rate, offset * rate, 0, null);
        }
        /**
         * 每天的周期
         * @param interval 间隔时间
         * @param offset 偏移时间(每天的什么时间执行)
         * @param times 执行次数(每天的最大执行次数), 0表示无限制
         * @return
         */
        public Period getDailyPeriod(long interval, long offset, int times) {
            return new Period(DAILY, interval*rate, offset*rate, times, null);
        }
        
        /**
         * 每周的周期
         * @param interval 间隔时间
         * @return
         */
        public Period getWeeklyPeriod(long interval) {
            return new Period(WEEKLY, interval * rate, 0, 0, null);
        }
        /**
         * 每周的周期
         * @param interval 间隔时间
         * @param offset 偏移时间(每周的什么时间执行)
         * @return
         */
        public Period getWeeklyPeriod(long interval, long offset) {
            return new Period(WEEKLY, interval * rate, offset * rate, 0, null);
        }
        /**
         * 每周的周期
         * @param interval 间隔时间
         * @param offset 偏移时间(每周的什么时间执行)
         * @param times 执行次数(每周的最大执行次数), 0表示无限制
         * @return
         */
        public Period getWeeklyPeriod(long interval, long offset, int times) {
            return new Period(WEEKLY, interval*rate, offset*rate, times, null);
        }
    
        /**
         * 每月的周期
         * @param interval 间隔时间
         * @return
         */
        public Period getMonthlyPeriod(long interval) {
            return new Period(MONTHLY, interval * rate, 0, 0, null);
        }
        /**
         * 每月的周期
         * @param interval 间隔时间
         * @param offset 偏移时间(每月的什么时间执行)
         * @return
         */
        public Period getMonthlyPeriod(long interval, long offset) {
            return new Period(MONTHLY, interval * rate, offset * rate, 0, null);
        }
        /**
         * 每月的周期
         * @param interval 间隔时间
         * @param offset 偏移时间(每月的什么时间执行)
         * @param times 执行次数(每月的最大执行次数), 0表示无限制
         * @return
         */
        public Period getMonthlyPeriod(long interval, long offset, int times) {
            return new Period(MONTHLY, interval*rate, offset*rate, times, null);
        }
    
        /**
         * 每年的周期
         * @param interval 间隔时间
         * @return
         */
        public Period getYearlyPeriod(long interval) {
            return new Period(YEARLY, interval * rate, 0, 0, null);
        }
        /**
         * 每年的周期
         * @param interval 间隔时间
         * @param offset 偏移时间(每年的什么时间执行)
         * @return
         */
        public Period getYearlyPeriod(long interval, long offset) {
            return new Period(YEARLY, interval * rate, offset * rate, 0, null);
        }
        /**
         * 每年的周期
         * @param interval 间隔时间
         * @param offset 偏移时间(每年的什么时间执行)
         * @param times 执行次数(每年的最大执行次数), 0表示无限制
         * @return
         */
        public Period getYearlyPeriod(long interval, long offset, int times) {
            return new Period(YEARLY, interval*rate, offset*rate, times, null);
        }
    }

    /** 测试 **/
    public static void main(String[] args) {
        Period.log = true;

        System.out.println("每天, 间隔1小时30分钟, 2小时后开始, 次数无限制");
        Period.MINUTE.getDailyPeriod(90, 2*60, 0)
                .toPoint(DateTools.parseDate("2009-02-05 03:10:10.000"))
                .toNextTime().toNextTime();

        System.out.println(); System.out.println();
        System.out.println("每天, 间隔1小时30分钟, 2小时后开始, 次数2");
        Period.MINUTE.getDailyPeriod(90, 2*60, 2)
                .toPoint(DateTools.parseDate("2009-02-05 03:10:10.000"))
                .toNextTime().toNextTime();

        System.out.println(); System.out.println();
        System.out.println("每周, 间隔15小时, 26小时后开始, 次数无限制");
        Period.HOUR.getWeeklyPeriod(15, 26, 0)
                .toPoint(DateTools.parseDate("2009-02-07 22:00:00.000"))
                .toNextTime().toNextTime();

        System.out.println(); System.out.println();
        System.out.println("每周, 间隔15小时, 26小时后开始, 次数无限制");
        Period.HOUR.getWeeklyPeriod(15, 26, 0)
                .toPoint(DateTools.parseDate("2009-02-05 10:10:10.000"))
                .toNextTime().toNextTime();

        System.out.println(); System.out.println();
        System.out.println("每月, 间隔15小时, 26小时后开始, 次数无限制");
        Period.HOUR.getMonthlyPeriod(15, 26, 0)
                .toPoint(DateTools.parseDate("2009-02-28 23:00:00.000"))
                .toNextTime().toNextTime();

        System.out.println(); System.out.println();
        System.out.println("每天5点执行一次");
        Period.HOUR.getDailyOncePeriod(5)
                .toPoint(DateTools.parseDate("2009-02-05 10:10:10.000"))
                .toNextTime().toNextTime().toNextTime();

        System.out.println(); System.out.println();
        System.out.println("每隔5小时执行一次");
        Period.HOUR.getYearlyPeriod(5)
                .toPoint(DateTools.parseDate("2009-02-05 18:10:10.000"))
                .toNextTime().toNextTime();

        System.out.println(); System.out.println();
        System.out.println("每周3的5点执行一次");
        Period.HOUR.getWeeklyOncePeriod(3*24+5)
                .toPoint(DateTools.parseDate("2009-02-05 10:10:10.000"))
                .toNextTime().toNextTime().toNextTime();

        Period.log = false;
    }
}


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值