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

chengshiwen 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 a925f64  [Feature][api] schedule add timezone support #5259 (#5465)
a925f64 is described below

commit a925f645719edec4afdb994ef57029c45c55d27e
Author: ruanwenjun <[email protected]>
AuthorDate: Mon May 17 21:08:20 2021 +0800

    [Feature][api] schedule add timezone support #5259 (#5465)
    
    * [Feature][api] schedule add timezone support #5259
    
    * import moment-timezone
    
    * fix code smell
---
 .../api/controller/SchedulerController.java        |   3 +-
 .../dolphinscheduler/api/dto/ScheduleParam.java    | 112 ++--
 .../api/service/impl/SchedulerServiceImpl.java     |  17 +-
 .../dolphinscheduler/common/utils/DateUtils.java   |  30 ++
 .../common/utils/DateUtilsTest.java                |  13 +
 .../dolphinscheduler/dao/entity/Schedule.java      | 567 +++++++++++----------
 .../dolphinscheduler/dao/mapper/ScheduleMapper.xml |   4 +-
 dolphinscheduler-dist/release-docs/LICENSE         |   1 +
 .../licenses/ui-licenses/LICENSE-moment-timezone   |  20 +
 .../service/quartz/QuartzExecutors.java            |  45 +-
 dolphinscheduler-ui/package.json                   |   1 +
 .../pages/definition/pages/list/_source/timing.vue |  22 +-
 .../src/js/module/i18n/locale/en_US.js             |   1 +
 .../src/js/module/i18n/locale/zh_CN.js             |   1 +
 sql/dolphinscheduler_mysql.sql                     |   1 +
 sql/dolphinscheduler_postgre.sql                   |   1 +
 .../1.4.0_schema/mysql/dolphinscheduler_ddl.sql    |  20 +
 .../postgresql/dolphinscheduler_ddl.sql            |  17 +
 18 files changed, 510 insertions(+), 366 deletions(-)

diff --git 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/SchedulerController.java
 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/SchedulerController.java
