com.catify.processengine.core.util.TimerUtil.java Source code

Java tutorial

Introduction

Here is the source code for com.catify.processengine.core.util.TimerUtil.java

Source

/**
 * *******************************************************
 * Copyright (C) 2013 catify <info@catify.com>
 * *******************************************************
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *         http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
package com.catify.processengine.core.util;

import java.util.ArrayList;
import java.util.List;

import org.joda.time.DateTime;
import org.joda.time.Period;
import org.joda.time.format.DateTimeFormatter;
import org.joda.time.format.ISODateTimeFormat;
import org.joda.time.format.ISOPeriodFormat;
import org.joda.time.format.PeriodFormatter;

/**
 * Util class to calculate time stamps based on ISO 8601 dates.
 * 
 * @author claus straube
 *
 */
public class TimerUtil {

    private static final DateTimeFormatter dateFormatter = ISODateTimeFormat.dateTimeNoMillis();
    private static final PeriodFormatter periodformatter = ISOPeriodFormat.standard();

    /**
     * Calculates the time stamp in millis for a 
     * given ISO 8601 date (e.g. 2013-04-09T16:34:08Z). 
     * The date must have a time zone.
     * 
     * @param isoDate ISO 8601 date as {@link String}
     * @return time stamp in millis
     */
    public static long calculateTimeToFireForDate(String isoDate) {
        DateTime time = dateFormatter.parseDateTime(isoDate);
        return time.getMillis();
    }

    /**
     * Calculates a list of time stamps in millis. For each
     * repetition a value will be calculated based on the time
     * to fire time of the previous repetition. If the 'R' is 
     * empty (e.g. R/PT1M), which means an unbounded number of repetitions,
     * the next time to fire is calculated.<br/><br/>
     * 
     * You can have the following scenarios:<br/>
     * Cycle with duration and bounded repetitions: R5/PT1M<br/>
     * Cycle with duration and unbounded repetitions: R/PT1M<br/>
     * Cycle with duration, bounded repetitions and start date: R3/2013-04-09T16:34:08Z/P1D<br/> 
     * Cycle with duration, unbounded repetitions and start date: R/2013-04-09T16:34:08Z/P1D<br/>
     * Cycle with duration, bounded repetitions and end date: R3/PT1H/2013-01-30T23:01:00Z<br/> 
     * Cycle with duration, unbounded repetitions and end date: R/PT1H/2013-01-30T23:01:00Z<br/>
     * 
     * @param now actual time stamp in millis
     * @param isoDate as {@link String} e.g. R7/2013-04-09T16:34:08Z/P1D
     * @return {@link List} of time stamps in millis
     */
    public static List<Long> calculateTimeToFireForCycle(long now, String isoDate) {
        List<Long> result = new ArrayList<Long>();

        String[] split = isoDate.split("/");
        if (split.length < 2 || split.length > 3) {
            throw new IllegalArgumentException("A ISO 8601 date for a cylce shout have a repeat and duration "
                    + "section, or a repeat, date and duration section separated by a slash (e.g R5/PT3M).");
        }

        // repeat is always the first one
        String repeat = split[0];
        // get the repeats
        if (!repeat.startsWith("R")) {
            throw new IllegalArgumentException(
                    "A ISO 8601 repeat should start with" + " a 'R', followed by the number of cycles.");
        }
        // get all after the 'R'
        repeat = repeat.substring(1);
        boolean unbounded = false;
        if (repeat.equals("")) {
            /*
             * if we have a unbounded number of repetitions,
             * calculate the next fire time.
             */
            repeat = "1";
            unbounded = true;
        }
        int r = Integer.parseInt(repeat);
        DateTime baseTime = new DateTime(now);

        if (split.length == 2) {
            Period period = periodformatter.parsePeriod(split[1]);
            // calculate the timestamps for the cycles
            for (int i = 0; i < r; i++) {
                baseTime = baseTime.plus(period);
                result.add(baseTime.getMillis());
            }
        }

        // we have start or end date
        if (split.length == 3) {

            if (split[1].startsWith("P")) {
                /*
                 * end date -- e.g. R4/PT1H/2013-01-30T23:00:00Z
                 */
                DateTime end = dateFormatter.parseDateTime(split[2]);
                Period period = periodformatter.parsePeriod(split[1]);
                if (unbounded) {
                    /*
                     * --> R/PT1H/2013-01-30T23:00:00Z <--
                     * calculate all times to fire, until the 
                     * end date is reached.
                     */
                    while (baseTime.isBefore(end)) {
                        baseTime = baseTime.plus(period);
                        result.add(baseTime.getMillis());
                    }
                } else {
                    /*
                     * --> R4/PT1H/2013-01-30T23:00:00Z <--
                     * calculate all times to fire until the 
                     * end date, or the maximum number
                     * of cycles is reached.
                     */
                    for (int i = 0; i < r; i++) {
                        baseTime = baseTime.plus(period);
                        if (baseTime.isBefore(end)) {
                            result.add(baseTime.getMillis());
                        }
                    }
                }
            } else if (split[2].startsWith("P")) {
                /*
                 * start date -- e.g. R/2013-04-09T16:34:08Z/P1D
                 */
                DateTime start = dateFormatter.parseDateTime(split[1]);
                Period period = periodformatter.parsePeriod(split[2]);
                if (unbounded) {
                    /*
                     * --> R/2013-04-09T16:34:08Z/P1D <--
                     * unbounded cycle with start date
                     */
                    if (start.isBefore(now)) {
                        /*
                         * if the start date is in the past,
                         * calculate the next time to fire based
                         * on the 'now' time.
                         */
                        baseTime = baseTime.plus(period);
                        result.add(baseTime.getMillis());
                    } else {
                        /* 
                         * if the start date is in the future,
                         * the first time to fire is the start
                         * date. 
                         */
                        result.add(start.getMillis());
                    }
                } else {
                    /*
                     * --> R7/2013-04-09T16:34:08Z/P1D <--
                     * bounded cycle with start date
                     */
                    baseTime = new DateTime(start.getMillis());
                    // the first entry is the start date
                    result.add(baseTime.getMillis());
                    for (int i = 0; i < r; i++) {
                        baseTime = baseTime.plus(period);
                        result.add(baseTime.getMillis());
                    }
                }
            } else {
                throw new IllegalArgumentException(
                        "Either the middle or last section of a cycle with start or end date must define a duration.");
            }

        }

        return result;
    }

