node-cron icon indicating copy to clipboard operation
node-cron copied to clipboard

Daylight savings?

Open robinsongz opened this issue 5 years ago • 23 comments

Is node-cron updated for daylight savings? When I put timezone America/Los_Angeles, it is still going off pre-daylight savings times.

robinsongz avatar Mar 11 '19 03:03 robinsongz

I have the same issue here using {scheduled: true, timezone: "America/Los_Angeles"}

vwbusguy avatar Mar 11 '19 16:03 vwbusguy

The timezone offset is hard-coded in the supporting library, so it doesn't support DST: https://github.com/node-cron/tz-offset/blob/master/generated/offsets.json

vwbusguy avatar Mar 13 '19 17:03 vwbusguy

Glad I checked this before using this node package :)

thehappycoder avatar Mar 14 '19 04:03 thehappycoder

Same problem here with timezone 'Europe/Paris'

ndelanou avatar Apr 03 '19 16:04 ndelanou

It will be a problem anywhere that does DST the way this is implemented. The offset is hard-coded.

vwbusguy avatar Apr 08 '19 21:04 vwbusguy

Instead of a hard-coded list of offsets, there's the way Luxon handles it using the Intl.DateTimeFormat api (https://moment.github.io/luxon/docs/manual/zones.html#iana-support). However this would mean only supporting modern browsers / node 8+ without a polyfill (https://moment.github.io/luxon/docs/manual/matrix.html).

jaclarke avatar May 14 '19 11:05 jaclarke

The problem is here - https://github.com/node-cron/tz-offset/issues/8

vwbusguy avatar May 19 '19 22:05 vwbusguy

Servers normally run UTC anyway, so the offset is just helpful. Besides, you wouldn't want the mess of making sure tasks don't duplicate on the night DST starts or ends. But maybe some one else has some examples of how it could be helpful. I guess it depends on what people use it for and it could be made optional.

Arlen22 avatar May 23 '19 04:05 Arlen22

As I understand it, a js Date object doesn't really have a timezone/offset associated with it anyway, it just stores the date as a number of milliseconds since the 1st Jan 1970 epoch, so is effectively using UTC internally.
The problem is getting that timestamp into a second, minute, hour, day, month, year format for matching to the cron expression correctly in the users local time, accounting for their local offset and DST, since the getMinutes(), getHours(), etc. methods work in the systems local time, so for most servers with UTC system time they return the same as the getUTCMinutes(), getUTCHours() methods. This is where tz-offset library returning the correct offsets for DST is needed.

I think you're right that it does depend on what the cron expression is. For something like "0 * * * *" where the task just runs every hour the offset doesn't matter, but for tasks that run for example once a day at a specific time (eg "0 9 * * *"), you probably want it to run in the users local time, eg. for a user in the America/Los_Angeles timezone, 9:00 local time should be scheduled at 17:00 UTC (or 16:00 UTC during DST) on the server.

As for handling the weird missing or repeated hour when DST starts or ends, here's how its handled it in cronosjs, which handles it by adding and subtracting an hour from the date in UTC, and comparing how many hours are actually offset in local time to detect the missing/repeating hours and adjusting the date accordingly.

jaclarke avatar May 23 '19 12:05 jaclarke

It would definitely need to explicitly enabled because cron is used all over the server world for stuff.

Arlen22 avatar May 23 '19 13:05 Arlen22

Is moment-timezone.js too big for you as a dep ?

JulienStitelet avatar May 23 '19 17:05 JulienStitelet

I recently started using node-cron for a project and I have this as my schedule

cron.schedule('5 * 0-1,5-23 * * *', job, { timezone: "America/New_York" });

This should have the cron run on the 5th second of every minute from 5am to 1am in EST, which observes DST.

The issue is that during DST, because node-cron doesn't observe it or have a way to enable it, the job runs from 6am to 2am.

The easiest fix for me is to just have to run constantly and not during a specific range of hours, but this isn't preferred. The server runs UTC, but the time range required is tied to real world event that runs from 5am to 1am, so I think this is a specific example where DST support is needed.

korydondzila avatar Jun 12 '19 13:06 korydondzila

I personally changed to 'cron' package. Where timezone and DST works as expected.

JulienStitelet avatar Jun 12 '19 13:06 JulienStitelet

I personally changed to 'cron' package. Where timezone and DST works as expected.

I personally just moved from 'cron' due to this issue https://github.com/kelektiv/node-cron/issues/315 and ran into this issue. I'm just going to have to use the real cron. It seems like none of the cron modules for node work.

