bunkat / later

A javascript library for defining recurring schedules and calculating future (or past) occurrences for them. Includes support for using English phrases and Cron schedules. Works in Node and in the browser.
http://bunkat.github.io/later/
MIT License
2.42k stars 245 forks source link

later.date.localTime(), "wy" and nextRange doesn't handle DST changes #227

Closed Grayda closed 6 years ago

Grayda commented 6 years ago

I guess that this project isn't maintained anymore, so it's more of a question to anyone reading it, but:

My timezone is Australia/Melbourne and I'm passing in a schedule object (because the schedule is loaded via a user-editable JSON file). When I use UTC time, I can calculate ranges from January 1st through to December 31st, but if I switch to localTime() using the same schedule, it completely skips DST (the last instance occurs just before the start of April (when DST ends), and the next instance after that doesn't happen until October (when DST starts again)

Here's some sample code:

var later = require("later")

var sched = {
  "schedules": [{
      "h": [9], // 9th hour
      "m": [8], // 8th minute
      "dw": [2, 3, 4, 5, 6], // Every weekday
      "wy": [6, 8, 10, 12, 17, 19, 21, 23, 25, 30, 32, 34, 36, 38, 42, 44, 46, 48, 50] // On these weeks of the year
    },
    {
      "h": [9], // 9th hour
      "m": [8], // 8th minute
      "dw": [2, 3, 4, 6], // Every weekday EXCEPT Thursday
      "wy": [5, 7, 9, 11, 13, 16, 18, 20, 22, 24, 26, 29, 31, 33, 35, 37, 41, 43, 45, 47, 49] // On these weeks of the year
    },
    {
      "h": [9], // 9th hour
      "m": [28], // 28th minute
      "dw": [5], // On Thursday ONLY
      "wy": [5, 7, 9, 11, 13, 16, 18, 20, 22, 24, 26, 29, 31, 33, 35, 37, 41, 43, 45, 47, 49] // On these weeks of the year.
    }
  ],
  "exceptions": []
}

var start = new Date("2018-01-01")
var end = new Date("2019-01-01")
schedule = later.schedule(sched)

nextRange = schedule.nextRange(1000, start, end)
later.date.UTC()
console.log("UTC time:")
console.log()
console.dir(nextRange)
console.log()
console.log("Count: " + nextRange.length)
console.log()
later.date.localTime()
nextRange = schedule.nextRange(1000, start, end)
console.log("Local Time:")
console.log()
console.dir(nextRange)
console.log()
console.log("Count: " + nextRange.length)

This schedule should ring at 9:08am every weekday EXCEPT for a "Special Thursday", when it'll run at 9:28am.

(this schedule is part of a school bell system. The "Special Thursday" is determined by the school, so I have to set weeks manually)

Anyway, when I run the above code, I get the output listed in this pastebin

As you can see, the UTC times give 200 results, but the exact same schedule gives only 95, with the entirety of April through to September missing

If I remove the wy from the schedule, it calculates correctly, and I get the same amount of instances for both.

Anyone have any ideas what's going on here? I had a look at the weekOfYear function, but my brain isn't in the right place to work out what's going on.

Grayda commented 6 years ago

For anyone reading this who may have this issue (and other issues with timezones using the library), my workaround was to use moment.js, get the UTC offset (in my case, 600 minutes, or 10 hours), and add that to all my times.

A somewhat-acceptable workaround, but not having to do this would be wonderful.

EDIT: I'm now using https://github.com/grayda/later (which is fork of https://github.com/mansami/later) which lets me set a tz in my schedule. Everything then runs without me needing to do any addition or subtraction:

var sched = {
  "schedules": [{/* Schedule goes in here as normal */}],
  "exceptions": [],
  "tz": "Australia/Melbourne" /* Or America/New_York, or whatever */
}

I'll close this issue, but I wouldn't mind knowing why the original bug occurs. I suspect it's an issue with a rollover function, but I can't seem to work it out, so I found a workaround.