Java Quartz-Scheduler 跨时区

我的服务器在欧洲/罗马时区运行-这是服务器上的默认 tz-,我需要根据用户的时区安排作业,所以,如果用户,生活在太平洋/檀香山时区,为他所在的地球地区安排了一个每天下午 22:00 触发的 CronTrigger 我找到了这个解决方案:

My server runs on Europe/Rome timezone -and this one is the default tz on the server-, I need to schedule jobs according the user's timezone, so, if a user, living on Pacific/Honolulu timezone, schedules a CronTrigger that fires every day at 22:00pm for his region of the Earth I have found this solution:

CronTrigger trigger = newTrigger()
  .withIdentity("name", "group")
  .withSchedule(
    cronSchedule("0 0 22 ? * *").inTimeZone(TimeZone.getTimeZone("Pacific/Honolulu"))
  )
  .startNow()
  .build();

在我的服务器上这项工作从我的"第二天早上 9:00 开始.

除了时区更新之外,还有一些特殊问题需要考虑(即时区更新工具) ?

There are particular problems to be taken into consideration besides the fact to keep updated the timezone (i.e. Timezone Updater Tool) ?

如果我想为上一份工作定义 .startAt() 和 .endAt(),这样的日期可以吗?使用此过程可能的夏令时是安全的吗?

If I want to define the .startAt() and .endAt() for the previous job, is this kind of date ok? A possible daylight saving time is safe using this procedure?

Calendar calTZStarts = new GregorianCalendar(TimeZone.getTimeZone("Pacific/Honolulu"));
calTZStarts.set(2013, Calendar.JANUARY, 10);

Calendar calTZEnds = new GregorianCalendar(TimeZone.getTimeZone("Pacific/Honolulu"));
calTZEnds.set(2013, Calendar.JANUARY, 30);

Calendar calStarts = Calendar.getInstance();
calStarts.set(Calendar.YEAR, calTZStarts.get(Calendar.YEAR));
calStarts.set(Calendar.MONTH, calTZStarts.get(Calendar.MONTH));
calStarts.set(Calendar.DAY_OF_MONTH, calTZStarts.get(Calendar.DAY_OF_MONTH));
calStarts.set(Calendar.HOUR_OF_DAY, calTZStarts.get(Calendar.HOUR_OF_DAY));
calStarts.set(Calendar.MINUTE, calTZStarts.get(Calendar.MINUTE));
calStarts.set(Calendar.SECOND, calTZStarts.get(Calendar.SECOND));
calStarts.set(Calendar.MILLISECOND, calTZStarts.get(Calendar.MILLISECOND));

Calendar calEnds = Calendar.getInstance();
calEnds.set(Calendar.YEAR, calTZEnds.get(Calendar.YEAR));
calEnds.set(Calendar.MONTH, calTZEnds.get(Calendar.MONTH));
calEnds.set(Calendar.DAY_OF_MONTH, calTZEnds.get(Calendar.DAY_OF_MONTH));
calEnds.set(Calendar.HOUR_OF_DAY, calTZEnds.get(Calendar.HOUR_OF_DAY));
calEnds.set(Calendar.MINUTE, calTZEnds.get(Calendar.MINUTE));
calEnds.set(Calendar.SECOND, calTZEnds.get(Calendar.SECOND));
calEnds.set(Calendar.MILLISECOND, calTZEnds.get(Calendar.MILLISECOND));

CronTrigger trigger = newTrigger()
  .withIdentity("name", "group")
  .withSchedule(
    cronSchedule("0 0 22 ? * *").inTimeZone(TimeZone.getTimeZone("Pacific/Honolulu"))
  )
  .startAt(calStarts.getTime())
  .endAt(calEnds.getTime())
  .build();

或者我必须简单地设置开始和结束使用:

or I have to set simply start and end using:

Calendar calTZStarts = new GregorianCalendar();
calTZStarts.set(2013, Calendar.JANUARY, 10, 0, 0, 0);

Calendar calTZEnds = new GregorianCalendar();
calTZEnds.set(2013, Calendar.JANUARY, 30, 0, 0, 0);

CronTrigger trigger = newTrigger()
  .withIdentity("name", "group")
  .withSchedule(
    cronSchedule("0 0 22 ? * *").inTimeZone(TimeZone.getTimeZone("Pacific/Honolulu"))
  )
  .startAt(calTZStarts.getTime())
  .endAt(calTZEnds.getTime())
  .build();

那么工作在太平洋/檀香山"定义的日子里正确开始/结束?

Then the job starts/ends correctly in "Pacific/Honolulu" defined days?

提前感谢您的每一个建议

Thanks in advance for every suggestion

推荐答案

我想我找到了解决方案,经过测试,它可以工作,直到证明不是这样;)

I think I found the solution, tested and it works until proved otherwise ;)

恢复我的服务器在特定时区(即欧洲/罗马)运行

Recup My server runs on a specific timezone (i.e. Europe/Rome)

如果 Pacific/Honolulu TZ 上的用户想要安排一个从 2013 年 1 月 27 日星期日下午 3:00 开始到 2013 年 1 月 31 日星期四晚上 9:00 结束的作业,从 2 点开始每天每五分钟触发一次:00PM to 10:55PM (0 0/5 14-22 * * ?) 正确方式如下:

If a user on Pacific/Honolulu TZ want to schedule a job that starts on Sun, 27 Jan 2013 at 3:00PM end ends on Thu, 31 Jan 2013 at 9:00PM that fire every day every five minutes starting from 2:00PM to 10:55PM (0 0/5 14-22 * * ?) the correct way is the following:

  • 在 CronScheduleBuilder 的 inTimeZone 方法中设置用户时区
  • 通过从太平洋/檀香山转换为欧洲/罗马来适应服务器时间 startAt 和 endAt 日期

