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.
规则 |
示例 |
说明 |
PnY |
P1Y |
1年以后 |
PnM |
P1M |
1个月以后 |
PnD |
P1D |
1天以后 |
PnD |
P1D |
1天以后 |
PTnH |
PT1H |
1小时以后 |
PTnM |
PT1M |
1分钟以后 |
PTnS |
PT1S |
1一秒钟以后 |
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