关于java:如何在每个时区使用spring scheduler在0 am.m做一些工作?

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

就像标题一样,某些任务需要在每个时区的0 am执行,但@scheduler注释只支持设置一个区域,如下所示:

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

我只能在亚洲/上海的上午0点做任务,如何在每个时区执行此任务。 Spring调度程序支持? 或任何其他工具可以帮助我?

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

这是我们的任务场景:我们有一个任务是在每个月的第一天早上0点执行,但是每个国家/地区都有不同的UTC,那么如何在每个国家/地区执行此任务0 a.m?

我们有一个国家/地区表,我可以使用国家/地区代码获取资本utc zoneid,就像:CN - > UTC + 08:00。


我只想警告将国家映射到抵消的方法,主要是因为:

  • 一个国家可以有超过1个时区,每个时区有不同的偏移量。例如,美国有4个(太平洋时间,山区时间,中部时间和东部时间 - 实际上,它有更多因为阿拉斯加和夏威夷),俄罗斯有超过10个等等。即使是同一个州也可以拥有超过1个时区(如亚利桑那州)
  • 同一个国家,即使它只有1个时区,也可以有夏令时(DST),因此它全年有2种不同的偏移量
  • 在一些国家,DST从午夜开始,这意味着时钟向前跳1小时,从23:59直接跳到凌晨1点 - 所以在DST过渡期间,这些时区中不存在午夜
  • DST规则可以随时由政府更改,因此您不能认为这些规则总是相同的。一个没有DST的国家可以决定开始使用它(反之亦然),或者更改DST开始和/或结束的日期等

因此,将国家/地区映射到偏移量是一种非常简单且容易出错的方法。它可能适用于某些情况,但不能保证在所有国家的所有月份都能一直工作。实际上,它会在很多情况下失败。

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

我还建议您使用Java 8 datetime API(如果可用)(或者对于较低版本,使用三个backport)。此API为您处理时区和DST内容,您可以获得与每个时区中的午夜相对应的确切UTC时刻。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
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为单位),以便将作业配置为在该时刻正好运行。


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

特别是如果工作依赖于任务被触发的时区。

1
2
3
4
5
    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

此外,简单地将任务设置为每小时或半小时触发也可能对您有用。