index 772fdce..20b668a 100644
--- 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/SchedulerController.java
+++ 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/controller/SchedulerController.java
@@ -94,7 +94,8 @@ public class SchedulerController extends BaseController {
     @ApiOperation(value = "createSchedule", notes = "CREATE_SCHEDULE_NOTES")
     @ApiImplicitParams({
             @ApiImplicitParam(name = "processDefinitionId", value = 
"PROCESS_DEFINITION_ID", required = true, dataType = "Int", example = "100"),
-            @ApiImplicitParam(name = "schedule", value = "SCHEDULE", dataType 
= "String", example = "{'startTime':'2019-06-10 00:00:00','endTime':'2019-06-13 
00:00:00','crontab':'0 0 3/6 * * ? *'}"),
+            @ApiImplicitParam(name = "schedule", value = "SCHEDULE", dataType 
= "String",
+                    example = "{'startTime':'2019-06-10 
00:00:00','endTime':'2019-06-13 
00:00:00','timezoneId':'America/Phoenix','crontab':'0 0 3/6 * * ? *'}"),
             @ApiImplicitParam(name = "warningType", value = "WARNING_TYPE", 
type = "WarningType"),
             @ApiImplicitParam(name = "warningGroupId", value = 
"WARNING_GROUP_ID", dataType = "Int", example = "100"),
             @ApiImplicitParam(name = "failureStrategy", value = 
"FAILURE_STRATEGY", type = "FailureStrategy"),
diff --git 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/ScheduleParam.java
 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/ScheduleParam.java
index a7cdfcf..6d957bf 100644
--- 
a/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/ScheduleParam.java
+++ 
b/dolphinscheduler-api/src/main/java/org/apache/dolphinscheduler/api/dto/ScheduleParam.java
@@ -14,62 +14,74 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
-package org.apache.dolphinscheduler.api.dto;
 
-import com.fasterxml.jackson.annotation.JsonFormat;
+package org.apache.dolphinscheduler.api.dto;
 
 import java.util.Date;
 
+import com.fasterxml.jackson.annotation.JsonFormat;
+
 /**
  * schedule parameters
  */
 public class ScheduleParam {
-  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
-  private Date startTime;
-  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
-  private Date endTime;
-  private String crontab;
-
-  public ScheduleParam() {
-  }
-
-  public ScheduleParam(Date startTime, Date endTime, String crontab) {
-    this.startTime = startTime;
-    this.endTime = endTime;
-    this.crontab = crontab;
-  }
-
-  public Date getStartTime() {
-    return startTime;
-  }
-
-  public void setStartTime(Date startTime) {
-    this.startTime = startTime;
-  }
-
-  public Date getEndTime() {
-    return endTime;
-  }
-
-  public void setEndTime(Date endTime) {
-    this.endTime = endTime;
-  }
-
-  public String getCrontab() {
-    return crontab;
-  }
-
-  public void setCrontab(String crontab) {
-    this.crontab = crontab;
-  }
-
-
-  @Override
-  public String toString() {
-    return "ScheduleParam{" +
-            "startTime=" + startTime +
-            ", endTime=" + endTime +
-            ", crontab='" + crontab + '\'' +
-            '}';
-  }
+
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date startTime;
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss", timezone = "GMT+8")
+    private Date endTime;
+    private String crontab;
+    private String timezoneId;
+
+    public ScheduleParam() {
+    }
+
+    public ScheduleParam(Date startTime, Date endTime, String timezoneId, 
String crontab) {
+        this.startTime = startTime;
+        this.endTime = endTime;
+        this.timezoneId = timezoneId;
+        this.crontab = crontab;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date endTime) {
+        this.endTime = endTime;
+    }
+
+    public String getCrontab() {
+        return crontab;
+    }
+
+    public void setCrontab(String crontab) {
+        this.crontab = crontab;
+    }
+
+    public String getTimezoneId() {
+        return timezoneId;
+    }
+
+    public void setTimezoneId(String timezoneId) {
+        this.timezoneId = timezoneId;
+    }
+
+    @Override
+    public String toString() {
+        return "ScheduleParam{"
+                + "startTime=" + startTime
+                + ", endTime=" + endTime
+                + ", crontab='" + crontab + '\''
+                + ", timezoneId='" + timezoneId + '\''
+                + '}';
+    }
 }
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 5955a44..5f17e80 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
@@ -157,6 +157,7 @@ public class SchedulerServiceImpl extends BaseServiceImpl 
implements SchedulerSe
             return result;
         }
         scheduleObj.setCrontab(scheduleParam.getCrontab());
+        scheduleObj.setTimezoneId(scheduleParam.getTimezoneId());
         scheduleObj.setWarningType(warningType);
         scheduleObj.setWarningGroupId(warningGroupId);
         scheduleObj.setFailureStrategy(failureStrategy);
@@ -258,6 +259,7 @@ public class SchedulerServiceImpl extends BaseServiceImpl 
implements SchedulerSe
                 return result;
             }
             schedule.setCrontab(scheduleParam.getCrontab());
+            schedule.setTimezoneId(scheduleParam.getTimezoneId());
         }
 
         if (warningType != null) {
@@ -471,20 +473,9 @@ public class SchedulerServiceImpl extends BaseServiceImpl 
implements SchedulerSe
     }
 
     public void setSchedule(int projectId, Schedule schedule) {
-        int scheduleId = schedule.getId();
-        logger.info("set schedule, project id: {}, scheduleId: {}", projectId, 
scheduleId);
-
-        Date startDate = schedule.getStartTime();
-        Date endDate = schedule.getEndTime();
-
-        String jobName = QuartzExecutors.buildJobName(scheduleId);
-        String jobGroupName = QuartzExecutors.buildJobGroupName(projectId);
-
-        Map<String, Object> dataMap = QuartzExecutors.buildDataMap(projectId, 
scheduleId, schedule);
-
-        QuartzExecutors.getInstance().addJob(ProcessScheduleJob.class, 
jobName, jobGroupName, startDate, endDate,
-                schedule.getCrontab(), dataMap);
+        logger.info("set schedule, project id: {}, scheduleId: {}", projectId, 
schedule.getId());
 
+        QuartzExecutors.getInstance().addJob(ProcessScheduleJob.class, 
projectId, schedule);
     }
 
     /**
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 c484dc0..6ea527a 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
@@ -22,9 +22,11 @@ import org.apache.dolphinscheduler.common.Constants;
 import java.time.Instant;
 import java.time.LocalDateTime;
 import java.time.ZoneId;
+import java.time.ZonedDateTime;
 import java.time.format.DateTimeFormatter;
 import java.util.Calendar;
 import java.util.Date;
+import java.util.TimeZone;
 
 import org.slf4j.Logger;
 import org.slf4j.LoggerFactory;
@@ -492,6 +494,34 @@ public class DateUtils {
         return getCurrentTime(Constants.YYYYMMDDHHMMSSSSS);
     }
 
+    /**
+     * transform 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
+     */
+    public static Date getTimezoneDate(Date date, String targetTimezoneId) {
+        if (StringUtils.isEmpty(targetTimezoneId)) {
+            return date;
+        }
+
+        String dateToString = dateToString(date);
+        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());
+    }
+
+    /**
+     * get timezone by timezoneId
+     */
+    public static TimeZone getTimezone(String timezoneId) {
+        if (StringUtils.isEmpty(timezoneId)) {
+            return null;
+        }
+        return TimeZone.getTimeZone(timezoneId);
+    }
+
     static final long C0 = 1L;
     static final long C1 = C0 * 1000L;
     static final long C2 = C1 * 1000L;
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 4a88085..38ab720 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
@@ -20,6 +20,7 @@ package org.apache.dolphinscheduler.common.utils;
 import java.text.ParseException;
 import java.text.SimpleDateFormat;
 import java.util.Date;
+import java.util.TimeZone;
 
 import org.junit.Assert;
 import org.junit.Test;
@@ -204,4 +205,16 @@ public class DateUtilsTest {
         Assert.assertNull(DateUtils.format2Duration(d1, d2));
     }
 
+    @Test
+    public void testTransformToTimezone() {
+        Date date = new Date();
+        Date mst = DateUtils.getTimezoneDate(date, 
TimeZone.getDefault().getID());
+        Assert.assertEquals(DateUtils.dateToString(date), 
DateUtils.dateToString(mst));
+    }
+
+    @Test
+    public void testGetTimezone() {
+        Assert.assertNull(DateUtils.getTimezone(null));
+        Assert.assertEquals(TimeZone.getTimeZone("MST"), 
DateUtils.getTimezone("MST"));
+    }
 }