示例代码:

// Begin User Input
String userDefinedTZ = "Pacific/Honolulu"; // +11

int userStartYear = 2013;
int userStartMonth = Calendar.JANUARY;
int UserStartDayOfMonth = 27;
int userStartHour = 15;
int userStartMinute = 0;
int userStartSecond = 0;

int userEndYear = 2013;
int userEndMonth = Calendar.JANUARY;
int UserEndDayOfMonth = 31;
int userEndHour = 21;
int userEndMinute = 0;
int userEndSecond = 0;
// End User Input


Calendar userStartDefinedTime = Calendar.getInstance();
// set start schedule by user input
userStartDefinedTime.set(userStartYear, userStartMonth, UserStartDayOfMonth, userStartHour, userStartMinute, userStartSecond);

Calendar userEndDefinedTime = Calendar.getInstance();
// set end schedule by user input
userEndDefinedTime.set(userEndYear, userEndMonth, UserEndDayOfMonth, userEndHour, userEndMinute, userEndSecond);


CronTrigger trigger = newTrigger()
  .withIdentity("name", "group")
  .withSchedule(
    // define timezone for the CronScheduleBuilder
    cronSchedule("0 0/5 14-22 * * ?").inTimeZone(TimeZone.getTimeZone("Pacific/Honolulu"))
  )
  // adapt user start date to server timezone
  .startAt( convertDateToServerTimeZone(userStartDefinedTime.getTime(), userDefinedTZ) )
  // adapt user end date to server timezone
  .endAt( convertDateToServerTimeZone(userEndDefinedTime.getTime(), userDefinedTZ) )
  .build();

根据 tz 转换日期的实用程序:

Utility to convert dates based on tz:

public Calendar convertDateToServerTimeZone(Date dateTime, String timeZone) {
    Calendar userDefinedTime = Calendar.getInstance();
    userDefinedTime.setTime(dateTime);
    if(!TimeZone.getDefault().getID().equalsIgnoreCase(timeZone)) {
    System.out.println        ("original defined time: " + userDefinedTime.getTime().toString() + " on tz:" + timeZone);
    Calendar quartzStartDate = new GregorianCalendar(TimeZone.getTimeZone(timeZone));
    quartzStartDate.set(Calendar.YEAR, userDefinedTime.get(Calendar.YEAR));
    quartzStartDate.set(Calendar.MONTH, userDefinedTime.get(Calendar.MONTH));
    quartzStartDate.set(Calendar.DAY_OF_MONTH, userDefinedTime.get(Calendar.DAY_OF_MONTH));
    quartzStartDate.set(Calendar.HOUR_OF_DAY, userDefinedTime.get(Calendar.HOUR_OF_DAY));
    quartzStartDate.set(Calendar.MINUTE, userDefinedTime.get(Calendar.MINUTE));
    quartzStartDate.set(Calendar.SECOND, userDefinedTime.get(Calendar.SECOND));
    quartzStartDate.set(Calendar.MILLISECOND, userDefinedTime.get(Calendar.MILLISECOND));
    System.out.println("adapted time for " + TimeZone.getDefault().getID() + ": " + quartzStartDate.getTime().toString());
    return quartzStartDate;
    } else {
    return userDefinedTime;
    }
}

== 更新开始 2012-01-24 ==

Quartz Based Utility 使用 DateBuilder 转换基于 tz 的日期:

== BEGIN OF UPDATE 2012-01-24 ==

Quartz Based Utility to convert dates based on tz using DateBuilder:

public Calendar convertDateToServerTimeZone(Date dateTime, String timeZone) {
    Calendar userDefinedTime = Calendar.getInstance();
    userDefinedTime.setTime(dateTime);
    if(!TimeZone.getDefault().getID().equalsIgnoreCase(timeZone)) {
      System.out.println("original defined time: " + userDefinedTime.getTime().toString() + " on tz:" + timeZone);

      Date translatedTime = DateBuilder.translateTime(userDefinedTime.getTime(), TimeZone.getDefault(), TimeZone.getTimeZone(timeZone));

      Calendar quartzStartDate = new GregorianCalendar();
      quartzStartDate.setTime(translatedTime);
      System.out.println("adapted time for " + TimeZone.getDefault().getID() + ": " + quartzStartDate.getTime().toString());
      return quartzStartDate;
    } else {
      return userDefinedTime;
    }
}

== 2012-01-24 更新结束 ==

所以在我的 Europe/Rome Quartz 服务器上,这项工作计划从 2013 年 1 月 28 日星期一 02:00:00 CET 到 2013 年 2 月 1 日星期五 08:00:00 CET 开始,每天每五分钟触发一次上午 01:00 至晚上 8:55

在为开始和结束时间构建日期时,还要在实例化日期之前指定时区(在 java.util.Calendar 或日期格式字符串或 org.quartz.DateBuilder 上).然后,quartz 将日期存储为自 1970 年 1 月 1 日以来在该特定时区的 UTC 中的毫秒数 - 因此,当服务器的时区更改时,触发器不会受到影响.

When building your Dates for start and end times also specify the timezone (on the java.util.Calendar, or date format string, or org.quartz.DateBuilder) before instantiating the Date. Then quartz stores the date as milliseconds since January 1, 1970 in UTC in that particular timezone - and hence when the server's timezone changes, the trigger is not affected.

相关文章