Agenda: New feature request - Consider TimeZone

Created on 26 Jun 2014  路  17Comments  路  Source: agenda/agenda

First of all I would like to thank for creating such a beautiful flawless scheduling functionality.
I would like to see new functionality for running scheduler based on different timezone for each job. For example, one job should run as per EST timezone and other one should be based on PST timezone. This way, I will be expose this module as HTTP service and deploy any where across the world and still make usable for any timezone users.

Since cron module, a module agenda internally uses, supports timezone, in the computeNextRunAt function of job.js should pass timezone information from job request to CronTime constructor. This way, nextRunDate is computed as per target timezone that is mentioned for job.

It would be great if this request is considered or I am equally existed to implement this feature if I can contribute.

Thanks,
Suresh

Most helpful comment

:+1:

All 17 comments

:+1:

So are you wanting the cron syntax to support timezones?

.schedule() accepts a Date object for scheduling, which supports time zones.

The one problem I can see is the .every() method. Currently it accepts a human-readable string, cron string, or number (which corresponds between the delta between Epoch and now).

Looking at the source, we use node-cron's CronTime API to construct the interval, which seems to accept a date object as well.

I do need to test this with an .every() call to make sure I'm not incorrect here, but I think this might be the case.

@droppedonjapan oh... I didn't notice schedule accepted a Date object, then for my use case I'm fine

I will say that CronTime does accept an optional time zone, so maybe adding a time zone would be doable.

Just to follow up here, it does appear as though .every() does not properly handle date objects (as of now).

agenda.define('every date interval job', function(job, done) {
  console.log('ran');
  done();
});

agenda.every( new Date(1000), 'every date interval job' );
// I'd expect this job to run every 1 second,
// however it just logs a quiet error of
// "WARNING: Date in past. Will never be fired."

Edit:

Added a simple failing test if you want to look at it.

new Date(1000) is creating a date of 1000ms past EPOCH (so Wed Dec 31 1969 19:00:01 GMT-5). If you want to use a number for the interval, you want:

agenda.every(1000, 'every second job');

To the timezone suport - I have this in my script:
agenda.every('15 0 * * *', 'proces1')
agenda.every('25 0 * * *', 'proces2')
agenda.start()

What I wanted to achieve was to run this at UTC midnight (15 min/25 mins after). But it runs at local time midnight. (If you are at +0200, then it will run at 22:15Z, 22:25Z)

I don't see other way than setting the values in script like
agenda.every('15 2 * * *', 'proces1')
agenda.every('25 2 * * *', 'proces2')

But the script is used in different places, in different timezones, so I have to change it in all instances.
(probably I will use external configuration)

But isn't there an easier way which could take 0 as UTC in the cron string, somehow? Thank you

Probably it has something to do with the system settings. I use RHEL.
at one system, which 'date' cmd reports:
Fri Sep 12 19:40:08 CEST 2014
agenda.every('15 0 * * *' ....) results in 22:15Z

at other system, which 'date' cmd reports:
Fri Sep 12 17:40:17 UTC 2014
agenda.every('15 0 * * *' ...) results in 0:15Z
(which is what I want)
(surprisingly the system itself is located at yet different timezone)

any idea?

@miira Well the bad news is that the cron integration is handled by node-cron and doesn't support timezones inside the cron string. If you'd want that sort of integration out-of-the-box with Agenda, you'd need to open an issue in that project asking for that type of support.

Good news is that it's still possible to do without those changes, you just have to be smart on how you create the cron string.

Personally I would do something like this:

var hourToRun = new Date( Date.UTC(0, 1, 0, 0) ).getHours();
agenda.every( hourToRun + ' 0 * * *', 'Some Job You Want Ran');

Create a date object based on UTC time at 12:00am, then get the hours relative to your timezone. You can use that to construct the cron string, which should then run at the correct time.

That's nice workaround. Thank you!

However, one more caveat - summer/winter time shift. I have to use current Date to get it properly. And after time change, I guess, because it's saved like UTC time in agenda db, it will be run with one hour difference to our relative time. (UTC remains the same, but our midnight is shifted about one hour) Therefore the final solution is to set it to the biggest of the two times (summer/winter), to be run after 'our' midnight each time (either in summer or winter). (Otherwise, there won't be all the data for processing) ?

At that point you'd have to restart your servers to reload the every() call, which should update to the correct UTC time (as Agenda will not recreate job entries for every() jobs).

The issue is that UTC isn't fully supported at the moment. The library uses Date values, which are inherently affected by timezone shifts.

By the way, I just sent a patch to node-cron that make it use moment-timezone so now it is compatible with Windows and Node 0.11.x!

So now that node-cron accepts a timezone parameter, could repeatEvery be changed to accept a timezone parameter?

I think it would make sense (and would be simple) to allow setting the timezone of the Agenda instance globally (which you probably wanna set to the timezone of the server defining the jobs).
For instance:
var agenda = new Agenda({timezone: 'Europe/Paris'});
Store/default it in the Agenda constructor:
this._timezone = config.timezone || 'Europe/London';
And passing the value to the CronTime instantiation:
var cronTime = new CronTime(interval, this.agenda._timezone);

I'd prefer setting the timezone for each job. We have some clients on the East Coast, and some on the West Coast. Say the West Coast client wants their jobs run at 12 noon, 3pm, 6pm, 5 minutes into each hour:
5 12,3,6 * * *
and the west coast client wants their jobs run at 12 noon, 3pm, 6pm, 9pm:
5 12,3,6,9 * * *

In order to implement this currently, we're using logic similar to what @droppedoncaprica described, which works great except for the shifts to/away from Daylight Saving Time, as @miira aluded to.
Let's say our server is an AWS box, and local time is always in UTC. So during DST, our jobs had repeatIntervals (in UTC hours) of:
5 19, 22, 1 * * *
and:
5 16, 19, 22 * * *

so once DST ended, we had to change the jobs to:
5 20, 23, 2 * * *
and:
5 17, 20, 23 * * *

and we also needed to set "nextRunAt", unless we modified the repeatInterval before the last job before the time shift.

If we could set the timezone of each job, say, "America/Los_Angeles" for the first and "America/New_York" for the 2nd, then the repeatIntervals could be "5 12, 15, 18 * * *" and "5 12, 15, 18, 21 * * *", and we'd never have to update them for DST time shifts.

+1 for this overall feature and for timezones specific to each job.

I've found it's very common in integration use cases to need to schedule tasks around an external entity's natural schedule and time zone. It's best for human readability of the schedule, and for nailing the timing around the "daylight savings time" shifts, to define the job's schedule in the local timezone.

A global default timezone for Agenda would be useful too, as long as you could override this per job.

Seems like this can be closed, see https://github.com/agenda/agenda/pull/267

Was this page helpful?
0 / 5 - 0 ratings