diff --git 
a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/Schedule.java
 
b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/Schedule.java
index 5d2bc38..74ed5c1 100644
--- 
a/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/Schedule.java
+++ 
b/dolphinscheduler-dao/src/main/java/org/apache/dolphinscheduler/dao/entity/Schedule.java
@@ -14,13 +14,9 @@
  * See the License for the specific language governing permissions and
  * limitations under the License.
  */
+
 package org.apache.dolphinscheduler.dao.entity;
 
-import com.baomidou.mybatisplus.annotation.IdType;
-import com.baomidou.mybatisplus.annotation.TableField;
-import com.baomidou.mybatisplus.annotation.TableId;
-import com.baomidou.mybatisplus.annotation.TableName;
-import com.fasterxml.jackson.annotation.JsonFormat;
 import org.apache.dolphinscheduler.common.enums.FailureStrategy;
 import org.apache.dolphinscheduler.common.enums.Priority;
 import org.apache.dolphinscheduler.common.enums.ReleaseState;
@@ -28,6 +24,12 @@ import org.apache.dolphinscheduler.common.enums.WarningType;
 
 import java.util.Date;
 
+import com.baomidou.mybatisplus.annotation.IdType;
+import com.baomidou.mybatisplus.annotation.TableField;
+import com.baomidou.mybatisplus.annotation.TableId;
+import com.baomidou.mybatisplus.annotation.TableName;
+import com.fasterxml.jackson.annotation.JsonFormat;
+
 /**
  * schedule
  *
@@ -35,278 +37,285 @@ import java.util.Date;
 @TableName("t_ds_schedules")
 public class Schedule {
 
-  @TableId(value="id", type=IdType.AUTO)
-  private int id;
-  /**
-   * process definition id
-   */
-  private int processDefinitionId;
-
-  /**
-   * process definition name
-   */
-  @TableField(exist = false)
-  private String processDefinitionName;
-
-  /**
-   * project name
-   */
-  @TableField(exist = false)
-  private String projectName;
-
-  /**
-   * schedule description
-   */
-  @TableField(exist = false)
-  private String definitionDescription;
-
-  /**
-   * schedule start time
-   */
-  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
-  private Date startTime;
-
-  /**
-   * schedule end time
-   */
-  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
-  private Date endTime;
-
-  /**
-   * crontab expression
-   */
-  private String crontab;
-
-  /**
-   * failure strategy
-   */
-  private FailureStrategy failureStrategy;
-
-  /**
-   * warning type
-   */
-  private WarningType warningType;
-
-  /**
-   * create time
-   */
-  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
-  private Date createTime;
-
-  /**
-   * update time
-   */
-  @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone="GMT+8")
-  private Date updateTime;
-
-  /**
-   * created user id
-   */
-  private int userId;
-
-  /**
-   * created user name
-   */
-  @TableField(exist = false)
-  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;
-
-  public int getWarningGroupId() {
-    return warningGroupId;
-  }
-
-  public void setWarningGroupId(int warningGroupId) {
-    this.warningGroupId = warningGroupId;
-  }
-
-
-
-  public Schedule() {
-  }
-
-  public String getProjectName() {
-    return projectName;
-  }
-
-  public void setProjectName(String projectName) {
-    this.projectName = projectName;
-  }
-
-
-
-  public Date getStartTime() {
-
-    return startTime;
-  }
-
-  public void setStartTime(Date startTime) {
-    this.startTime = startTime;
-  }
-
-  public Date getEndTime() {
-    return endTime;
-  }
-
-  public void setEndTime(Date endTime) {
-    this.endTime = endTime;
-  }
-
-  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 int getProcessDefinitionId() {
-    return processDefinitionId;
-  }
-
-  public void setProcessDefinitionId(int processDefinitionId) {
-    this.processDefinitionId = processDefinitionId;
-  }
-
-  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;
-  }
-
-  @Override
-  public String toString() {
-    return "Schedule{" +
-            "id=" + id +
-            ", processDefinitionId=" + processDefinitionId +
-            ", processDefinitionName='" + processDefinitionName + '\'' +
-            ", projectName='" + projectName + '\'' +
-            ", description='" + definitionDescription + '\'' +
-            ", startTime=" + startTime +
-            ", endTime=" + endTime +
-            ", crontab='" + crontab + '\'' +
-            ", failureStrategy=" + failureStrategy +
-            ", warningType=" + warningType +
-            ", createTime=" + createTime +
-            ", updateTime=" + updateTime +
-            ", userId=" + userId +
-            ", userName='" + userName + '\'' +
-            ", releaseState=" + releaseState +
-            ", warningGroupId=" + warningGroupId +
-            ", processInstancePriority=" + processInstancePriority +
-            ", workerGroup='" + workerGroup + '\'' +
-            '}';
-  }
-
-  public String getDefinitionDescription() {
-    return definitionDescription;
-  }
-
-  public void setDefinitionDescription(String definitionDescription) {
-    this.definitionDescription = definitionDescription;
-  }
+    @TableId(value = "id", type = IdType.AUTO)
+    private int id;
+    /**
+     * process definition id
+     */
+    private int processDefinitionId;
+
+    /**
+     * process definition name
+     */
+    @TableField(exist = false)
+    private String processDefinitionName;
+
+    /**
+     * project name
+     */
+    @TableField(exist = false)
+    private String projectName;
+
+    /**
+     * schedule description
+     */
+    @TableField(exist = false)
+    private String definitionDescription;
+
+    /**
+     * schedule start time
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+    private Date startTime;
+
+    /**
+     * schedule end time
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+    private Date 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
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+    private Date createTime;
+
+    /**
+     * update time
+     */
+    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss",timezone = "GMT+8")
+    private Date updateTime;
+
+    /**
+     * created user id
+     */
+    private int userId;
+
+    /**
+     * created user name
+     */
+    @TableField(exist = false)
+    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;
+
+    public int getWarningGroupId() {
+        return warningGroupId;
+    }
+
+    public void setWarningGroupId(int warningGroupId) {
+        this.warningGroupId = warningGroupId;
+    }
+
+    public Schedule() {
+    }
+
+    public String getProjectName() {
+        return projectName;
+    }
+
+    public void setProjectName(String projectName) {
+        this.projectName = projectName;
+    }
+
+    public Date getStartTime() {
+        return startTime;
+    }
+
+    public void setStartTime(Date startTime) {
+        this.startTime = startTime;
+    }
+
+    public Date getEndTime() {
+        return endTime;
+    }
+
+    public void setEndTime(Date 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 int getProcessDefinitionId() {
+        return processDefinitionId;
+    }
+
+    public void setProcessDefinitionId(int processDefinitionId) {
+        this.processDefinitionId = processDefinitionId;
+    }
+
+    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;
+    }
+
+    @Override
+    public String toString() {
+        return "Schedule{"
+                + "id=" + id
+                + ", processDefinitionId=" + processDefinitionId
+                + ", 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 + '\''
+                + '}';
+    }
+
+    public String getDefinitionDescription() {
+        return definitionDescription;
+    }
+
+    public void setDefinitionDescription(String definitionDescription) {
+        this.definitionDescription = definitionDescription;
+    }
 }
