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

Posted

技术标签:

【中文标题】如何为 cron 触发器处理石英夏令时的用户时区?【英文标题】:how to handle user timezone for daylight savings in quartz for cron Triggers? 【发布时间】:2018-04-16 21:08:23 【问题描述】:

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

例如,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 表的样子。

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

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

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

【问题讨论】:

【参考方案1】:

不要使用偏移量来表示时区。相反,您可以要求用户输入时区,例如“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 表时,TIME_ZONE_ID 的值将是 America/Los_Angeles

【讨论】:

【参考方案2】:

您可以使用 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;

【讨论】:

以上是关于如何为 cron 触发器处理石英夏令时的用户时区?的主要内容,如果未能解决你的问题,请参考以下文章

PHP 处理夏令时的时区

日期、时间、DSL 和时区信息处理

EBS Worker 和 SQS 的 Cron 作业和夏令时

如何为 pytz 处理 POSIX 时区信息(如 CST)?

如何为每个线程设置时区? [关闭]

如何在android中处理时区和夏令时?