Как справиться с переходом на летнее время с помощью Quartz Cron Trigger - PullRequest
7 голосов
/ 07 марта 2011

У меня есть кварцевый хронограф, который выглядит так:

<bean id="batchProcessCronTrigger" class="org.springframework.scheduling.quartz.CronTriggerBean">
    <property name="jobDetail" ref="batchProcessJobDetail" />
    <property name="cronExpression" value="0 30 2 * * ?" />
</bean>

Как мне решить эту проблему, если у меня есть несколько конфигураций, которые происходят в течение 2-3 часов?Есть ли принятая передовая практика?

Соответствующая ссылка: http://www.quartz -scheduler.org / docs / faq.html # FAQ-daylightSavings

В основном это говорит "Сделкас этим."Но мой вопрос, как!

Ответы [ 3 ]

5 голосов
/ 10 марта 2011

Я решил это с помощью отдельного триггера, который срабатывает (на час раньше) в дату начала летнего времени для конфигураций, которые происходят между 2 и 3 часами утра.

Кажется бледным, но это работает ...

3 голосов
/ 24 марта 2014

Мы используем следующее решение. Для этого вам также понадобится библиотека времени joda.

public class MyCronExpression extends CronExpression
{
    CronExpression _orgCronExpression;

    public MyCronExpression(String cronExpression) throws ParseException
    {
        super(cronExpression);
        setTimeZone(TimeZone.getTimeZone("UTC"));
        _orgCronExpression = new CronExpression(cronExpression);
    }

    @Override
    public Date getTimeAfter(Date date)
    {

        Date date1 = super.getTimeAfter(new Date(date.getTime()-date.getTimezoneOffset()*60*1000));
        if (TimeZone.getDefault().inDaylightTime( date1 ) && !TimeZone.getDefault().inDaylightTime( date ))
        {
            DateTimeZone dtz = DateTimeZone.getDefault();
            Date dstEnd = new Date(dtz.nextTransition(date.getTime()));
            int dstEndHour = dstEnd.getHours();
            int dstDuration = (dtz.getOffset(date1.getTime()) - dtz.getStandardOffset(date1.getTime()))/(60*60*1000);
            int hour = date1.getHours()+date1.getTimezoneOffset()/60;
            if (hour < dstEndHour && hour >= dstEndHour-dstDuration)
                return dstEnd;
            else
                return _orgCronExpression.getTimeAfter(date);
        }
        else
            return _orgCronExpression.getTimeAfter(date);
    }

}

Класс используется следующим образом:

        CronTriggerImpl trigger = new CronTriggerImpl();
    trigger.setCronExpression(new MyCronExpression("0 15 2 * * ?"));

Вот некоторые примеры времени запуска:

Tue Mar 25 02:15:00 CET 2014
Wed Mar 26 02:15:00 CET 2014
Thu Mar 27 02:15:00 CET 2014
Fri Mar 28 02:15:00 CET 2014
Sat Mar 29 02:15:00 CET 2014
**Sun Mar 30 03:00:00 CEST 2014**
Mon Mar 31 02:15:00 CEST 2014
Tue Apr 01 02:15:00 CEST 2014
Wed Apr 02 02:15:00 CEST 2014

Пожалуйста, напишите, если вы обнаружите какие-либо ошибки / проблемы с этим решением.

2 голосов
/ 23 мая 2014

Я взял очень интересный ответ Рона и улучшил метод getTimeAfter, чтобы настроить его на время работы сервера GMT и возможные различия при планировании выражений cron «Один раз в год».

@Override
  public Date getTimeAfter(Date date) {

    Date nextDate = super.getTimeAfter(date);
    if(nextDate == null){
      return null;
    }
    DateTime date1 = new DateTime(nextDate);

    if (getTimeZone().inDaylightTime(date1.toDate()) && !getTimeZone().inDaylightTime(date)) {

      DateTimeZone dtz = DateTimeZone.forTimeZone(getTimeZone());
      DateTime dstEndDateTime = new DateTime(new Date(dtz.nextTransition(date.getTime())));

      int dstEndHour = dstEndDateTime.getHourOfDay();
      int dstDuration = (dtz.getOffset(date1.getMillis()) - dtz.getStandardOffset(date1.getMillis())) / (60 * 60 * 1000);
      int hour = date1.getHourOfDay();

      // Verifies if the scheduled hour is within a phantom hour (dissapears upon DST change)
      if (hour < dstEndHour && hour >= dstEndHour-dstDuration){       
        // Verify if the date is  a skip, otherwise it is a date in the future (like threads that run once a year)
        if(dstEndDateTime.getDayOfYear() == date1.minusDays(1).getDayOfYear()){
          return dstEndDateTime.toDate();
        }else{
          return nextDate;
        }

      }else{
        return nextDate;
      }

    } else{
      return nextDate;
    }

  }

Обратите внимание на мой серверработает в режиме GMT, поэтому я не использую некоторые из преобразований смещения, присутствующих в ответе Рона.

Также я обнаружил ошибку Quartz, в которой, если вы используете следующую конфигурацию, она потерпит неудачу, потому что она не способнаправильной обработки выражения cron:

SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy HH:mm:ss");

String cron = "0 15 2 8 3 ? 2015";
FailsafeCronExpression cronExpression = new FailsafeCronExpression(cron);
cronExpression.setTimeZone(DateTimeZone.forID("America/Vancouver"));

DateTime nextDate = new DateTime(cronExpression.getTimeAfter(sdf.parse("12/11/2014 10:15:00")));

Похоже, это действительно происходит из-за того, что изменение летнего времени происходит в Ванкувере 9 марта в 2 часа ночи и кажется, что внутренняя реализация Quartz метода super.getTimeAfter (date) всегда будетотправить ноль.

Я надеюсь, что эта информация полезна.

...