我如何使用 Spring 调度程序在 0 a.m 做一些工作。在每个时区?

How can I use Spring scheduler to do some work at 0 a.m. in each time zone?

正如标题,有些任务需要在0执行a.m。每个时区,但是@Scheduled注解只支持设置一个时区,如下图:

@Scheduled(cron = "0 0 0 * * ?", zone = "Asia/Shanghai")
public void scheduleTask() {...}

我只能在 0 做任务a.m。在“Asia/Shanghai”中,如何在每个时区执行此任务? Spring 调度程序支持或任何其他工具可以帮助我吗?


对不起,我可能没有描述清楚问题。

这是我们的任务场景。我们有一个任务要在 0 a.m 执行。在每个月的第一天,但​​是每个国家/地区都有不同的 UTC,所以我如何在每个国家/地区 00 a.m.?

执行此任务

我们有一个国家table,我可以使用国家代码获取首都UTC zoneId,就像这样:

CN -> UTC+08:00

我认为如果您创建多个触发器并将它们设置为您需要从所需时区列表中执行任务的时间,您会得到更好的服务。

特别是如果工作取决于触发任务的时区。

    List<Integer> timezoneDelays = Arrays.asList(1, 2, 5);
    for (double d : timezoneDelays){
        CronTrigger cronTrigger = new CronTrigger("0 0 " + d + " * * ?");
        taskScheduler.schedule(new RunnableTask("Cron Trigger"), cronTrigger);
    }

http://www.baeldung.com/spring-task-scheduler

此外,只需将任务设置为每小时或半小时触发一次也可能适合您。

我只想警告将国家/地区映射到偏移量的方法,主要是因为:

  • 一个国家可以有多个时区,每个时区都有不同的时差。例如,美国有 4 个(太平洋时间、山地时间、中部时间和东部时间 - 实际上,它有更多,因为阿拉斯加和夏威夷),Russia has more than 10, etc. Even the same state can have more than 1 timezone (such as Arizona)
  • 同一个国家,即使它只有 1 个时区,也可以使用夏令时 (DST),因此全年会有 2 个不同的时差
  • 在某些国家/地区,夏令时从午夜开始,这意味着时钟向前跳 1 小时,从 23:59 直接跳到凌晨 1 点 - 因此在夏令时转换期间,这些时区不存在午夜
  • DST 规则可能会因政府而随时更改,因此您不能假设偏移量始终相同。一个没有 DST 的国家可以决定开始使用它(和 vice-versa),或者更改 DST 开始的日期 and/or 结束等

因此,将国家/地区映射到偏移量是一种非常简单的 error-prone 方法。它可能适用于某些情况,但不能保证在所有国家/地区的所有时间、所有月份都有效。实际上,它在很多情况下都会失败。

理想的方式是使用IANA's timezones names,格式为Continent/Region(例如America/New_YorkAsia/Shanghai),并使用一个API可以处理 DST 问题并自动为您计算正确的偏移量。对于有多个时区的国家/地区,您必须根据一些标准(例如:在美国,我应该使用 America/New_YorkAmerica/ChicagoAmerica/Los_Angeles 任意选择一个 IANA 名称吗?这取决于你来决定)。

我还建议您对较低版本使用 Java 8 datetime API, if available (or threeten backport)。 API 为您处理时区和 DST 内容,您可以获得与每个时区午夜相对应的确切 UTC 时刻。

List<String> timezones = // timezones you want

// month that I care about (example: March 2018)
YearMonth yearMonth = YearMonth.of(2018, 3);
for (String zoneName : timezones) {
    ZoneId zone = ZoneId.of(zoneName);
    // get midnight at first day of month, in this timezone
    ZonedDateTime utc = yearMonth
        // 1st of month
        .atDay(1)
        // midnight at the timezone
        .atStartOfDay(zone)
        // convert to UTC
        .withZoneSameInstant(ZoneOffset.UTC);

    // get the fields you need to configure the job
    utc.getDayOfMonth();
    utc.getHour();
    ... etc
}

使用此代码,变量 utc 将相当于每个时区的午夜(或当天的第一时刻,例如 DST 从午夜开始),但已转换为 UTC 的值。您可以获得日、月、年、小时、分钟,任何您想要的(UTC 格式)配置您的工作 运行 恰好在那一刻。