Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZonedDateTime;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
Expand All @@ -27,6 +28,7 @@
import org.flowable.engine.runtime.Execution;
import org.flowable.engine.runtime.ProcessInstance;
import org.flowable.task.api.Task;
import org.junit.jupiter.api.AfterEach;
import org.junit.jupiter.api.Test;

/**
Expand All @@ -53,6 +55,8 @@ public void testProcessTaskTypeConversion() {
variables.put("periodVar", "P10D");
variables.put("durationVar", "PT10H");

ZonedDateTime now = setClocks();

CaseInstance caseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("processTaskTypeConversion")
.variables(variables)
Expand All @@ -79,16 +83,12 @@ public void testProcessTaskTypeConversion() {
.isInstanceOf(Long.class).isEqualTo(789012345678L);

// Verify period/duration conversions
Date now = new Date();
Object periodToDate = processEngine.getRuntimeService().getVariable(processInstance.getId(), "periodToDate");
assertThat(periodToDate).isInstanceOf(Date.class);
assertThat(Duration.between(now.toInstant(), ((Date) periodToDate).toInstant()))
.isBetween(Duration.ofDays(9).plusHours(23), Duration.ofDays(10).plusMinutes(1));
assertThat(((Date) periodToDate).toInstant()).isEqualTo(now.plusDays(10).toInstant());

Object durationToLocalDate = processEngine.getRuntimeService().getVariable(processInstance.getId(), "durationToLocalDate");
assertThat(durationToLocalDate).isInstanceOf(LocalDate.class);
LocalDate today = LocalDate.now();
assertThat((LocalDate) durationToLocalDate).isBetween(today, today.plusDays(1));
assertThat(durationToLocalDate).isInstanceOf(LocalDate.class).isEqualTo(now.plusHours(10).toLocalDate());

// Set variables on the process instance for out parameter testing
processEngine.getRuntimeService().setVariable(processInstance.getId(), "processIntVar", "456");
Expand Down Expand Up @@ -120,6 +120,8 @@ public void testCaseTaskTypeConversion() {
variables.put("periodVar", "P10D");
variables.put("durationVar", "PT10H");

ZonedDateTime now = setClocks();

CaseInstance parentCaseInstance = cmmnRuntimeService.createCaseInstanceBuilder()
.caseDefinitionKey("parentCaseTypeConversion")
.variables(variables)
Expand All @@ -140,16 +142,12 @@ public void testCaseTaskTypeConversion() {
.isInstanceOf(Boolean.class).isEqualTo(true);

// Verify period/duration conversions
Date now = new Date();
Object periodToDate = cmmnRuntimeService.getVariable(childCaseInstance.getId(), "periodToDate");
assertThat(periodToDate).isInstanceOf(Date.class);
assertThat(Duration.between(now.toInstant(), ((Date) periodToDate).toInstant()))
.isBetween(Duration.ofDays(9).plusHours(23), Duration.ofDays(10).plusMinutes(1));
assertThat(((Date) periodToDate).toInstant()).isEqualTo(now.plusDays(10).toInstant());

Object durationToLocalDate = cmmnRuntimeService.getVariable(childCaseInstance.getId(), "durationToLocalDate");
assertThat(durationToLocalDate).isInstanceOf(LocalDate.class);
LocalDate today = LocalDate.now();
assertThat((LocalDate) durationToLocalDate).isBetween(today, today.plusDays(1));
assertThat(durationToLocalDate).isInstanceOf(LocalDate.class).isEqualTo(now.plusHours(10).toLocalDate());

// Set variables on the child case for out parameter testing
cmmnRuntimeService.setVariable(childCaseInstance.getId(), "childIntVar", "456");
Expand Down Expand Up @@ -224,4 +222,19 @@ public void testCaseServiceTaskTypeConversion() {
processEngineRepositoryService.deleteDeployment(deployment.getId(), true);
}
}

protected ZonedDateTime setClocks() {
// The period and duration conversions are relative to the current time of the engine clock.
// The next 10 days cross the end of the daylight saving time in Europe and in the US
Instant now = Instant.parse("2025-10-24T10:15:30Z");
processEngineConfiguration.getClock().setCurrentTime(Date.from(now));
cmmnEngineConfiguration.getClock().setCurrentTime(Date.from(now));
return now.atZone(cmmnEngineConfiguration.getClock().getCurrentTimeZone().toZoneId());
}

@AfterEach
public void resetClocks() {
processEngineConfiguration.getClock().reset();
cmmnEngineConfiguration.getClock().reset();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -645,7 +645,7 @@ public void initIdGenerator() {

public void initVariableValueConversionHandler() {
if (variableValueConversionHandler == null) {
variableValueConversionHandler = new DefaultVariableValueConversionHandler();
variableValueConversionHandler = new DefaultVariableValueConversionHandler(clock);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
import org.flowable.common.engine.api.FlowableException;
import org.flowable.common.engine.impl.json.FlowableJsonNode;
import org.flowable.common.engine.impl.json.VariableJsonMapper;
import org.flowable.common.engine.impl.runtime.ClockReader;
import org.flowable.common.engine.impl.util.DateUtil;
import org.flowable.common.engine.impl.util.JsonUtil;

Expand All @@ -34,6 +35,12 @@
*/
public class DefaultVariableValueConversionHandler implements VariableValueConversionHandler {

protected final ClockReader clockReader;

public DefaultVariableValueConversionHandler(ClockReader clockReader) {
this.clockReader = clockReader;
}

@Override
public Object convertValue(Object value, String type, VariableJsonMapper variableJsonMapper) {
return switch (type.toLowerCase()) {
Expand Down Expand Up @@ -194,11 +201,11 @@ protected Object convertToArray(Object value, VariableJsonMapper variableJsonMap
}

/**
* Parses an ISO 8601 duration/period string (e.g. "P10D", "PT10H", "P1Y2M3DT4H") and adds it to the current time.
* Parses an ISO 8601 duration/period string (e.g. "P10D", "PT10H", "P1Y2M3DT4H") and adds it to the current time of the clock.
* Uses the same parsing logic as {@link org.flowable.common.engine.impl.calendar.DueDateBusinessCalendar}.
*/
protected ZonedDateTime addDurationToNow(String durationString) {
ZonedDateTime now = ZonedDateTime.now();
ZonedDateTime now = ZonedDateTime.ofInstant(clockReader.getCurrentTime().toInstant(), clockReader.getCurrentTimeZone().toZoneId());
Period period;
Duration duration;
if (durationString.startsWith("PT")) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,10 +14,11 @@

import static org.assertj.core.api.Assertions.assertThat;

import java.time.Duration;
import java.time.Instant;
import java.time.LocalDate;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.time.temporal.ChronoUnit;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
Expand Down Expand Up @@ -87,6 +88,12 @@ public void testInParameterTypeConversion() {
// Source variables for array conversion
variables.put("arrayStringVar", "[1,2,3]");

// The period and duration conversions are relative to the current time of the engine clock.
// The next 10 days cross the end of the daylight saving time in Europe and in the US
Instant now = Instant.parse("2025-10-24T10:15:30Z");
processEngineConfiguration.getClock().setCurrentTime(Date.from(now));
ZonedDateTime zonedNow = now.atZone(processEngineConfiguration.getClock().getCurrentTimeZone().toZoneId());

ProcessInstance processInstance = runtimeService.startProcessInstanceByKey("parentProcess", variables);

// Get the child process instance
Expand Down Expand Up @@ -145,16 +152,14 @@ public void testInParameterTypeConversion() {
assertThat(((Date) instantToDate).toInstant()).isEqualTo(testInstant);

// Verify period/duration to date conversions
Date now = new Date();
// A period is added in calendar days in the system time zone, so it is not always a multiple of 24 hours (e.g. when crossing a DST change)
Object periodToDate = runtimeService.getVariable(childProcessInstance.getId(), "periodToDate");
assertThat(periodToDate).isInstanceOf(Date.class);
assertThat(Duration.between(now.toInstant(), ((Date) periodToDate).toInstant()))
.isBetween(Duration.ofDays(9).plusHours(23), Duration.ofDays(10).plusMinutes(1));
assertThat(((Date) periodToDate).toInstant()).isEqualTo(zonedNow.plusDays(10).toInstant());

Object durationToDate = runtimeService.getVariable(childProcessInstance.getId(), "durationToDate");
assertThat(durationToDate).isInstanceOf(Date.class);
assertThat(Duration.between(now.toInstant(), ((Date) durationToDate).toInstant()))
.isBetween(Duration.ofHours(9).plusMinutes(59), Duration.ofHours(10).plusMinutes(1));
assertThat(((Date) durationToDate).toInstant()).isEqualTo(now.plus(10, ChronoUnit.HOURS));

// Verify localdate conversions
Object stringToLocalDate = runtimeService.getVariable(childProcessInstance.getId(), "stringToLocalDate");
Expand All @@ -167,14 +172,11 @@ public void testInParameterTypeConversion() {
assertThat(instantToLocalDate).isInstanceOf(LocalDate.class).isEqualTo(testLocalDate);

// Verify period/duration to localdate conversions
LocalDate today = LocalDate.now();
Object periodToLocalDate = runtimeService.getVariable(childProcessInstance.getId(), "periodToLocalDate");
assertThat(periodToLocalDate).isInstanceOf(LocalDate.class).isEqualTo(today.plusDays(10));
assertThat(periodToLocalDate).isInstanceOf(LocalDate.class).isEqualTo(zonedNow.plusDays(10).toLocalDate());

Object durationToLocalDate = runtimeService.getVariable(childProcessInstance.getId(), "durationToLocalDate");
assertThat(durationToLocalDate).isInstanceOf(LocalDate.class);
// PT10H from now could be today or tomorrow depending on time of day
assertThat((LocalDate) durationToLocalDate).isBetween(today, today.plusDays(1));
assertThat(durationToLocalDate).isInstanceOf(LocalDate.class).isEqualTo(zonedNow.plusHours(10).toLocalDate());

// Verify JSON conversion
Object stringToJson = runtimeService.getVariable(childProcessInstance.getId(), "stringToJson");
Expand Down
Loading