diff --git 
a/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/ScheduleMapper.xml
 
b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/ScheduleMapper.xml
index ae95655..587680f 100644
--- 
a/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/ScheduleMapper.xml
+++ 
b/dolphinscheduler-dao/src/main/resources/org/apache/dolphinscheduler/dao/mapper/ScheduleMapper.xml
@@ -19,11 +19,11 @@
 <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" 
"http://mybatis.org/dtd/mybatis-3-mapper.dtd"; >
 <mapper namespace="org.apache.dolphinscheduler.dao.mapper.ScheduleMapper">
     <sql id="baseSql">
-        id, process_definition_id, start_time, end_time, crontab, 
failure_strategy, user_id, release_state,
+        id, process_definition_id, start_time, end_time, timezone_id, crontab, 
failure_strategy, user_id, release_state,
         warning_type, warning_group_id, process_instance_priority, 
worker_group, create_time, update_time
     </sql>
     <sql id="baseSqlV2">
-        ${alias}.id, ${alias}.process_definition_id, ${alias}.start_time, 
${alias}.end_time, ${alias}.crontab, ${alias}.failure_strategy, 
${alias}.user_id, ${alias}.release_state,
+        ${alias}.id, ${alias}.process_definition_id, ${alias}.start_time, 
${alias}.end_time, ${alias}.timezone_id, ${alias}.crontab, 
${alias}.failure_strategy, ${alias}.user_id, ${alias}.release_state,
         ${alias}.warning_type, ${alias}.warning_group_id, 
