如何处理 cron 触发器的石英夏令时的用户时区?

how to handle user timezone for daylight savings in quartz for cron Triggers?

我的服务api接受石英作业的开始日期和执行作业的月份日期。 在内部,我将其转换为 cron 表达式并保存在 quartz 中。

例如,PST 中的用户今天(2017 年 11 月 3 日)提交了如下工作请求。

{
"start": "2017-11-03T18:00:00-07:00",
"dayOfMonth" : 15
}

在这里,用户想要安排一个作业,从 2017 年 11 月 3 日开始,在每个月的 15 日下午 6 点触发。所以第一天石英会火将是 2017-11-15。 这就是上述请求如何转换为 cron 表达式 0 0 18 15 * ? *,这是正确的。 这是 QRTZ_CRON_TRIGGERS table 的样子。

如您所见,time_zone_id 保存为 GMT-07:00,但一旦夏令时在 11 月 5 日开始,它必须是 GMT-08:00。否则我的石英作业会提前一小时启动。事实上,当我查询 nextFireTime 时,我确实得到了 1510794000000,这确实是 2017 年 11 月 15 日星期三 17:00:00(下午),时区 [=33] =] 洛杉矶(太平洋标准时间)

我们如何处理这个 time_zone_id 问题?

P.S:我使用的 cronTrigger 没有 CalendarIntervalTrigger 提供的 preserveHourOfDayAcrossDaylightSavings 概念。

不要使用偏移量来表示时区。相反,您可以要求用户传入时区,例如“America/Los_Angeles”。然后您可以使用 http://www.quartz-scheduler.org/api/2.2.1/org/quartz/CronScheduleBuilder.html#inTimeZone(java.util.TimeZone) 创建具有适当时区的触发器。

inTimeZone(TimeZone.getTimeZone("USER_ENTERED_VALUE")

最后,当您查看 QRTZ_CRON_TRIGGERS table 时,TIME_ZONE_ID 的值将是 America/Los_Angeles

您可以使用 java8/java8+ 中的 ZonedDateTime,这样您就不需要将给定时间显式转换为服务器特定时间,它还会处理夏令时时区:

protected static Trigger createCronTrigger(String triggerName, ZonedDateTime startTime, String cronExpression, int misFireInstruction, ZoneId timeZone) {
    Trigger trigger = TriggerBuilder.newTrigger()
        .withIdentity(triggerName)
        .startAt(Date.from(startTime.toInstant())).withSchedule(CronScheduleBuilder.cronSchedule(cronExpression).inTimeZone(TimeZone.getTimeZone(timeZone)).withMisfireHandlingInstructionDoNothing())
        .build();
    return trigger;
}