Flowable的DurationHelper时间计算工具类

DurationHelper是Flowable框架中的一个工具类,用于解析和处理符合XMLSchema1.0定义的PnYnMnDTnHnMnS格式的时间间隔。它支持计算基于给定起始和结束日期的下一个定时器日期,还可以处理重复间隔。示例展示了如何使用DurationHelper来获取一年、一个月、一天等不同时间单位之后的日期。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

Flowable的DurationHelper时间计算工具类

PnYnMnDTnHnMnS时间规则

Obtain a new instance of a Duration specifying the Duration as its string representation, “PnYnMnDTnHnMnS”, as defined in XML Schema 1.0 section 3.2.6.1.

规则示例说明
PnYP1Y1年以后
PnMP1M1个月以后
PnDP1D1天以后
PnDP1D1天以后
PTnHPT1H1小时以后
PTnMPT1M1分钟以后
PTnSPT1S1一秒钟以后

DurationHelper工具类

package org.flowable.common.engine.impl.calendar;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.GregorianCalendar;
import java.util.List;

import javax.xml.datatype.DatatypeFactory;
import javax.xml.datatype.Duration;

import org.flowable.common.engine.api.FlowableIllegalArgumentException;
import org.flowable.common.engine.impl.runtime.ClockReader;
import org.flowable.common.engine.impl.util.TimeZoneUtil;
import org.joda.time.DateTimeZone;
import org.joda.time.format.ISODateTimeFormat;

/**
 * Helper class for parsing ISO8601 duration format (also recurring) and computing next timer date.
 */
public class DurationHelper {

    protected static DateFormat DATE_FORMAT = new SimpleDateFormat("EEE MMM dd kk:mm:ss z yyyy");

    private Calendar start;
    private Calendar end;
    private Duration period;
    private boolean isRepeat;
    private int times;
    private int maxIterations = -1;
    private boolean repeatWithNoBounds;

    private DatatypeFactory datatypeFactory;

    public Calendar getStart() {
        return start;
    }

    public Calendar getEnd() {
        return end;
    }

    public Duration getPeriod() {
        return period;
    }

    public boolean isRepeat() {
        return isRepeat;
    }

    public int getTimes() {
        return times;
    }

    protected ClockReader clockReader;

    public DurationHelper(String expressionS, int maxIterations, ClockReader clockReader) throws Exception {
        this.clockReader = clockReader;
        this.maxIterations = maxIterations;
        List<String> expression = Arrays.asList(expressionS.split("/"));
        datatypeFactory = DatatypeFactory.newInstance();

        if (expression.size() > 3 || expression.isEmpty()) {
            throw new FlowableIllegalArgumentException("Cannot parse duration");
        }
        if (expression.get(0).startsWith("R")) {
            isRepeat = true;
            times = expression.get(0).length() == 1 ? Integer.MAX_VALUE - 1 : Integer.parseInt(expression.get(0).substring(1));

            if (expression.get(0).equals("R")) { // R without params
                repeatWithNoBounds = true;
            }

            expression = expression.subList(1, expression.size());
        }

        if (isDuration(expression.get(0))) {
            period = parsePeriod(expression.get(0));
            end = expression.size() == 1 ? null : parseDate(expression.get(1));
        } else {
            start = parseDate(expression.get(0));
            if (isDuration(expression.get(1))) {
                period = parsePeriod(expression.get(1));
            } else {
                end = parseDate(expression.get(1));
                period = datatypeFactory.newDuration(end.getTimeInMillis() - start.getTimeInMillis());
            }
        }
        if (start == null) {
            start = clockReader.getCurrentCalendar();
        }

    }

    public DurationHelper(String expressionS, ClockReader clockReader) throws Exception {
        this(expressionS, -1, clockReader);
    }

    public Calendar getCalendarAfter() {
        return getCalendarAfter(clockReader.getCurrentCalendar());
    }

    public Calendar getCalendarAfter(Calendar time) {
        if (isRepeat) {
            return getDateAfterRepeat(time);
        }
        // TODO: is this correct?
        if (end != null) {
            return end;
        }
        return add(start, period);
    }