    /**
     * Convenient method to calculate the time cycle based on
     * the now time.
     * 
     * @param isoDate as {@link String} e.g. R7/2013-04-09T16:34:08Z/P1D
     * @return a {@link List} of time stamps in millis
     */
    public static List<Long> calculateTimeToFireForCycle(String isoDate) {
        return calculateTimeToFireForCycle(System.currentTimeMillis(), isoDate);
    }

    /**
     * Calculate the next time to fire time stamp (in millis) 
     * based on the given 'now' time, plus the ISO 8601 duration.
     * 
     * @param now actual time stamp in millis
     * @param isoDate as {@link String} e.g. PT2H1M10S
     * @return time to fire time stamp in millis
     */
    public static long calculateTimeToFireForDuration(long now, String isoDate) {
        DateTime dateTime = new DateTime(now);
        Period period = periodformatter.parsePeriod(isoDate);
        dateTime = dateTime.plus(period);
        return dateTime.getMillis();
    }

    /**
     * Convenient method to calculate the duration based on the
     * now time.
     * 
     * @param isoDate as {@link String} e.g. PT2H1M10S
     * @return time to fire time stamp in millis
     */
    public static long calculateTimeToFireForDuration(String isoDate) {
        return calculateTimeToFireForDuration(System.currentTimeMillis(), isoDate);
    }

    /**
     * This method checks, if a cycle has unbounded
     * repetitions or not. If it is unbounded 'true' 
     * will be returned, otherwise 'false'.
     * 
     * @param isoDate as {@link String} e.g. R7/2013-04-09T16:34:08Z/P1D
     * @return true if it's unbounded
     */
    public static boolean isUnboundedCycle(String isoDate) {
        boolean result = Boolean.FALSE;

        if (isoDate.startsWith("R")) {
            String num = isoDate.substring(1, 2);

            if (num.equals("/")) {
                result = Boolean.TRUE;
            }
        } else {
            throw new IllegalArgumentException("A ISO 8601 repetition date must start with a 'R'.");
        }

        return result;
    }

}