I have a scenario where I need to schedule a job which has to be execute daily at a specific time. When I schedule it with specific time as the start time for scheduler the quartz won't trigger the job at the set start time instead it would trigger at the next cycle i.e. after 24 hrs delay.Even on checking the the nextFireTime, we get a day's delay.
For E.g:
I need to schedule a job daily to run at 6 pm in the evening. And start it at 5 pm Today (27th March 2018).The job doesn't start and nextFireTime is 6pm 28th March 2018.
Code snippet :
Date startDateTime = new Date(scheduler.getStartDateTime());
Calendar calendar = GregorianCalendar.getInstance();
calendar.setTime(startDateTime);
int hours = calendar.get(Calendar.HOUR_OF_DAY);
int minutes = calendar.get(Calendar.MINUTE);
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(scheduleTriggerName, schdeuleGroupName).startAt(startDateTime).withSchedule(dailyAtHourAndMinute(hours, minutes)).build();
JobDetail jobDetail = this.getJobDetail(schdeuleJobName, schdeuleGroupName);
Scheduler configuration for spring
SchedulerFactoryBean schedulerFactoryBean= new SchedulerFactoryBean();
QuartzAutowireBeanFactory jobFactory = new QuartzAutowireBeanFactory();
jobFactory.setApplicationContext(applicationContext);
schedulerFactoryBean.setJobFactory(jobFactory);
schedulerFactoryBean.scheduleJob(jobDetail, trigger)// scheduling the job
Solution
One liner:
Cron only handles 1-minute resolutions
The starttime that which was passed to the startAt() function was a timestamp till milliseconds and cron does support till minutes.
so the simple solution was to use the calendar to set the minutes and seconds as zero.
calendar.setTime(startDateTime);
calendar.set(Calendar.SECOND, 0); // this was the solution
calendar.set(Calendar.MILLISECOND, 0); // this was the solution
int hours = calendar.get(Calendar.HOUR_OF_DAY);
int minutes = calendar.get(Calendar.MINUTE);
trigger = TriggerBuilder.newTrigger();
trigger.startAt(calendar.getTime()).withSchedule(dailyAtHourAndMinute(hours, minutes));
Detailed One:
Finally I got the reason for the behavior, while debugging I saw that it was setting the delay of 24 hours but when one would print the time it would be in hh:mm:00 format I mean the output would set the seconds parts as 00 as default , so the problem was the starttime that was passed as parameter was a timestamp through the UI consisting of seconds and milliseconds so after reading on the Cron format I came to know that it supported till minutes resolutions so wherever it use to get the timestamp the startAt(startDateTime) in the
CronTrigger trigger = TriggerBuilder.newTrigger().withIdentity(scheduleTriggerName, schdeuleGroupName).startAt(startDateTime).withSchedule(dailyAtHourAndMinute(hours, minutes)).build();
it would calculate the next run by skipping the seconds and milliseconds part.
Related
I am looking for a way to convert cron expression from one timezone to another one timezone.
For example, my web-client user is GMT+1200 and my server-side is GMT+0800, while user setting 02:10 to execute task every Tuesday and Thursday, the cron expression will be 0 10 2 3,5 * ?, and I have used code as below, it can get current fire time for user's timezone
CronExpression expr = new CronExpression("0 10 2 3,5 * ?");
System.out.println(expr.getNextValidTimeAfter(new Date()));
System.out.println(expr.getTimeZone());
System.out.println(expr.getExpressionSummary());
System.out.println("=======");
TimeZone tz = TimeZone.getTimeZone("GMT+1200");
expr.setTimeZone(tz);
System.out.println(expr.getNextValidTimeAfter(new Date()));
System.out.println(expr.getTimeZone());
System.out.println(expr.getExpressionSummary());
The getNextValidTimeAfter will print Mon Feb 02 22:10:00 CST 2015, which after setTimeZone(tz);, however the getExpreesionSummary or even getCronExpression() will still be 0 10 2 3,5 * ?, where I want to get string will be 0 10 22 2,4 * ? and then I can save into DB for next time fire and also another time-zone user to query setting (of course this will need to convert 0 10 22 2,4 * ? to this user's timezone)
Any help is appreciated
If you are willing to retain same cron expression, but give contextual calculations based on date timezone (so that user and serverside get next execution based on their timezones for same expression), you may use cron-utils, which provides such functionality. All next/previous execution calculations are contextual to timezone, since release 3.1.1.
They provide an example at the docs:
//Get date for last execution
DateTime now = DateTime.now();
ExecutionTime executionTime = ExecutionTime.forCron(parser.parse("* * * * * * *"));
DateTime lastExecution = executionTime.lastExecution(now));
//Get date for next execution
DateTime nextExecution = executionTime.nextExecution(now));
nextExecution value will be calculated for same timezone as reference date (now).
I already checked here but seems no solution given.
Here is my problem.
I have a cron job in my seam project which is written with jboss async. It runs at 3am everyday.
However last night, the application needed to reboot before that time. Past 3am when the application started.
The task set to run every 3am but did not run. In the code, the final expiration is set to 12/31/9999. Technically speaking, this will assume that it is already done.
Is there any chance to still run that job even past of scheduled given since it never run at that time? Like executing it right after the application is ready for production. If there are solutions, how would I make it?
Putting some flag to check if the job is done would be the least option.
Here is my sample code.
public void someMethodToSetJob() {
final String cronTabSchedule = "0 0 3 ? * MON-FRI *";
final Calendar cal = Calendar.getInstance();
cal.add(Calendar.MINUTE, 1);
cal.set(Calendar.SECOND, 0);
final Calendar expiry = Calendar.getInstance();
expiry.set(Calendar.MONTH, 11);
expiry.set(Calendar.DATE, 31);
expiry.set(Calendar.YEAR, 9999);
expiry.set(Calendar.SECOND, 0);
processBackgroundProcessCheck(cal.getTime(), cronTabSchedule, expiry.getTime());
}
#Asynchronous
#Transactional(TransactionPropagationType.REQUIRED)
public QuartzTriggerHandle processBackgroundProcessCheck(
#Expiration final Date when,
#IntervalCron final String cron,
#FinalExpiration final Date endDate) {
...
return null;
}
Any help would be highly appreciated. Thanks!
Use Spring batch tasklet to achieve that.. reason being, spring has provided ways to achieve last job run number/timing and picks next chunk from then on.
It would be far easier to achieve that way.
Some example you may find at this link.
https://www.mkyong.com/spring-batch/spring-batch-tasklet-example/
You might go for annotation based spring batch (If not comfortable with xml based)
It is possible by backdating the begin date which is #Expiration. Since I have in CRON schedule at 3AM, then let's say the application is deployed at 4AM. By setting the Date for #Expiration into something that it would catch the 3AM. it will run the process at the very moment. But the next schedule will be exactly 3AM.
public void someMethodToSetJob() {
final String cronTabSchedule = "0 0 3 ? * MON-FRI *";
final Calendar cal = Calendar.getInstance();
cal.add(Calendar.HOUR_OF_DAY, 3);
cal.set(Calendar.SECOND, 0);
final Calendar expiry = Calendar.getInstance();
expiry.set(Calendar.MONTH, 11);
expiry.set(Calendar.DATE, 31);
expiry.set(Calendar.YEAR, 9999);
expiry.set(Calendar.SECOND, 0);
processBackgroundProcessCheck(cal.getTime(), cronTabSchedule, expiry.getTime());
}
I have an alarm clock app that uses the following two methods:
private void fastForwardAlarmToNext24Hours() {
// Get Alarm Time (unix)
DataStorageController alarmTimeController = new DataStorageController(getApplicationContext());
epochAlarmTime = alarmTimeController.getAlarmTime();
// Get Current Time (unix)
long epochCurrentTime = System.currentTimeMillis() / 1000L;
// While Alarm Time in the past, fast forward another 24 hours
while (epochAlarmTime < epochCurrentTime) {
epochAlarmTime = epochAlarmTime + (24*60*60); // Add a days worth of seconds
}
}
private Long getNumberOfSecondsUntilAlarm() {
long epochCurrentTime = System.currentTimeMillis() / 1000L;
return epochAlarmTime - epochCurrentTime;
}
This works fine and I get these sorts of results back:
SUMMER
10-12 07:46:26.678: D/CJS Logging(776): epochCurrentTime: 1381560386 10/12/2013 7:46:26 AM +1
10-12 07:46:26.678: D/CJS Logging(776): epochAlarmTime: 1381560480 10/12/2013 7:48:00 AM +1
However, when I forward the datetime of my device to the winter (post clocks change), I get the following result and the alarm fires 1 hour off:
WINTER
11-12 07:47:10.441: D/CJS Logging(942): epochCurrentTime: 1384242430 11/12/2013 7:47:10 AM +0
11-12 07:47:10.441: D/CJS Logging(942): epochAlarmTime: 1384325280 11/13/2013 6:48:00 AM +0
The reason this is happening is because the saved alarm time remains the same epoch time but when the clocks change that moves the local time by 1 hour.
Does anyone have any suggestions on how to deal with this?
Cheers, Charlie
Instead of using UNIX time, you may want to use local time instead, using Calendar instances:
// Get Current Time
Calendar currentTime = GregorianCalendar.getInstance();
// While Alarm Time in the past, fast forward a day
while (alarmTime.before(currentTime)) {
alarmTime.add(Calendar.DAY_OF_YEAR, 1);
}
Can you register for
http://developer.android.com/reference/android/content/Intent.html#ACTION_TIME_CHANGED
and update your next firing time?
The long value returned by System.currentTimeMillis() is always the the number of seconds after January 1, 1970 UTC.
Depending on how you initialize the timestamp, different values can displayed ( based on your locale/ machine / time etc ).
To keep it consistent, deal with your times internally using GMT ( or a timezone that does not have daylight savings ).
I'm trying to use the Google Calendar API in my own Java class. Unfortunately, the endTime of the newly created event (vacation in this case) seems to decremented by 1 day.
Example: I create an event with startTime 2011-01-01 and endTime 2011-01-05 the event will show up in Google Calendar from 2011-01-01 to 2011-01-04.
This is what I got so far (just the date part, taken from the Google Calendar API Developer's Guide, changed to Date because I want All Day events):
...
CalendarEventEntry myEntry = new CalendarEventEntry();
DateTime startTime = DateTime.parseDate("2011-01-01");
DateTime endTime = DateTime.parseDate("2011-01-05");
When eventTimes = new When();
eventTimes.setStartTime(startTime);
eventTimes.setEndTime(endTime);
myEntry.addTime(eventTimes);
Reminder reminder = new Reminder();
reminder.setMethod(Method.NONE);
myEntry.getReminder().add(reminder);
CalendarEventEntry insertedEntry = myService.insert(postUrl, myEntry);
...
Could this be somehow related to timezone issues? (I am from Germany)
When you don't provide DateTime.parseDate() with a time it will default to midnight. An event starting at midnight on the 1st and ending midnight on the 5th will display in the interface as running as full-day events from the 1st to the 4th. The time period doesn't include any time on the 5th, so it won't be displayed as being on the 5th.
You either need to set the end time as 2011-01-05 23:59, or add a day to the end date.
findCalendarStart: time into Calendar: 1260575897
findCalendarStart: set hour : 13
findCalendarStart: after hour : 1249775897
findCalendarStart: after hour string: Thu Jan 15 11:09:35 UTC 1970
findCalendarStart: set minutes : 13
findCalendarStart: after minutes: 1250015897
findCalendarStart: what calendar returns: 1250015897
I place a Date (initialized by passing long from a millisecond from today) in a Calendar. Calendar is correctly initialized. In the first calculation, I change the hour of day to 13. At this point, startCalTime.set(Calendar.HOUR_OF_DAY, ((new Integer(m.group(1)).intValue())*2)-1 );
I am passing the right hour of day values and minutes because Im seeing them in the logger. What could possibly be causing calendar to come up with such strange dates after I only change the hour of day from todays Date object?
More code:
Calendar startCalTime = Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"));
Date d = new Date(creationTime);
startCalTime.setTime(d);
startCalTime.getTimeInMillis();
..regex..
if(m.find()){
//SET HOUR OF DAY
_logger.warning("set hour 1 : " + new Integer((new Integer(m.group(1)).intValue())-1)); startCalTime.set(Calendar.HOUR_OF_DAY, new Integer(m.group(1)).intValue()-1 );
_logger.warning("after hour 1: " + new Long(startCalTime.getTime().getTime()));
_logger.warning("after hour 1 string: " + startCalTime.getTime().toString());
//SET MINUTE
_logger.warning("set minutes 1 : " + new Integer(m.group(2).toString()));
startCalTime.set(Calendar.MINUTE, new Integer(m.group(2)).intValue());
_logger.warning("after minutes 1: " + new Long(startCalTime.getTime().getTime()));}
Thanks,
culov
Let's see how you initialize your date. I suspect that instead of milliseconds, you are passing it seconds since epoch start - this (seconds, not milliseconds) is how regular Unix timestamps are defined. Java uses milliseconds for better granularity.
Calendar startCalTime = Calendar.getInstance(TimeZone.getTimeZone("America/Los_Angeles"));
Date d = new Date(creationTime);
What happens there? startCalTime and creationTime don't seem to be connected, I'd assume they should be?
Also for very slightly better performance/memory footprint, avoid new Integer/Long as much as possible and use Long/Integer.valueOf() instead.
Those times in your Calendar don't look right. If those are supposed to be times in milliseconds, then 126..... represents a time of only 350 hours, which looks to be off by almost 40 years.
The reason seems to be that your initialization is not really setting your calendar to today's date. The initial date seems to be just a few hours past the epoch.
Please post some more code and we can fix it for you.