Skip to content
Advertisement

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

Just like the title , some task need to execute at 0 a.m. in each time zone, but @Scheduled annotation only support setting one zone, as shown below:

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

I only can do task at 0 a.m. in “Asia/Shanghai”, how can I execute this task at each time zone? Can the Spring scheduler support or any other tool help me?


Sorry, I may not have described the problem clearly.

Here is our task scenario. We have a task to execute at 0 a.m. on the first day of the month, but each country has different UTC, so how can I execute this task at each country 00 a.m.?

We have a country table, I can use country code to get capital UTC zoneId, just like this:

CN -> UTC+08:00

Advertisement

Answer

I’d just like to warn about the approach of mapping a country to an offset, mainly because:

  • one country can have more than 1 timezone, each one with a different offset. For example, USA has 4 (Pacific Time, Mountain Time, Central Time and Eastern Time – actually, it has more because of Alaska and Hawaii), Russia has more than 10, etc. Even the same state can have more than 1 timezone (such as Arizona)
  • the same country, even if it has only 1 timezone, can have Daylight Saving Time (DST), so it’ll have 2 different offsets throughout the year
  • in some countries, DST starts at midnight, which means that clocks jump 1 hour forward, from 23:59 directly to 1 AM – so during DST transition, midnight doesn’t exist in those timezones
  • DST rules can change all the time by governments, so you can’t assume that the offets will always be the same. One country without DST can decide to start using it (and vice-versa), or change the day when DST starts and/or ends, etc

So mapping a country to an offset is a very simplistic and error-prone approach. It might work for some cases, but it’s not guaranteed to work all the time, for all months, in all countries. Actually, it’ll fail in lots of cases.

The ideal way is to use IANA’s timezones names, in the format Continent/Region (such as America/New_York or Asia/Shanghai), and use an API that can handle DST issues and automatically calculate the correct offset for you. For countries with more than one timezone, you must arbitrarily choose one IANA name, based on some criteria (example: in USA, should I use America/New_York, America/Chicago, America/Los_Angeles? It’s up to you to decide).

I’d also recommend you to use Java 8 datetime API, if available (or threeten backport for lower versions). This API handles timezones and DST stuff for you, and you can get the exact UTC moment that corresponds to midnight in each timezone.

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
}

With this code, the variable utc will be equivalent to midnight (or to the first moment of the day, when DST starts at midnight for example) in each timezone, but already converted to UTC’s values. You can get the day, month, year, hour, minute, anything you want (in UTC) to configure your job to run exactly at that moment.

User contributions licensed under: CC BY-SA
8 People found this is helpful
Advertisement