willtalmadge avatar Aug 28 '19 18:08 willtalmadge

In fairness, willtalmadge, it works about as well as most nodejs libraries do. It fit the specific use case for the person who made it and now the rest of us are trying to figure out how to workaround that implementation. Unless someone is willing to fork this code and maintain it, we're stuck with remembering to patch and release our code that depends on this every time daylight savings changes happen.

vwbusguy avatar Aug 28 '19 19:08 vwbusguy

I've submitted a pull request to the node-cron/tz-offset library that this one depends on, to handle DST changes correctly using the Intl.DateTimeFormat api. Though since it does depend on the Intl.DateTimeFormat api, it does mean it will only work on newer browser/node versions, so the maintainer may not want to merge it if they still require support for older browsers.

Alternatively, if you don't mind only having support for newer browsers/node versions, I've also published the cron library (cronosjs) that I wrote when I ran into the DST problem using this library (also because I needed to be able to get the next date the cron expression matched), which I'm happy to maintain. (As a bonus it handles some extended cron syntax and has configurable handling of the missing/repeated hours that come with DST starting/ending)

jaclarke avatar Aug 28 '19 20:08 jaclarke

In fairness, willtalmadge, it works about as well as most nodejs libraries do. It fit the specific use case for the person who made it and now the rest of us are trying to figure out how to workaround that implementation. Unless someone is willing to fork this code and maintain it, we're stuck with remembering to patch and release our code that depends on this every time daylight savings changes happen.

You are right, I was overly harsh out of frustration.

willtalmadge avatar Aug 28 '19 20:08 willtalmadge

Looks like this might have been resolved with https://github.com/node-cron/node-cron/pull/248 in version 3.0.0.

jgillick avatar Mar 16 '21 20:03 jgillick

I recently started using node-cron for a project and I have this as my schedule

cron.schedule('5 * 0-1,5-23 * * *', job, { timezone: "America/New_York" });

This should have the cron run on the 5th second of every minute from 5am to 1am in EST, which observes DST.

The issue is that during DST, because node-cron doesn't observe it or have a way to enable it, the job runs from 6am to 2am.

The easiest fix for me is to just have to run constantly and not during a specific range of hours, but this isn't preferred. The server runs UTC, but the time range required is tied to real world event that runs from 5am to 1am, so I think this is a specific example where DST support is needed.

I was stuck with a similar issue and found a simpler workaround using dayjs, which can account for daylight savings on its own. I feel using multiple schedules as shown below could be sufficient for most cases where DST support is needed.

Schedule the job thrice:

  1. cron.schedule('5 * 4 * * *', job, { timezone: "America/New_York" }); This runs at 5 am when DST is on, at 4 am when DST is off
  2. cron.schedule('5 * 0,5-23 * * *', job, { timezone: "America/New_York" }); This runs from 6am to 1 am when DST is on, from 5 am to 12 am when DST is off
  3. cron.schedule('5 * 1 * * *', job, { timezone: "America/New_York" }); This runs at 1 am when DST is off, at 2 am when DST is on

Inside the job function, use dayjs to run the job only when needed:

function job() {
    const curr_time = dayjs();
    const curr_time_ny = curr_time.tz("America/New_York");
    const curr_hour_ny = curr_time_ny.hour();
    if (curr_hour_ny <=1 || curr_hour_ny>=5) { /* execute the job */ }
}

Now the job runs from 5 am to 1 am throughout the year cuz the above if condition prevents execution at 4 am when DST is off (as in schedule 1 above) and at 2 am when DST is on (as in schedule 3 above).

likhit-vivek avatar Apr 11 '23 18:04 likhit-vivek

I upgraded node-schedule to version 2.0.0 and can confirm that timezones are now working correctly (tested the timezone 'America/New_York' on AWS ECS which had a default timezone set to UTC.

absorpheus avatar Apr 13 '23 04:04 absorpheus

It's not working for me - now that it's entered British Summer Time all the jobs I set are happening 1 hour later than expected. I'm using node-cron version 3.0.2

tanosaur avatar Apr 28 '23 08:04 tanosaur

Timezones and daylight savings work for me too. Running node-cron 3.0.2, on a server with UTC, a cronjob scheduled every day at 9am on Copenhagen timezone, runs correctly at 9am during copenhagen's summer time.

elgiano avatar Aug 29 '23 12:08 elgiano

However, there is a memory leak when using timezone: https://github.com/node-cron/node-cron/issues/358

elgiano avatar Aug 30 '23 10:08 elgiano