${alias}.process_instance_priority, ${alias}.worker_group, 
${alias}.create_time, ${alias}.update_time
     </sql>
     <select id="queryByProcessDefineIdPaging" 
resultType="org.apache.dolphinscheduler.dao.entity.Schedule">
diff --git a/dolphinscheduler-dist/release-docs/LICENSE 
b/dolphinscheduler-dist/release-docs/LICENSE
index bc18daf..2308359 100644
--- a/dolphinscheduler-dist/release-docs/LICENSE
+++ b/dolphinscheduler-dist/release-docs/LICENSE
@@ -539,6 +539,7 @@ MIT licenses
     js-cookie 2.2.1: https://github.com/js-cookie/js-cookie MIT
     jsplumb 2.8.6: https://github.com/jsplumb/jsplumb MIT and GPLv2
     lodash 4.17.11: https://github.com/lodash/lodash MIT
+    moment-timezone 0.5.33: https://github.com/moment/moment-timezone MIT
     vue-treeselect 0.4.0: https://github.com/riophae/vue-treeselect MIT
     vue        2.5.17: https://github.com/vuejs/vue    MIT
     vue-router 2.7.0: https://github.com/vuejs/vue-router MIT
diff --git 
a/dolphinscheduler-dist/release-docs/licenses/ui-licenses/LICENSE-moment-timezone
 
b/dolphinscheduler-dist/release-docs/licenses/ui-licenses/LICENSE-moment-timezone
new file mode 100644
index 0000000..f0bb3b4
--- /dev/null
+++ 
b/dolphinscheduler-dist/release-docs/licenses/ui-licenses/LICENSE-moment-timezone
@@ -0,0 +1,20 @@
+The MIT License (MIT)
+
+Copyright (c) JS Foundation and other contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of
+this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to
+use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies 
of
+the Software, and to permit persons to whom the Software is furnished to do so,
+subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 
FITNESS
+FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
+COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
+IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
+CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
\ No newline at end of file
diff --git 
a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/quartz/QuartzExecutors.java
 
b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/quartz/QuartzExecutors.java
index 7f53f8e..487abad 100644
--- 
a/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/quartz/QuartzExecutors.java
+++ 
b/dolphinscheduler-service/src/main/java/org/apache/dolphinscheduler/service/quartz/QuartzExecutors.java
@@ -60,6 +60,7 @@ 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.common.utils.PropertyUtils;
 import org.apache.dolphinscheduler.common.utils.StringUtils;
