Skip to content

Commit 4372063

Browse files
committed
Replace usage of Joda Time with Java Time in our production code
1 parent 8ea06b4 commit 4372063

File tree

17 files changed

+161
-109
lines changed

17 files changed

+161
-109
lines changed

modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/HumanTaskActivityBehavior.java

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
import org.flowable.common.engine.api.delegate.event.FlowableEventDispatcher;
4949
import org.flowable.common.engine.api.scope.ScopeTypes;
5050
import org.flowable.common.engine.impl.assignment.CandidateUtil;
51+
import org.flowable.common.engine.impl.calendar.DueDateBusinessCalendar;
5152
import org.flowable.common.engine.impl.el.ExpressionManager;
5253
import org.flowable.common.engine.impl.identity.Authentication;
5354
import org.flowable.common.engine.impl.interceptor.CommandContext;
@@ -57,8 +58,6 @@
5758
import org.flowable.task.service.TaskService;
5859
import org.flowable.task.service.delegate.TaskListener;
5960
import org.flowable.task.service.impl.persistence.entity.TaskEntity;
60-
import org.joda.time.DateTime;
61-
import org.joda.time.Period;
6261

6362
import com.fasterxml.jackson.databind.node.ObjectNode;
6463

@@ -293,12 +292,10 @@ protected void handleDueDate(CommandContext commandContext, PlanItemInstanceEnti
293292

294293
} else if (dueDate instanceof String) {
295294
String dueDateString = (String) dueDate;
296-
if (dueDateString.startsWith("P")) {
297-
taskEntity.setDueDate(new DateTime(CommandContextUtil.getCmmnEngineConfiguration(commandContext).getClock().getCurrentTime())
298-
.plus(Period.parse(dueDateString)).toDate());
299-
} else {
300-
taskEntity.setDueDate(DateTime.parse(dueDateString).toDate());
301-
}
295+
Date resolvedDuedate = CommandContextUtil.getCmmnEngineConfiguration(commandContext).getBusinessCalendarManager()
296+
.getBusinessCalendar(DueDateBusinessCalendar.NAME)
297+
.resolveDuedate(dueDateString);
298+
taskEntity.setDueDate(resolvedDuedate);
302299

303300
} else if (dueDate instanceof Instant) {
304301
taskEntity.setDueDate(Date.from((Instant) dueDate));

modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/behavior/impl/TimerEventListenerActivityBehaviour.java

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import java.time.LocalDate;
1717
import java.time.LocalDateTime;
1818
import java.time.ZoneId;
19+
import java.time.ZonedDateTime;
1920
import java.util.Date;
2021
import java.util.List;
2122

@@ -42,14 +43,12 @@
4243
import org.flowable.common.engine.impl.interceptor.CommandContext;
4344
import org.flowable.common.engine.impl.joda.JodaDeprecationLogger;
4445
import org.flowable.common.engine.impl.runtime.Clock;
46+
import org.flowable.common.engine.impl.util.DateUtil;
4547
import org.flowable.job.service.JobServiceConfiguration;
4648
import org.flowable.job.service.impl.persistence.entity.JobEntity;
4749
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
4850
import org.flowable.job.service.impl.persistence.entity.TimerJobEntityManager;
4951
import org.joda.time.DateTime;
50-
import org.joda.time.DateTimeZone;
51-
import org.joda.time.format.DateTimeFormatter;
52-
import org.joda.time.format.ISODateTimeFormat;
5352

5453
/**
5554
* {@link CmmnActivityBehavior} implementation for the CMMN Timer Event Listener.
@@ -125,7 +124,7 @@ protected void handleCreateTransition(CommandContext commandContext, PlanItemIns
125124

126125
// Try to parse as ISO8601 first
127126
try {
128-
timerDueDate = DateTime.parse(timerString).toDate();
127+
timerDueDate = DateUtil.parseDate(timerString);
129128
} catch (Exception e) { }
130129

131130
// Try to parse as cron expression
@@ -240,8 +239,7 @@ protected boolean isDurationString(String timerString) {
240239

241240
public String prepareRepeat(String dueDate, Clock clock) {
242241
if (dueDate.startsWith("R") && dueDate.split("/").length == 2) {
243-
DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
244-
return dueDate.replace("/", "/" + fmt.print(new DateTime(clock.getCurrentTime(),DateTimeZone.forTimeZone(clock.getCurrentTimeZone()))) + "/");
242+
return dueDate.replace("/", "/" + clock.getCurrentTime().toInstant().toString() + "/");
245243
}
246244
return dueDate;
247245
}

modules/flowable-cmmn-engine/src/main/java/org/flowable/cmmn/engine/impl/cmd/RescheduleTimerJobCmd.java

Lines changed: 3 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -26,15 +26,12 @@
2626
import org.flowable.common.engine.impl.interceptor.Command;
2727
import org.flowable.common.engine.impl.interceptor.CommandContext;
2828
import org.flowable.common.engine.impl.runtime.Clock;
29+
import org.flowable.common.engine.impl.util.DateUtil;
2930
import org.flowable.job.api.Job;
3031
import org.flowable.job.service.JobService;
3132
import org.flowable.job.service.JobServiceConfiguration;
3233
import org.flowable.job.service.TimerJobService;
3334
import org.flowable.job.service.impl.persistence.entity.TimerJobEntity;
34-
import org.joda.time.DateTime;
35-
import org.joda.time.DateTimeZone;
36-
import org.joda.time.format.DateTimeFormatter;
37-
import org.joda.time.format.ISODateTimeFormat;
3835

3936
public class RescheduleTimerJobCmd implements Command<Job> {
4037

@@ -108,7 +105,7 @@ public Job execute(CommandContext commandContext) {
108105

109106
// Try to parse as ISO8601 first
110107
try {
111-
timerDueDate = DateTime.parse(newDateValue).toDate();
108+
timerDueDate = DateUtil.parseDate(newDateValue);
112109
} catch (Exception e) { }
113110

114111
// Try to parse as cron expression
@@ -162,8 +159,7 @@ protected boolean isDurationString(String timerString) {
162159

163160
public String prepareRepeat(String dueDate, Clock clock) {
164161
if (dueDate.startsWith("R") && dueDate.split("/").length == 2) {
165-
DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
166-
return dueDate.replace("/", "/" + fmt.print(new DateTime(clock.getCurrentTime(),DateTimeZone.forTimeZone(clock.getCurrentTimeZone()))) + "/");
162+
return dueDate.replace("/", "/" + clock.getCurrentTime().toInstant().toString() + "/");
167163
}
168164
return dueDate;
169165
}

modules/flowable-dmn-engine/src/main/java/org/flowable/dmn/engine/impl/el/ExecutionVariableFactory.java

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,13 +14,17 @@
1414

1515
import java.math.BigDecimal;
1616
import java.math.BigInteger;
17+
import java.time.Instant;
18+
import java.time.LocalDate;
19+
import java.time.LocalDateTime;
20+
import java.time.ZoneOffset;
1721
import java.util.ArrayList;
1822
import java.util.Date;
1923
import java.util.List;
2024

2125
import org.apache.commons.lang3.StringUtils;
2226
import org.flowable.common.engine.api.FlowableException;
23-
import org.joda.time.DateTime;
27+
import org.flowable.common.engine.impl.util.DateUtil;
2428
import org.slf4j.Logger;
2529
import org.slf4j.LoggerFactory;
2630

@@ -66,8 +70,14 @@ public static Object getExecutionVariable(String type, Object expressionResult)
6670
} else if (StringUtils.equals("date", type)) {
6771
if (expressionResult instanceof Date) {
6872
executionVariable = expressionResult;
73+
} else if (expressionResult instanceof Instant instant) {
74+
executionVariable = Date.from(instant);
75+
} else if (expressionResult instanceof LocalDate localDate) {
76+
executionVariable = Date.from(localDate.atStartOfDay().atZone(ZoneOffset.UTC).toInstant());
77+
} else if (expressionResult instanceof LocalDateTime localDateTime) {
78+
executionVariable = Date.from(localDateTime.atZone(ZoneOffset.UTC).toInstant());
6979
} else {
70-
executionVariable = new DateTime(expressionResult.toString()).toDate();
80+
executionVariable = DateUtil.parseDate(expressionResult.toString());
7181
}
7282
} else {
7383
LOGGER.error("could not create result variable: unrecognized mapping type");

modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/calendar/BusinessCalendarImpl.java

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,7 @@
1515
import java.util.Date;
1616

1717
import org.flowable.common.engine.impl.runtime.ClockReader;
18-
import org.joda.time.DateTimeZone;
19-
import org.joda.time.format.ISODateTimeFormat;
18+
import org.flowable.common.engine.impl.util.DateUtil;
2019

2120
/**
2221
* This class implements business calendar based on internal clock
@@ -44,7 +43,7 @@ public Boolean validateDuedate(String duedateDescription, int maxIterations, Dat
4443

4544
@Override
4645
public Date resolveEndDate(String endDateString) {
47-
return ISODateTimeFormat.dateTimeParser().withZone(DateTimeZone.forTimeZone(clockReader.getCurrentTimeZone())).parseDateTime(endDateString).toCalendar(null).getTime();
46+
return DateUtil.parseDate(endDateString);
4847
}
4948

5049
}

modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/calendar/DueDateBusinessCalendar.java

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,14 @@
1212
*/
1313
package org.flowable.common.engine.impl.calendar;
1414

15+
import java.time.Duration;
16+
import java.time.Period;
17+
import java.time.ZonedDateTime;
1518
import java.util.Date;
1619

1720
import org.flowable.common.engine.api.FlowableException;
1821
import org.flowable.common.engine.impl.runtime.ClockReader;
19-
import org.joda.time.DateTime;
20-
import org.joda.time.Period;
22+
import org.flowable.common.engine.impl.util.DateUtil;
2123

2224
public class DueDateBusinessCalendar extends BusinessCalendarImpl {
2325

@@ -32,10 +34,29 @@ public Date resolveDuedate(String duedate, int maxIterations) {
3234
try {
3335
// check if due period was specified
3436
if (duedate.startsWith("P")) {
35-
return new DateTime(clockReader.getCurrentTime()).plus(Period.parse(duedate)).toDate();
37+
ZonedDateTime calculateTime = clockReader.getCurrentTime()
38+
.toInstant()
39+
.atZone(clockReader.getCurrentTimeZone().toZoneId());
40+
Period period;
41+
Duration duration;
42+
if (duedate.startsWith("PT")) {
43+
period = Period.ZERO;
44+
duration = Duration.parse(duedate);
45+
} else {
46+
int timeIndex = duedate.indexOf('T');
47+
if (timeIndex > 0) {
48+
period = Period.parse(duedate.substring(0, timeIndex));
49+
duration = Duration.parse("P" + duedate.substring(timeIndex));
50+
} else {
51+
period = Period.parse(duedate);
52+
duration = Duration.ZERO;
53+
}
54+
}
55+
56+
return Date.from(calculateTime.plus(period).plus(duration).toInstant());
3657
}
3758

38-
return DateTime.parse(duedate).toDate();
59+
return DateUtil.parseDate(duedate);
3960

4061
} catch (Exception e) {
4162
throw new FlowableException("couldn't resolve duedate: " + e.getMessage(), e);

modules/flowable-engine-common/src/main/java/org/flowable/common/engine/impl/calendar/DurationHelper.java

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616

1717
import java.text.DateFormat;
1818
import java.text.SimpleDateFormat;
19+
import java.time.format.DateTimeParseException;
1920
import java.util.Arrays;
2021
import java.util.Calendar;
2122
import java.util.Date;
@@ -27,9 +28,8 @@
2728

2829
import org.flowable.common.engine.api.FlowableIllegalArgumentException;
2930
import org.flowable.common.engine.impl.runtime.ClockReader;
31+
import org.flowable.common.engine.impl.util.DateUtil;
3032
import org.flowable.common.engine.impl.util.TimeZoneUtil;
31-
import org.joda.time.DateTimeZone;
32-
import org.joda.time.format.ISODateTimeFormat;
3333

3434
/**
3535
* Helper class for parsing ISO8601 duration format (also recurring) and computing next timer date.
@@ -183,10 +183,9 @@ protected Calendar add(Calendar date, Duration duration) {
183183
protected Calendar parseDate(String date) throws Exception {
184184
Calendar dateCalendar = null;
185185
try {
186-
dateCalendar = ISODateTimeFormat.dateTimeParser().withZone(DateTimeZone.forTimeZone(
187-
clockReader.getCurrentTimeZone())).parseDateTime(date).toCalendar(null);
186+
dateCalendar = DateUtil.parseCalendar(date, clockReader.getCurrentTimeZone().toZoneId());
188187

189-
} catch (IllegalArgumentException e) {
188+
} catch (IllegalArgumentException | DateTimeParseException e) {
190189
// try to parse a java.util.date to string back to a java.util.date
191190
dateCalendar = new GregorianCalendar();
192191
dateCalendar.setTime(DATE_FORMAT.parse(date));
Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
/* Licensed under the Apache License, Version 2.0 (the "License");
2+
* you may not use this file except in compliance with the License.
3+
* You may obtain a copy of the License at
4+
*
5+
* http://www.apache.org/licenses/LICENSE-2.0
6+
*
7+
* Unless required by applicable law or agreed to in writing, software
8+
* distributed under the License is distributed on an "AS IS" BASIS,
9+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
10+
* See the License for the specific language governing permissions and
11+
* limitations under the License.
12+
*/
13+
package org.flowable.common.engine.impl.util;
14+
15+
import java.time.Instant;
16+
import java.time.ZoneId;
17+
import java.time.ZonedDateTime;
18+
import java.time.format.DateTimeFormatter;
19+
import java.time.format.DateTimeFormatterBuilder;
20+
import java.time.format.SignStyle;
21+
import java.time.temporal.ChronoField;
22+
import java.util.Calendar;
23+
import java.util.Date;
24+
import java.util.GregorianCalendar;
25+
26+
/**
27+
* @author Filip Hrisafov
28+
*/
29+
public class DateUtil {
30+
31+
/**
32+
* This is the closest date formatter we can get to what used to be supported with Joda DateTime.
33+
* This makes everything (except the year) optional.
34+
*/
35+
private static final DateTimeFormatter defaultDateFormatter =
36+
// @formatter:off
37+
new DateTimeFormatterBuilder()
38+
.appendValue(ChronoField.YEAR, 1, 10, SignStyle.NORMAL)
39+
.optionalStart()
40+
.appendLiteral('-')
41+
.appendValue(ChronoField.MONTH_OF_YEAR, 2)
42+
.optionalEnd()
43+
.optionalStart()
44+
.appendLiteral('-')
45+
.appendValue(ChronoField.DAY_OF_MONTH, 2)
46+
.optionalEnd()
47+
.optionalStart()
48+
.appendLiteral('T')
49+
.appendValue(ChronoField.HOUR_OF_DAY)
50+
.optionalEnd()
51+
.optionalStart()
52+
.appendLiteral(':')
53+
.appendValue(ChronoField.MINUTE_OF_HOUR)
54+
.optionalEnd()
55+
.optionalStart()
56+
.appendLiteral(':')
57+
.appendValue(ChronoField.SECOND_OF_MINUTE)
58+
.appendFraction(ChronoField.NANO_OF_SECOND, 0, 9, true)
59+
.optionalEnd()
60+
.parseLenient()
61+
.optionalStart()
62+
.appendOffsetId()
63+
.optionalEnd()
64+
.parseStrict()
65+
.parseDefaulting(ChronoField.MONTH_OF_YEAR, 1)
66+
.parseDefaulting(ChronoField.DAY_OF_MONTH, 1)
67+
.parseDefaulting(ChronoField.HOUR_OF_DAY, 0)
68+
.parseDefaulting(ChronoField.MINUTE_OF_HOUR, 0)
69+
.parseDefaulting(ChronoField.SECOND_OF_MINUTE, 0)
70+
.parseDefaulting(ChronoField.NANO_OF_SECOND, 0)
71+
.toFormatter()
72+
.withZone(ZoneId.systemDefault());
73+
// @formatter:on
74+
75+
public static Date parseDate(String dateString) {
76+
return Date.from(defaultDateFormatter.parse(dateString, Instant::from));
77+
}
78+
79+
public static Calendar parseCalendar(String dateString, ZoneId zoneId) {
80+
return GregorianCalendar.from(defaultDateFormatter.withZone(zoneId).parse(dateString, ZonedDateTime::from));
81+
}
82+
}

modules/flowable-engine/src/main/java/org/flowable/engine/impl/el/DateUtil.java

Lines changed: 1 addition & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -14,21 +14,13 @@
1414

1515
import java.util.Date;
1616

17-
import org.flowable.engine.impl.util.CommandContextUtil;
18-
import org.joda.time.DateTime;
19-
import org.joda.time.DateTimeZone;
20-
import org.joda.time.format.DateTimeFormatter;
21-
import org.joda.time.format.ISODateTimeFormat;
22-
2317
public class DateUtil {
2418

2519
public static String format(Object value) {
2620
String formattedString = null;
2721
if (value instanceof Date) {
2822
Date date = (Date) value;
29-
DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
30-
DateTimeZone dateTimeZone = DateTimeZone.forTimeZone(CommandContextUtil.getProcessEngineConfiguration().getClock().getCurrentTimeZone());
31-
formattedString = fmt.print(new DateTime(date, dateTimeZone));
23+
formattedString = date.toInstant().toString();
3224
} else {
3325
formattedString = value.toString();
3426
}

modules/flowable-engine/src/main/java/org/flowable/engine/impl/util/TimerUtil.java

Lines changed: 1 addition & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -50,9 +50,6 @@
5050
import org.flowable.variable.api.delegate.VariableScope;
5151
import org.flowable.variable.service.impl.el.NoExecutionVariableScope;
5252
import org.joda.time.DateTime;
53-
import org.joda.time.DateTimeZone;
54-
import org.joda.time.format.DateTimeFormatter;
55-
import org.joda.time.format.ISODateTimeFormat;
5653

5754
/**
5855
* @author Joram Barrez
@@ -268,11 +265,8 @@ public static TimerJobEntity rescheduleTimerJob(String timerJobId, TimerEventDef
268265

269266
public static String prepareRepeat(String dueDate) {
270267
if (dueDate.startsWith("R") && dueDate.split("/").length == 2) {
271-
DateTimeFormatter fmt = ISODateTimeFormat.dateTime();
272268
Clock clock = CommandContextUtil.getProcessEngineConfiguration().getClock();
273-
Date now = clock.getCurrentTime();
274-
return dueDate.replace("/", "/" + fmt.print(new DateTime(now,
275-
DateTimeZone.forTimeZone(clock.getCurrentTimeZone()))) + "/");
269+
return dueDate.replace("/", "/" + clock.getCurrentTime().toInstant().toString() + "/");
276270
}
277271
return dueDate;
278272
}

0 commit comments

Comments
 (0)