    public Boolean isValidDate(Date newTimer) {
        return end == null || end.getTime().after(newTimer) || end.getTime().equals(newTimer);
    }

    public Date getDateAfter() {
        Calendar date = getCalendarAfter();

        return date == null ? null : date.getTime();
    }

    private Calendar getDateAfterRepeat(Calendar date) {
        Calendar current = TimeZoneUtil.convertToTimeZone(start, date.getTimeZone());

        if (repeatWithNoBounds) {

            while (current.before(date) || current.equals(date)) { // As long as current date is not past the engine date, we keep looping
                Calendar newTime = add(current, period);
                if (newTime.equals(current) || newTime.before(current)) {
                    break;
                }
                current = newTime;
            }

        } else {

            int maxLoops = times;
            if (maxIterations > 0) {
                maxLoops = maxIterations - times;
            }
            for (int i = 0; i < maxLoops + 1 && !current.after(date); i++) {
                current = add(current, period);
            }

        }
        return current.before(date) ? date : TimeZoneUtil.convertToTimeZone(current, clockReader.getCurrentTimeZone());

    }

    protected Calendar add(Calendar date, Duration duration) {
        Calendar calendar = (Calendar) date.clone();

        // duration.addTo does not account for daylight saving time (xerces),
        // reversing order of addition fixes the problem
        calendar.add(Calendar.SECOND, duration.getSeconds() * duration.getSign());
        calendar.add(Calendar.MINUTE, duration.getMinutes() * duration.getSign());
        calendar.add(Calendar.HOUR, duration.getHours() * duration.getSign());
        calendar.add(Calendar.DAY_OF_MONTH, duration.getDays() * duration.getSign());
        calendar.add(Calendar.MONTH, duration.getMonths() * duration.getSign());
        calendar.add(Calendar.YEAR, duration.getYears() * duration.getSign());

        return calendar;
    }

    protected Calendar parseDate(String date) throws Exception {
        Calendar dateCalendar = null;
        try {
            dateCalendar = ISODateTimeFormat.dateTimeParser().withZone(DateTimeZone.forTimeZone(
                    clockReader.getCurrentTimeZone())).parseDateTime(date).toCalendar(null);

        } catch (IllegalArgumentException e) {
            // try to parse a java.util.date to string back to a java.util.date
            dateCalendar = new GregorianCalendar();
            dateCalendar.setTime(DATE_FORMAT.parse(date));
        }

        return dateCalendar;
    }

    protected Duration parsePeriod(String period) throws Exception {
        return datatypeFactory.newDuration(period);
    }

    protected boolean isDuration(String time) {
        return time.startsWith("P");
    }

}

DurationHelper工具类使用示例

public class ExecuteTest {


    @Test
    public void testD() throws Exception {
        System.out.println("当前时间:"+format(new Date()));
       //1年以后
        printDate("P1Y");
        //1个月以后
        printDate("P1M");
        //1天以后
        printDate("P1D");
        //1小时以后
        printDate("PT1H");
        //1分钟以后
        printDate("PT1M");
        //1秒钟以后
        printDate("PT1S");

    }

    public void printDate(String lexicalRepresentation) throws Exception {
        ClockReader clockReader = new DefaultClockImpl();
        DurationHelper dh = new DurationHelper(lexicalRepresentation, clockReader);
        System.out.println("规则:"+lexicalRepresentation+"    时间:"+format(dh.getDateAfter()));
    }

    private String format(Date date) {
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        return dateFormat.format(date);
    }
}

示例结果:

当前时间:2023-02-15 18:14:53
规则:P1Y     时间:2024-02-15 18:14:53
规则:P1M     时间:2023-03-15 18:14:53
规则:P1D     时间:2023-02-16 18:14:53
规则:PT1H    时间:2023-02-15 19:14:53
规则:PT1M    时间:2023-02-15 18:15:53
规则:PT1S    时间:2023-02-15 18:14:54

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

顺随心动

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值