You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Boot集成Quartz触发器执行时间延迟8小时求助

问题:Spring Boot + Quartz 定时任务时区异常排查

环境与核心代码

1. 依赖引入

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-quartz</artifactId>
</dependency>

2. TimeJob 实体类

@Data
@AllArgsConstructor
@NoArgsConstructor
@TableName("time_job")
public class TimeJob {

    @Null(groups = {Add.class},message = "不允许指定id")
    @NotNull(groups = {Update.class},message = "不允许指定id")
    @TableId(type = IdType.AUTO)
    private Integer jobId;
   
    @NotBlank(groups = {Add.class,Update.class},message = "jobName不能为空")
    private String jobName;

    @Null(groups = {Add.class,Update.class},message = "不允许指定time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date createTime;

    @Null(groups = {Add.class,Update.class},message = "不允许指定time")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date updateTime;

    @NotBlank(groups = {Add.class,Update.class},message = "cron表达式不能为空")
    private String cronExpress;

    @Pattern(groups = {Add.class},regexp = "^\\[[1-9]+(,[1-9]+)*\\]$",message = "不符合数组规范")
    private String floorList;

    @NotNull(groups = {Add.class,Update.class},message = "startAt不能为空")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date startAt;

    @NotNull(groups = {Add.class,Update.class},message = "endAt不能为空")
    @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
    private Date endAt;

    @NotNull(groups = {Add.class,Update.class},message = "enabled不能为空")
    private Boolean enabled;

    @NotNull(groups = {Add.class,Update.class},message = "UnitsId不能为空")
    private Integer unitsId;

    @Null(groups = {Add.class,Update.class},message = "不允许指定createBy")
    private Integer createBy;

    @Null(groups = {Add.class,Update.class},message = "不允许指定User name")
    @TableField(exist = false)
    private String createName;
}

3. 新增任务接口

@PostMapping("/job")
public CommonResult<String> addTimeJob(@RequestBody @Validated(Add.class) TimeJob timeJob) throws FloorFormatException, SchedulerException {
    int userId = StpUtil.getLoginIdAsInt();
    timeJob.setCreateBy(userId);
    timeJob.setCreateTime(new Date());
    timeJobService.saveJob(timeJob);
    return CommonResult.success("add success");
}

4. 保存任务核心逻辑

timeJobMapper.insert(timeJob);
JobDetail jobDetail = JobBuilder.newJob(PubRedisByteJob.class)
        .usingJobData("data",ByteUtils.byteArrayTo0xStringArray(data))
        .withIdentity(timeJob.getJobName()+":"+timeJob.getJobId().toString(),"PubRedisByteJob")
        .build();
CronTrigger  trigger = TriggerBuilder.newTrigger()
        .withIdentity(timeJob.getJobName()+":"+timeJob.getJobId().toString(),"PubRedisByteTrigger")
        .startAt(timeJob.getStartAt())
        .endAt(timeJob.getEndAt())
        .withSchedule(CronScheduleBuilder.cronSchedule(timeJob.getCronExpress())
                .inTimeZone(TimeZone.getTimeZone("Asia/Shanghai")))
        .build();
scheduler.scheduleJob(jobDetail,trigger);
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
log.info("next trigger time is {}",sdf.format(trigger.getNextFireTime()));

问题现象

传入参数:

{
    "jobName": "test2",
    "cronExpress": "*/5 * * * * ?",
    "floorList": "[1]",
    "startAt": "2023-09-07 12:50:55",
    "endAt": "2023-09-08 12:50:55",
    "enabled": true,
    "remoteControlType": 1,
    "callUp": true,
    "unitsId": 26
}

控制台输出显示:

  • 数据库插入的startAt变为2023-09-07 20:50:55.0(比传入时间晚8小时)
  • 日志输出的下次触发时间为2023-09-07 20:50:55

已尝试在application.yml和代码中指定Quartz时区为Asia/Shanghai,问题仍存在,求其他排查方案。

排查方案

  • 修复JSON反序列化时区:给实体类的@JsonFormat注解添加timezone = "Asia/Shanghai"参数,确保前端传入的本地时间字符串正确转换为上海时区的Date对象,避免默认UTC转换导致时间偏移。
  • 修正数据库时区配置:检查数据库服务器时区,同时在JDBC连接URL中指定时区(如MySQL添加&serverTimezone=Asia/Shanghai),保证存储和读取时间时的时区一致性。
  • 全局配置Jackson时区:在application.yml中配置Jackson默认使用上海时区:
    spring:
      jackson:
        time-zone: Asia/Shanghai
    
  • 强制JVM使用指定时区:启动应用时添加JVM参数-Duser.timezone=Asia/Shanghai,覆盖系统默认时区,避免new Date()、日期格式化等操作出现时区偏差。
  • 显式设置日志格式化时区:修改日志输出用的SimpleDateFormat,指定上海时区,确保日志显示的时间与实际触发时间一致:
    SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
    sdf.setTimeZone(TimeZone.getTimeZone("Asia/Shanghai"));
    log.info("next trigger time is {}", sdf.format(trigger.getNextFireTime()));
    
  • 完善Quartz全局时区配置:在application.yml中配置Quartz调度器的全局时区,确保所有Trigger默认使用该时区:
    spring:
      quartz:
        properties:
          org.quartz.scheduler.timezone: Asia/Shanghai
    
  • 验证Cron表达式逻辑:确认*/5 * * * * ?在上海时区下的触发规则,可通过本地代码或离线工具验证表达式的执行周期是否符合预期。

内容的提问来源于stack exchange,提问作者liang li

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.11 12:25:53