@@ -220,16 +221,18 @@ public class QuartzExecutors {
      * add task trigger , if this task already exists, return this task with 
updated trigger
      *
      * @param clazz job class name
-     * @param jobName job name
-     * @param jobGroupName job group name
-     * @param startDate job start date
-     * @param endDate job end date
-     * @param cronExpression cron expression
-     * @param jobDataMap job parameters data map
+     * @param projectId projectId
+     * @param schedule schedule
      */
-    public void addJob(Class<? extends Job> clazz, String jobName, String 
jobGroupName, Date startDate, Date endDate,
-                       String cronExpression,
-                       Map<String, Object> jobDataMap) {
+    public void addJob(Class<? extends Job> clazz, int projectId, final 
Schedule schedule) {
+        String jobName = QuartzExecutors.buildJobName(schedule.getId());
+        String jobGroupName = QuartzExecutors.buildJobGroupName(projectId);
+        Date startDate = schedule.getStartTime();
+        Date endDate = schedule.getEndTime();
+        Map<String, Object> jobDataMap = 
QuartzExecutors.buildDataMap(projectId, schedule);
+        String cronExpression = schedule.getCrontab();
+        String timezoneId = schedule.getTimezoneId();
+
         lock.writeLock().lock();
         try {
 
@@ -239,15 +242,11 @@ public class QuartzExecutors {
             if (scheduler.checkExists(jobKey)) {
 
                 jobDetail = scheduler.getJobDetail(jobKey);
-                if (jobDataMap != null) {
-                    jobDetail.getJobDataMap().putAll(jobDataMap);
-                }
+                jobDetail.getJobDataMap().putAll(jobDataMap);
             } else {
                 jobDetail = newJob(clazz).withIdentity(jobKey).build();
 
-                if (jobDataMap != null) {
-                    jobDetail.getJobDataMap().putAll(jobDataMap);
-                }
+                jobDetail.getJobDataMap().putAll(jobDataMap);
 
                 scheduler.addJob(jobDetail, false, true);
 
@@ -263,8 +262,15 @@ public class QuartzExecutors {
              * current time (taking into account any associated Calendar),
              * but it does not want to be fired now.
              */
-            CronTrigger cronTrigger = 
newTrigger().withIdentity(triggerKey).startAt(startDate).endAt(endDate)
-                    
.withSchedule(cronSchedule(cronExpression).withMisfireHandlingInstructionDoNothing())
+            CronTrigger cronTrigger = newTrigger()
+                    .withIdentity(triggerKey)
+                    .startAt(DateUtils.getTimezoneDate(startDate, timezoneId))
+                    .endAt(DateUtils.getTimezoneDate(endDate, timezoneId))
+                    .withSchedule(
+                            cronSchedule(cronExpression)
+                                    .withMisfireHandlingInstructionDoNothing()
+                                    
.inTimeZone(DateUtils.getTimezone(timezoneId))
+                    )
                     .forJob(jobDetail).build();
 
             if (scheduler.checkExists(triggerKey)) {
@@ -368,14 +374,13 @@ public class QuartzExecutors {
      * add params to map
      *
      * @param projectId project id
-     * @param scheduleId schedule id
      * @param schedule schedule
      * @return data map
      */
-    public static Map<String, Object> buildDataMap(int projectId, int 
scheduleId, Schedule schedule) {
+    public static Map<String, Object> buildDataMap(int projectId, Schedule 
schedule) {
         Map<String, Object> dataMap = new HashMap<>(8);
         dataMap.put(PROJECT_ID, projectId);
-        dataMap.put(SCHEDULE_ID, scheduleId);
+        dataMap.put(SCHEDULE_ID, schedule.getId());
         dataMap.put(SCHEDULE, JSONUtils.toJsonString(schedule));
 
         return dataMap;
diff --git a/dolphinscheduler-ui/package.json b/dolphinscheduler-ui/package.json
index 12a2cee..417dbad 100644
--- a/dolphinscheduler-ui/package.json
+++ b/dolphinscheduler-ui/package.json
@@ -33,6 +33,7 @@
     "js-cookie": "^2.2.1",
     "jsplumb": "^2.8.6",
     "lodash": "^4.17.11",
+    "moment-timezone": "^0.5.33",
     "normalize.css": "^8.0.1",
     "remixicon": "^2.5.0",
     "vue": "^2.5.17",
diff --git 
a/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/timing.vue
 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/timing.vue
index 9fa497f..08e06cd 100644
--- 
a/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/timing.vue
+++ 
b/dolphinscheduler-ui/src/js/conf/home/pages/projects/pages/definition/pages/list/_source/timing.vue
@@ -62,6 +62,21 @@
       </div>
     </div>
     <div class="clearfix list">
+      <div class="text">
+        {{$t('Timezone')}}
+      </div>
+      <div class="cont">
+        <el-select v-model=timezoneId filterable placeholder="Timezone">
+          <el-option
+            v-for="item in availableTimezoneIDList"
+            :key="item"
+            :label="item"
+            :value="item">
+          </el-option>
+        </el-select>
+      </div>
+    </div>
+    <div class="clearfix list">
       <div style = "padding-left: 150px;">{{$t('Next five execution 
times')}}</div>
       <ul style = "padding-left: 150px;">
         <li v-for="(time,i) in previewTimes" :key='i'>{{time}}</li>
@@ -144,6 +159,7 @@
   </div>
 </template>
 <script>
+  import moment from 'moment-timezone'
   import i18n from '@/module/i18n'
   import store from '@/conf/home/store'
   import { warningTypeList } from './util'
@@ -160,12 +176,14 @@
         processDefinitionId: 0,
         failureStrategy: 'CONTINUE',
         warningTypeList: warningTypeList,
+        availableTimezoneIDList: moment.tz.names(),
         warningType: 'NONE',
         notifyGroupList: [],
         warningGroupId: '',
         spinnerLoading: false,
         scheduleTime: '',
         crontab: '0 0 * * * ? *',
+        timezoneId: moment.tz.guess(),
         cronPopover: false,
         i18n: i18n.globalScope.LOCALE,
         processInstancePriority: 'MEDIUM',
@@ -204,7 +222,8 @@
             schedule: JSON.stringify({
               startTime: this.scheduleTime[0],
               endTime: this.scheduleTime[1],
-              crontab: this.crontab
+              crontab: this.crontab,
+              timezoneId: this.timezoneId
             }),
             failureStrategy: this.failureStrategy,
             warningType: this.warningType,
@@ -323,6 +342,7 @@
       if (this.timingData.item.crontab) {
         this.crontab = item.crontab
         this.scheduleTime = [formatDate(item.startTime), 
formatDate(item.endTime)]
+        this.timezoneId = item.timezoneId === null ? moment.tz.guess() : 
item.timezoneId
         this.failureStrategy = item.failureStrategy
         this.warningType = item.warningType
         this.processInstancePriority = item.processInstancePriority
diff --git a/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js 
b/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js
index 0ee852c..bc05c65 100755
--- a/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js
+++ b/dolphinscheduler-ui/src/js/module/i18n/locale/en_US.js
@@ -399,6 +399,7 @@ export default {
   'Import process': 'Import process',
   'Timing state': 'Timing state',
   Timing: 'Timing',
+  Timezone: 'Timezone',
   TreeView: 'TreeView',
   'Mailbox already exists! Recipients and copyers cannot repeat': 'Mailbox 
already exists! Recipients and copyers cannot repeat',
   'Mailbox input is illegal': 'Mailbox input is illegal',
diff --git a/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js 
b/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js
index d67113a..478f675 100755
--- a/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js
+++ b/dolphinscheduler-ui/src/js/module/i18n/locale/zh_CN.js
@@ -399,6 +399,7 @@ export default {
   'Import process': '导入工作流',
   'Timing state': '定时状态',
   Timing: '定时',
+  Timezone: '时区',
   TreeView: '树形图',
   'Mailbox already exists! Recipients and copyers cannot repeat': 
'邮箱已存在!收件人和抄送人不能重复',
   'Mailbox input is illegal': '邮箱输入不合法',
diff --git a/sql/dolphinscheduler_mysql.sql b/sql/dolphinscheduler_mysql.sql
index 864b327..47fe310 100644
--- a/sql/dolphinscheduler_mysql.sql
+++ b/sql/dolphinscheduler_mysql.sql
@@ -750,6 +750,7 @@ CREATE TABLE `t_ds_schedules` (
   `process_definition_id` int(11) NOT NULL COMMENT 'process definition id',
   `start_time` datetime NOT NULL COMMENT 'start time',
   `end_time` datetime NOT NULL COMMENT 'end time',
+  `timezone_id` varchar(40) DEFAULT NULL COMMENT 'timezoneId',
   `crontab` varchar(256) NOT NULL COMMENT 'crontab description',
   `failure_strategy` tinyint(4) NOT NULL COMMENT 'failure strategy. 
0:end,1:continue',
   `user_id` int(11) NOT NULL COMMENT 'user id',
diff --git a/sql/dolphinscheduler_postgre.sql b/sql/dolphinscheduler_postgre.sql
index fd9c1ec..f9299cd 100644
--- a/sql/dolphinscheduler_postgre.sql
+++ b/sql/dolphinscheduler_postgre.sql
@@ -614,6 +614,7 @@ CREATE TABLE t_ds_schedules (
   process_definition_id int NOT NULL ,
   start_time timestamp NOT NULL ,
   end_time timestamp NOT NULL ,
+  timezone_id varchar(40) default NULL ,
   crontab varchar(256) NOT NULL ,
   failure_strategy int NOT NULL ,
   user_id int NOT NULL ,
diff --git a/sql/upgrade/1.4.0_schema/mysql/dolphinscheduler_ddl.sql 
b/sql/upgrade/1.4.0_schema/mysql/dolphinscheduler_ddl.sql
index 744294a..b9d9c72 100644
--- a/sql/upgrade/1.4.0_schema/mysql/dolphinscheduler_ddl.sql
+++ b/sql/upgrade/1.4.0_schema/mysql/dolphinscheduler_ddl.sql
@@ -316,6 +316,26 @@ d//
 delimiter ;
 CALL uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName();
 DROP PROCEDURE uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName;
+
+-- uc_dolphin_T_t_ds_schedules_A_add_timezone
+drop PROCEDURE if EXISTS uc_dolphin_T_t_ds_schedules_A_add_timezone;
+delimiter d//
+CREATE PROCEDURE uc_dolphin_T_t_ds_schedules_A_add_timezone()
+BEGIN
+    IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS
+                   WHERE TABLE_NAME='t_ds_schedules'
+                     AND TABLE_SCHEMA=(SELECT DATABASE())
+                     AND COLUMN_NAME ='timezone_id')
+    THEN
+        ALTER TABLE t_ds_schedules ADD COLUMN `timezone_id` varchar(40) 
default NULL COMMENT 'schedule timezone id' AFTER `end_time`;
+    END IF;
+END;
+
+d//
+
+delimiter ;
+CALL uc_dolphin_T_t_ds_schedules_A_add_timezone();
+DROP PROCEDURE uc_dolphin_T_t_ds_schedules_A_add_timezone;
 -- ----------------------------
 -- These columns will not be used in the new version,if you determine that the 
historical data is useless, you can delete it using the sql below
 -- ----------------------------
diff --git a/sql/upgrade/1.4.0_schema/postgresql/dolphinscheduler_ddl.sql 
b/sql/upgrade/1.4.0_schema/postgresql/dolphinscheduler_ddl.sql
index 13aa298..0fadd71 100644
--- a/sql/upgrade/1.4.0_schema/postgresql/dolphinscheduler_ddl.sql
+++ b/sql/upgrade/1.4.0_schema/postgresql/dolphinscheduler_ddl.sql
@@ -304,6 +304,23 @@ delimiter ;
 SELECT uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName();
 DROP FUNCTION IF EXISTS uc_dolphin_T_t_ds_datasource_A_add_UN_datasourceName();
 
+-- uc_dolphin_T_t_ds_schedules_A_add_timezone
+delimiter d//
+CREATE OR REPLACE FUNCTION uc_dolphin_T_t_ds_schedules_A_add_timezone() 
RETURNS void AS $$
+BEGIN
+    IF NOT EXISTS (SELECT 1 FROM information_schema.COLUMNS
+          WHERE TABLE_NAME='t_ds_schedules'
+                            AND COLUMN_NAME ='timezone_id')
+      THEN
+ALTER TABLE t_ds_schedules ADD COLUMN timezone_id varchar(40) DEFAULT NULL;
+END IF;
+END;
+$$ LANGUAGE plpgsql;
+d//
+
+delimiter ;
+SELECT uc_dolphin_T_t_ds_schedules_A_add_timezone();
+DROP FUNCTION IF EXISTS uc_dolphin_T_t_ds_schedules_A_add_timezone();
 -- ----------------------------
 -- These columns will not be used in the new version,if you determine that the 
historical data is useless, you can delete it using the sql below
 -- ----------------------------

Reply via email to