This is an automated email from the ASF dual-hosted git repository.

zhongjiajie pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/dolphinscheduler.git


The following commit(s) were added to refs/heads/dev by this push:
     new 5d5125d507 [Bug-9829][Api] Fix schedule timezone (#9830)
5d5125d507 is described below

commit 5d5125d50760ac7846d78b12c270143bbbe5a037
Author: caishunfeng <[email protected]>
AuthorDate: Fri Apr 29 10:17:34 2022 +0800

    [Bug-9829][Api] Fix schedule timezone (#9830)
---
 .../api/service/impl/ExecutorServiceImpl.java      |  33 +-
 .../api/service/impl/SchedulerServiceImpl.java     |  24 +-
 .../apache/dolphinscheduler/api/vo/ScheduleVo.java | 337 +++++++++++++++++++++
 .../dolphinscheduler/common/utils/DateUtils.java   |  21 +-
 .../common/utils/DateUtilsTest.java                |   9 +-
 .../common/utils/JSONUtilsTest.java                |   3 +
 .../service/quartz/impl/QuartzExecutorImpl.java    |  56 ++--
 7 files changed, 439 insertions(+), 44 deletions(-)

diff --git 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ExecutorServiceImpl.java
 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ExecutorServiceImpl.java
index fe562ef41d..5f72be37ed 100644
--- 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ExecutorServiceImpl.java
+++ 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/ExecutorServiceImpl.java
@@ -44,8 +44,23 @@ import org.apache.dolphinscheduler.common.enums.WarningType;
 import org.apache.dolphinscheduler.common.model.Server;
 import org.apache.dolphinscheduler.common.utils.DateUtils;
 import org.apache.dolphinscheduler.common.utils.JSONUtils;
-import org.apache.dolphinscheduler.dao.entity.*;
-import org.apache.dolphinscheduler.dao.mapper.*;
+import org.apache.dolphinscheduler.dao.entity.Command;
+import org.apache.dolphinscheduler.dao.entity.DependentProcessDefinition;
+import org.apache.dolphinscheduler.dao.entity.ProcessDefinition;
+import org.apache.dolphinscheduler.dao.entity.ProcessInstance;
+import org.apache.dolphinscheduler.dao.entity.ProcessTaskRelation;
+import org.apache.dolphinscheduler.dao.entity.Project;
+import org.apache.dolphinscheduler.dao.entity.Schedule;
+import org.apache.dolphinscheduler.dao.entity.TaskDefinition;
+import org.apache.dolphinscheduler.dao.entity.TaskGroupQueue;
+import org.apache.dolphinscheduler.dao.entity.Tenant;
+import org.apache.dolphinscheduler.dao.entity.User;
+import org.apache.dolphinscheduler.dao.mapper.ProcessDefinitionMapper;
+import org.apache.dolphinscheduler.dao.mapper.ProcessInstanceMapper;
+import org.apache.dolphinscheduler.dao.mapper.ProcessTaskRelationMapper;
+import org.apache.dolphinscheduler.dao.mapper.ProjectMapper;
+import org.apache.dolphinscheduler.dao.mapper.TaskDefinitionMapper;
+import org.apache.dolphinscheduler.dao.mapper.TaskGroupQueueMapper;
 import org.apache.dolphinscheduler.plugin.task.api.TaskConstants;
 import org.apache.dolphinscheduler.plugin.task.api.enums.ExecutionStatus;
 import org.apache.dolphinscheduler.remote.command.StateEventChangeCommand;
@@ -58,7 +73,13 @@ import org.apache.commons.collections.CollectionUtils;
 import org.apache.commons.collections.MapUtils;
 import org.apache.commons.lang.StringUtils;
 
-import java.util.*;
+import java.util.ArrayList;
+import java.util.Date;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
 
 import org.slf4j.Logger;
@@ -223,7 +244,7 @@ public class ExecutorServiceImpl extends BaseServiceImpl 
implements ExecutorServ
         } else if (processDefinition.getReleaseState() != ReleaseState.ONLINE) 
{
             // check process definition online
             putMsg(result, Status.PROCESS_DEFINE_NOT_RELEASE, 
String.valueOf(processDefineCode), version);
-        } else if (!checkSubProcessDefinitionValid(processDefinition)){
+        } else if (!checkSubProcessDefinitionValid(processDefinition)) {
             // check sub process definition online
             putMsg(result, Status.SUB_PROCESS_DEFINE_NOT_RELEASE);
         } else {
@@ -241,7 +262,7 @@ public class ExecutorServiceImpl extends BaseServiceImpl 
implements ExecutorServ
     public boolean checkSubProcessDefinitionValid(ProcessDefinition 
processDefinition) {
         // query all subprocesses under the current process
         List<ProcessTaskRelation> processTaskRelations = 
processTaskRelationMapper.queryDownstreamByProcessDefinitionCode(processDefinition.getCode());
-        if (processTaskRelations.isEmpty()){
+        if (processTaskRelations.isEmpty()) {
             return true;
         }
         Set<Long> relationCodes = 
processTaskRelations.stream().map(ProcessTaskRelation::getPostTaskCode).collect(Collectors.toSet());
@@ -252,7 +273,7 @@ public class ExecutorServiceImpl extends BaseServiceImpl 
implements ExecutorServ
         taskDefinitions.stream()
                 .filter(task -> 
TaskConstants.TASK_TYPE_SUB_PROCESS.equalsIgnoreCase(task.getTaskType()))
                 .forEach(taskDefinition -> 
processDefinitionCodeSet.add(Long.valueOf(JSONUtils.getNodeString(taskDefinition.getTaskParams(),
 Constants.CMD_PARAM_SUB_PROCESS_DEFINE_CODE))));
-        if (processDefinitionCodeSet.isEmpty()){
+        if (processDefinitionCodeSet.isEmpty()) {
             return true;
         }
 
diff --git 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/SchedulerServiceImpl.java
 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/SchedulerServiceImpl.java
index 52b22577c3..0909d3e582 100644
--- 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/SchedulerServiceImpl.java
+++ 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/service/impl/SchedulerServiceImpl.java
@@ -26,6 +26,7 @@ import org.apache.dolphinscheduler.api.service.ProjectService;
 import org.apache.dolphinscheduler.api.service.SchedulerService;
 import org.apache.dolphinscheduler.api.utils.PageInfo;
 import org.apache.dolphinscheduler.api.utils.Result;
+import org.apache.dolphinscheduler.api.vo.ScheduleVo;
 import org.apache.dolphinscheduler.common.Constants;
 import org.apache.dolphinscheduler.common.enums.FailureStrategy;
 import org.apache.dolphinscheduler.common.enums.Priority;
@@ -47,7 +48,6 @@ import org.apache.dolphinscheduler.dao.mapper.ScheduleMapper;
 import org.apache.dolphinscheduler.service.process.ProcessService;
 import org.apache.dolphinscheduler.service.quartz.ProcessScheduleJob;
 import org.apache.dolphinscheduler.service.quartz.QuartzExecutor;
-import org.apache.dolphinscheduler.service.quartz.impl.QuartzExecutorImpl;
 import org.apache.dolphinscheduler.service.quartz.cron.CronUtils;
 
 import org.apache.commons.lang.StringUtils;
@@ -173,6 +173,7 @@ public class SchedulerServiceImpl extends BaseServiceImpl 
implements SchedulerSe
             putMsg(result, Status.START_TIME_BIGGER_THAN_END_TIME_ERROR);
             return result;
         }
+
         scheduleObj.setStartTime(scheduleParam.getStartTime());
         scheduleObj.setEndTime(scheduleParam.getEndTime());
         if 
(!org.quartz.CronExpression.isValidExpression(scheduleParam.getCrontab())) {
@@ -414,9 +415,14 @@ public class SchedulerServiceImpl extends BaseServiceImpl 
implements SchedulerSe
         IPage<Schedule> scheduleIPage = 
scheduleMapper.queryByProcessDefineCodePaging(page, processDefineCode,
             searchVal);
 
-        PageInfo<Schedule> pageInfo = new PageInfo<>(pageNo, pageSize);
+        List<ScheduleVo> scheduleList = new ArrayList<>();
+        for (Schedule schedule : scheduleIPage.getRecords()) {
+            scheduleList.add(new ScheduleVo(schedule));
+        }
+
+        PageInfo<ScheduleVo> pageInfo = new PageInfo<>(pageNo, pageSize);
         pageInfo.setTotal((int) scheduleIPage.getTotal());
-        pageInfo.setTotalList(scheduleIPage.getRecords());
+        pageInfo.setTotalList(scheduleList);
         result.setData(pageInfo);
         putMsg(result, Status.SUCCESS);
         return result;
@@ -441,8 +447,12 @@ public class SchedulerServiceImpl extends BaseServiceImpl 
implements SchedulerSe
         }
 
         List<Schedule> schedules = 
scheduleMapper.querySchedulerListByProjectName(project.getName());
+        List<ScheduleVo> scheduleList = new ArrayList<>();
+        for (Schedule schedule : schedules) {
+            scheduleList.add(new ScheduleVo(schedule));
+        }
 
-        result.put(Constants.DATA_LIST, schedules);
+        result.put(Constants.DATA_LIST, scheduleList);
         putMsg(result, Status.SUCCESS);
 
         return result;
@@ -561,8 +571,10 @@ public class SchedulerServiceImpl extends BaseServiceImpl 
implements SchedulerSe
         ScheduleParam scheduleParam = JSONUtils.parseObject(schedule, 
ScheduleParam.class);
         Date now = new Date();
 
-        Date startTime = now.after(scheduleParam.getStartTime()) ? now : 
scheduleParam.getStartTime();
-        Date endTime = scheduleParam.getEndTime();
+        Date startTime = 
DateUtils.transformTimezoneDate(scheduleParam.getStartTime(), 
scheduleParam.getTimezoneId());
+        Date endTime = 
DateUtils.transformTimezoneDate(scheduleParam.getEndTime(), 
scheduleParam.getTimezoneId());
+        startTime =  now.after(startTime) ? now : startTime;
+
         try {
             cronExpression = 
CronUtils.parse2CronExpression(scheduleParam.getCrontab());
         } catch (ParseException e) {
diff --git 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/vo/ScheduleVo.java
 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/vo/ScheduleVo.java
new file mode 100644
index 0000000000..0137c76ac2
--- /dev/null
+++ 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/vo/ScheduleVo.java
@@ -0,0 +1,337 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You 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 org.apache.dolphinscheduler.api.vo;
+
+import org.apache.dolphinscheduler.common.enums.FailureStrategy;
+import org.apache.dolphinscheduler.common.enums.Priority;
+import org.apache.dolphinscheduler.common.enums.ReleaseState;
+import org.apache.dolphinscheduler.common.enums.WarningType;
+import org.apache.dolphinscheduler.common.utils.DateUtils;
+import org.apache.dolphinscheduler.dao.entity.Schedule;
+
+import java.time.ZoneId;
+import java.util.Date;
+
+public class ScheduleVo {
+
+    private int id;
+
+    /**
+     * process definition code
+     */
+    private long processDefinitionCode;
+
+    /**
+     * process definition name
+     */
+    private String processDefinitionName;
+
+    /**
+     * project name
+     */
+    private String projectName;
+
+    /**
+     * schedule description
+     */
+    private String definitionDescription;
+
+    /**
+     * schedule start time
+     */
+    private String startTime;
+
+    /**
+     * schedule end time
+     */
+    private String endTime;
+
+    /**
+     * timezoneId
+     * <p>see {@link java.util.TimeZone#getTimeZone(String)}
+     */
+    private String timezoneId;
+
+    /**
+     * crontab expression
+     */
+    private String crontab;
+
+    /**
+     * failure strategy
+     */
+    private FailureStrategy failureStrategy;
+
+    /**
+     * warning type
+     */
+    private WarningType warningType;
+
+    /**
+     * create time
+     */
+    private Date createTime;
+
+    /**
+     * update time
+     */
+    private Date updateTime;
+
+    /**
+     * created user id
+     */
+    private int userId;
+
+    /**
+     * created user name
+     */
+    private String userName;
+
+    /**
+     * release state
+     */
+    private ReleaseState releaseState;
+
+    /**
+     * warning group id
+     */
+    private int warningGroupId;
+
+
+    /**
+     * process instance priority
+     */
+    private Priority processInstancePriority;
+
+    /**
+     *  worker group
+     */
+    private String workerGroup;
+
+    /**
+     * environment code
+     */
+    private Long environmentCode;
+
+    public ScheduleVo(Schedule schedule) {
+        this.setId(schedule.getId());
+        this.setCrontab(schedule.getCrontab());
+        this.setProjectName(schedule.getProjectName());
+        this.setUserName(schedule.getUserName());
+        this.setWorkerGroup(schedule.getWorkerGroup());
+        this.setWarningType(schedule.getWarningType());
+        this.setWarningGroupId(schedule.getWarningGroupId());
+        this.setUserId(schedule.getUserId());
+        this.setUpdateTime(schedule.getUpdateTime());
+        this.setTimezoneId(schedule.getTimezoneId());
+        this.setReleaseState(schedule.getReleaseState());
+        this.setProcessInstancePriority(schedule.getProcessInstancePriority());
+        this.setProcessDefinitionName(schedule.getProcessDefinitionName());
+        this.setProcessDefinitionCode(schedule.getProcessDefinitionCode());
+        this.setFailureStrategy(schedule.getFailureStrategy());
+        this.setEnvironmentCode(schedule.getEnvironmentCode());
+        this.setStartTime(DateUtils.dateToString(schedule.getStartTime(), 
ZoneId.systemDefault().getId()));
+        this.setEndTime(DateUtils.dateToString(schedule.getEndTime(), 
ZoneId.systemDefault().getId()));
+    }
+
+    public int getWarningGroupId() {
+        return warningGroupId;
+    }
+
+    public void setWarningGroupId(int warningGroupId) {
+        this.warningGroupId = warningGroupId;
+    }
+
+    public String getProjectName() {
+        return projectName;
+    }
+
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }
+
+    public String getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(String startTime) {
+        this.startTime = startTime;
+    }
+
+    public String getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(String endTime) {
+        this.endTime = endTime;
+    }
+
+    public String getTimezoneId() {
+        return timezoneId;
+    }
+
+    public void setTimezoneId(String timezoneId) {
+        this.timezoneId = timezoneId;
+    }
+
+    public String getCrontab() {
+        return crontab;
+    }
+
+    public void setCrontab(String crontab) {
+        this.crontab = crontab;
+    }
+
+    public FailureStrategy getFailureStrategy() {
+        return failureStrategy;
+    }
+
+    public void setFailureStrategy(FailureStrategy failureStrategy) {
+        this.failureStrategy = failureStrategy;
+    }
+
+    public WarningType getWarningType() {
+        return warningType;
+    }
+
+    public void setWarningType(WarningType warningType) {
+        this.warningType = warningType;
+    }
+
+    public Date getCreateTime() {
+        return createTime;
+    }
+
+    public void setCreateTime(Date createTime) {
+        this.createTime = createTime;
+    }
+
+    public ReleaseState getReleaseState() {
+        return releaseState;
+    }
+
+    public void setReleaseState(ReleaseState releaseState) {
+        this.releaseState = releaseState;
+    }
+
+    public long getProcessDefinitionCode() {
+        return processDefinitionCode;
+    }
+
+    public void setProcessDefinitionCode(long processDefinitionCode) {
+        this.processDefinitionCode = processDefinitionCode;
+    }
+
+    public String getProcessDefinitionName() {
+        return processDefinitionName;
+    }
+
+    public void setProcessDefinitionName(String processDefinitionName) {
+        this.processDefinitionName = processDefinitionName;
+    }
+
+    public Date getUpdateTime() {
+        return updateTime;
+    }
+
+    public void setUpdateTime(Date updateTime) {
+        this.updateTime = updateTime;
+    }
+
+    public int getUserId() {
+        return userId;
+    }
+
+    public void setUserId(int userId) {
+        this.userId = userId;
+    }
+
+    public String getUserName() {
+        return userName;
+    }
+
+    public void setUserName(String userName) {
+        this.userName = userName;
+    }
+
+    public int getId() {
+        return id;
+    }
+
+    public void setId(int id) {
+        this.id = id;
+    }
+
+    public Priority getProcessInstancePriority() {
+        return processInstancePriority;
+    }
+
+    public void setProcessInstancePriority(Priority processInstancePriority) {
+        this.processInstancePriority = processInstancePriority;
+    }
+
+    public String getWorkerGroup() {
+        return workerGroup;
+    }
+
+    public void setWorkerGroup(String workerGroup) {
+        this.workerGroup = workerGroup;
+    }
+
+    public Long getEnvironmentCode() {
+        return this.environmentCode;
+    }
+
+    public void setEnvironmentCode(Long environmentCode) {
+        this.environmentCode = environmentCode;
+    }
+
+    @Override
+    public String toString() {
+        return "Schedule{"
+            + "id=" + id
+            + ", processDefinitionCode=" + processDefinitionCode
+            + ", processDefinitionName='" + processDefinitionName + '\''
+            + ", projectName='" + projectName + '\''
+            + ", description='" + definitionDescription + '\''
+            + ", startTime=" + startTime
+            + ", endTime=" + endTime
+            + ", timezoneId='" + timezoneId + +'\''
+            + ", crontab='" + crontab + '\''
+            + ", failureStrategy=" + failureStrategy
+            + ", warningType=" + warningType
+            + ", createTime=" + createTime
+            + ", updateTime=" + updateTime
+            + ", userId=" + userId
+            + ", userName='" + userName + '\''
+            + ", releaseState=" + releaseState
+            + ", warningGroupId=" + warningGroupId
+            + ", processInstancePriority=" + processInstancePriority
+            + ", workerGroup='" + workerGroup + '\''
+            + ", environmentCode='" + environmentCode + '\''
+            + '}';
+    }
+
+    public String getDefinitionDescription() {
+        return definitionDescription;
+    }
+
+    public void setDefinitionDescription(String definitionDescription) {
+        this.definitionDescription = definitionDescription;
+    }
+}
diff --git 
a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/DateUtils.java
 
b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/DateUtils.java
index 3cc7d5f340..6ee9ac99d7 100644
--- 
a/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/DateUtils.java
+++ 
b/dolphinscheduler-common/src/main/java/org/apache/dolphinscheduler/common/utils/DateUtils.java
@@ -536,17 +536,24 @@ public final class DateUtils {
 
     /**
      * transform date to target timezone date
+     * sourceTimeZoneId is system default timezone
+     */
+    public static Date transformTimezoneDate(Date date, String 
targetTimezoneId) {
+        return transformTimezoneDate(date, ZoneId.systemDefault().getId(), 
targetTimezoneId);
+    }
+
+    /**
+     * transform date from source timezone date to target timezone date
      * <p>e.g.
-     * <p> if input date is 2020-01-01 00:00:00 current timezone is CST
-     * <p>targetTimezoneId is MST
-     * <p>this method will return 2020-01-01 15:00:00
+     * <p> if input date is `Thu Apr 28 10:00:00 UTC 2022`, sourceTimezoneId 
is UTC
+     * <p>targetTimezoneId is Asia/Shanghai
+     * <p>this method will return `Thu Apr 28 02:00:00 UTC 2022`
      */
-    public static Date getTimezoneDate(Date date, String targetTimezoneId) {
-        if (StringUtils.isEmpty(targetTimezoneId)) {
+    public static Date transformTimezoneDate(Date date, String 
sourceTimezoneId, String targetTimezoneId) {
+        if (StringUtils.isEmpty(sourceTimezoneId) || 
StringUtils.isEmpty(targetTimezoneId)) {
             return date;
         }
-
-        String dateToString = dateToString(date);
+        String dateToString = dateToString(date, sourceTimezoneId);
         LocalDateTime localDateTime = LocalDateTime.parse(dateToString, 
DateTimeFormatter.ofPattern(Constants.YYYY_MM_DD_HH_MM_SS));
         ZonedDateTime zonedDateTime = ZonedDateTime.of(localDateTime, 
TimeZone.getTimeZone(targetTimezoneId).toZoneId());
         return Date.from(zonedDateTime.toInstant());
diff --git 
a/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/DateUtilsTest.java
 
b/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/DateUtilsTest.java
index e61f581155..96c4450923 100644
--- 
a/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/DateUtilsTest.java
+++ 
b/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/DateUtilsTest.java
@@ -214,9 +214,14 @@ public class DateUtilsTest {
 
     @Test
     public void testTransformToTimezone() {
+        TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
+
         Date date = new Date();
-        Date mst = DateUtils.getTimezoneDate(date, 
TimeZone.getDefault().getID());
-        Assert.assertEquals(DateUtils.dateToString(date), 
DateUtils.dateToString(mst));
+        Date defaultTimeZoneDate = DateUtils.transformTimezoneDate(date, 
TimeZone.getDefault().getID());
+        Assert.assertEquals(DateUtils.dateToString(date), 
DateUtils.dateToString(defaultTimeZoneDate));
+
+        Date targetTimeZoneDate = DateUtils.transformTimezoneDate(date, 
TimeZone.getDefault().getID(), "Asia/Shanghai");
+        Assert.assertEquals(DateUtils.dateToString(date, 
TimeZone.getDefault().getID()), DateUtils.dateToString(targetTimeZoneDate, 
"Asia/Shanghai"));
     }
 
     @Test
diff --git 
a/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/JSONUtilsTest.java
 
b/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/JSONUtilsTest.java
index b32640c189..2d638c943e 100644
--- 
a/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/JSONUtilsTest.java
+++ 
b/dolphinscheduler-common/src/test/java/org/apache/dolphinscheduler/common/utils/JSONUtilsTest.java
@@ -28,6 +28,7 @@ import java.util.HashMap;
 import java.util.LinkedHashMap;
 import java.util.List;
 import java.util.Map;
+import java.util.TimeZone;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -261,6 +262,7 @@ public class JSONUtilsTest {
 
     @Test
     public void dateToString() {
+        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
         String time = "2022-02-22 13:38:24";
         Date date = DateUtils.stringToDate(time);
         String json = JSONUtils.toJsonString(date);
@@ -272,6 +274,7 @@ public class JSONUtilsTest {
 
     @Test
     public void stringToDate() {
+        TimeZone.setDefault(TimeZone.getTimeZone("Asia/Shanghai"));
         String json = "\"2022-02-22 13:38:24\"";
         Date date = JSONUtils.parseObject(json, Date.class);
         Assert.assertEquals(date, DateUtils.stringToDate("2022-02-22 
13:38:24"));
diff --git 
a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/quartz/impl/QuartzExecutorImpl.java
 
b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/quartz/impl/QuartzExecutorImpl.java
index cd5f7803df..b4d5ee9246 100644
--- 
a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/quartz/impl/QuartzExecutorImpl.java
+++ 
b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/quartz/impl/QuartzExecutorImpl.java
@@ -17,12 +17,31 @@
 
 package org.apache.dolphinscheduler.service.quartz.impl;
 
-import org.apache.commons.lang.StringUtils;
+import static org.apache.dolphinscheduler.common.Constants.PROJECT_ID;
+import static 
org.apache.dolphinscheduler.common.Constants.QUARTZ_JOB_GROUP_PREFIX;
+import static org.apache.dolphinscheduler.common.Constants.QUARTZ_JOB_PREFIX;
+import static org.apache.dolphinscheduler.common.Constants.SCHEDULE;
+import static org.apache.dolphinscheduler.common.Constants.SCHEDULE_ID;
+import static org.apache.dolphinscheduler.common.Constants.UNDERLINE;
+
+import static org.quartz.CronScheduleBuilder.cronSchedule;
+import static org.quartz.JobBuilder.newJob;
+import static org.quartz.TriggerBuilder.newTrigger;
+
 import org.apache.dolphinscheduler.common.utils.DateUtils;
 import org.apache.dolphinscheduler.common.utils.JSONUtils;
 import org.apache.dolphinscheduler.dao.entity.Schedule;
 import org.apache.dolphinscheduler.service.exceptions.ServiceException;
 import org.apache.dolphinscheduler.service.quartz.QuartzExecutor;
+
+import org.apache.commons.lang.StringUtils;
+
+import java.util.Date;
+import java.util.HashMap;
+import java.util.Map;
+import java.util.concurrent.locks.ReadWriteLock;
+import java.util.concurrent.locks.ReentrantReadWriteLock;
+
 import org.quartz.CronTrigger;
 import org.quartz.Job;
 import org.quartz.JobDetail;
@@ -34,22 +53,6 @@ import org.slf4j.LoggerFactory;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Service;
 
-import java.util.Date;
-import java.util.HashMap;
-import java.util.Map;
-import java.util.concurrent.locks.ReadWriteLock;
-import java.util.concurrent.locks.ReentrantReadWriteLock;
-
-import static org.apache.dolphinscheduler.common.Constants.PROJECT_ID;
-import static 
org.apache.dolphinscheduler.common.Constants.QUARTZ_JOB_GROUP_PREFIX;
-import static org.apache.dolphinscheduler.common.Constants.QUARTZ_JOB_PREFIX;
-import static org.apache.dolphinscheduler.common.Constants.SCHEDULE;
-import static org.apache.dolphinscheduler.common.Constants.SCHEDULE_ID;
-import static org.apache.dolphinscheduler.common.Constants.UNDERLINE;
-import static org.quartz.CronScheduleBuilder.cronSchedule;
-import static org.quartz.JobBuilder.newJob;
-import static org.quartz.TriggerBuilder.newTrigger;
-
 @Service
 public class QuartzExecutorImpl implements QuartzExecutor {
     private static final Logger logger = 
LoggerFactory.getLogger(QuartzExecutorImpl.class);
@@ -70,12 +73,21 @@ public class QuartzExecutorImpl implements QuartzExecutor {
     public void addJob(Class<? extends Job> clazz, int projectId, final 
Schedule schedule) {
         String jobName = this.buildJobName(schedule.getId());
         String jobGroupName = this.buildJobGroupName(projectId);
-        Date startDate = schedule.getStartTime();
-        Date endDate = schedule.getEndTime();
+
         Map<String, Object> jobDataMap = this.buildDataMap(projectId, 
schedule);
         String cronExpression = schedule.getCrontab();
         String timezoneId = schedule.getTimezoneId();
 
+        /**
+         * transform from server default timezone to schedule timezone
+         * e.g. server default timezone is `UTC`
+         * user set a schedule with startTime `2022-04-28 10:00:00`, timezone 
is `Asia/Shanghai`,
+         * api skip to transform it and save into databases directly, 
startTime `2022-04-28 10:00:00`, timezone is `UTC`, which actually added 8 
hours,
+         * so when add job to quartz, it should recover by transform timezone
+         */
+        Date startDate = 
DateUtils.transformTimezoneDate(schedule.getStartTime(), timezoneId);
+        Date endDate = DateUtils.transformTimezoneDate(schedule.getEndTime(), 
timezoneId);
+
         lock.writeLock().lock();
         try {
 
@@ -107,8 +119,8 @@ public class QuartzExecutorImpl implements QuartzExecutor {
              */
             CronTrigger cronTrigger = newTrigger()
                     .withIdentity(triggerKey)
-                    .startAt(DateUtils.getTimezoneDate(startDate, timezoneId))
-                    .endAt(DateUtils.getTimezoneDate(endDate, timezoneId))
+                    .startAt(startDate)
+                    .endAt(endDate)
                     .withSchedule(
                             cronSchedule(cronExpression)
                                     .withMisfireHandlingInstructionDoNothing()
@@ -140,13 +152,11 @@ public class QuartzExecutorImpl implements QuartzExecutor 
{
         }
     }
 
-
     @Override
     public String buildJobName(int processId) {
         return QUARTZ_JOB_PREFIX + UNDERLINE + processId;
     }
 
-
     @Override
     public String buildJobGroupName(int projectId) {
         return QUARTZ_JOB_GROUP_PREFIX + UNDERLINE + projectId;

Reply via email to