Node.js Random Scheduling - javascript

I have a function that I want to call within a random range in Node. So, for instance, I would like it to be executed once between 2 and 12 hours, then again once between 2 and 12 hours, and so on. However, this interval should not be fixed, so I need that interval to be recalculated at the end of every call.
If possible, I'd also like to limit the days of the week that this can execute, so not on Saturdays and Sundays as an example.
I've tried looking into later.js, which does the second part very well, but I can't seem to find a way to incrementally randomize the interval. Does anyone know how to accomplish this?

I must admit to not being an later.js expert but isn't custom time periods what you are after?

At the end of the cycle,
Use new Date(y, m, d, h) constructor to construct the first applicable time and last applicable time (use now = new Date(); y = now.getYear(); ... to extract the components you will need
Convert them to timestamps
Generate random integer between the two timestamps
setTimeout(process, new Date(randomTimestamp - new Date())

Related

Add a duration to a repeating event's start time so that it's end is always the same time (i.e 2pm to 4 pm)

I have a bunch of rrules (implemented in rrule.js) that gives me an array of event start times (see the demo). rrule.js doesn't actually provide the concept of an event duration or endtime... So it can tell me the precise date when the millionth occurrence of a repeating event will start but not when it will end. Turns out I actually want to know when an event ends so I'll have to get creative. As far as I see it I've got two options
DB SIDE: Store an rrule string + an event duration.
CLIENT SIDE: Reconstitute events start date array from rrule string. Only start times would be known and end times would be calculated by adding the duration as an offset to each start time in the array.
DB SIDE: Store a modified rrule string which encodes an endtime.
CLIENT SIDE: A special wrapper function reads the modified rrule string and reconstitutes it as two date arrays; one representing event start times and the other end times.
Option 1 seems easier but I suspect it will run into problems with daylight savings. For example, say I've an event that is every Tuesday from 6pm to 2 am Wednesday. In that case I'd store a duration of 8 hours in my database alongside that stringified rrule. Now let's fast forward to any 6pm Tuesday in the future. Does my event always end on Wednesday at 2am (or does that 8 hour duration sometimes make my event end at 1am or 3am)? How do I get it to always end at 2am?
... If you know the answer then just stop reading here.
How I've seen others handle duration offset
According to Kip in How to add 30 minutes to a JavaScript Date object? the smart way to offset a date time is to use a fancy library like moment.js.
He emphasizes that point by showing how easily things go wrong using non fancy date time libraries (showing how a naive minute offset function fails due to daylight savings)
function addMinutes(date, minutes) {
return new Date(date.getTime() + minutes*60000);
}
addMinutes(new Date('2014-11-02'), 60*24) //In USA, prints 11pm on Nov 2, not 12am Nov 3!
But something weird happens for me. The function above was supposed to output 11pm on Nov 2 - which is the wrong answer i.e. it was supposed to fail because of daylight savings. When I run it, it actually outputs the right time 12am on Nov 3 (note: I'm in Chicago/Central time).
When I compare the output of his naive function to the output of moment.js and luxon.js, I get the same answer as you can see in this observable notebook.
Scratching my head
What's more, if using luxon or moment, when you add a days worth of minutes to 2014-11-02 you get2014-11-03T00:00:00.000Z but if you just directly add a day to 2014-11-02 you get 2014-11-03T01:00:00.000Z - it's an hour off.
So am I better off pursuing option 2?
Now let's fast forward to any 6pm Tuesday in the future. Does my event always end on Wednesday at 2am (or does that 8 hour duration sometimes make my event end at 1am or 3am)? How do I get it to always end at 2am?
The standard Javascript Date object automatically handles the daylight savings shift for you. Even if you add 8 hours to a date at 6pm the day before daylight savings, the new date will still end at 2am the next day.
Incidently, I implemented duration support in rSchedule and since it supports both the standard javascript Date as well as moment/luxon dates, you can test a recurring event with a duration using either library and see that they both produce the same result.
This example can be seen on stackblitz.
import { Schedule } from '#rschedule/rschedule';
import { StandardDateAdapter } from '#rschedule/standard-date-adapter';
// This example will also work with `moment`, `moment-timezone`, and `luxon`
// (assuming you import the proper date adapter -- see rSchedule docs)
const schedule = new Schedule({
rrules: [
{
start: new Date(2019,9,10,18),
frequency: "DAILY",
duration: 1000 * 60 * 60 * 8,
count: 30
}
],
dateAdapter: StandardDateAdapter,
});
schedule.occurrences().toArray().forEach(adapter => {
console.log(
{
start: adapter.date.toLocaleString(),
end: adapter.end.toLocaleString(),
}
)
})
Turns out I actually want to know when an event ends
To find out when this event ends, you could do:
const iterator = schedule.occurrences({ reverse: true })
const { end } = iterator.next().value
This trick would only work with an event that actually has an end date (so not an event with infinite occurrences).
I wrote the original answer you are referring to about a decade ago. Seven years later, I made an edit, changing new Date(2014, 10, 2) to new Date('2014-11-02'). I thought this would be easier to read (because you don't have to explain that the months in that version of the constructor start at 0 instead of 1). But as #RobG pointed out, formatting in this way causes it to be parsed as UTC. I've gone back and fixed this now (thanks for pointing it out).
To get to your "scratching my head" part of your question:
What's more, if using luxon or moment, when you add a days worth of minutes to 2014-11-02 you get 2014-11-03T00:00:00.000Z
The Z at the end of that timestamp means it is in UTC, and UTC does not observe daylight savings time. So if you start with 2014-11-02T00:00:00.000Z, and add 24 hours, you get 2014-11-03T00:00:00.000Z. When you add hours/minutes/seconds, there's no need to worry about daylight saving time.
but if you just directly add a day to 2014-11-02 you get 2014-11-03T01:00:00.000Z - it's an hour off.
In this case what is happening is you are starting with 2014-11-02T00:00:00.000Z, but when you tell the library to add one day, and you don't specify a time zone, the library is assuming you are in your local time zone, so it adds one local day. Because you cross a DST boundary, that day is 25 hours long, and when you print it as an ISO timestamp in UTC, you end up with 2014-11-03T01:00:00.000Z (25 hours later).
Time zone stuff is hard, even if you are using a library. Most people can get by for a long time not knowing or caring that for many users one day a year is 25 hours long. But if these edge cases will matter to you, the best approach is to play around with them like you're doing, and make sure you really understand what is happening and why.

What is a good way to convert %H:%M:%S strings to timespan?

I have a data set with most of the data points in H:M:S format, such as 20:59:59, showing the time span used by individual athletes at different laps.
With D3.js, what is the best way to convert it to time spans, i.e. number of seconds?
I tried parser = d3.timeParse("%H:%M:%S"); but it seems to be very wrong..
If you are using D3 3.x, then you can construct a suitable parser with:
var parser=d3.time.format("%H:%M:%S");
or if you were using D3 4.x:
var parser=d3.timeParse("%H:%M:%S");
Either way, you would then be able to convert your data points into JS Date objects like this:
var t1 = parser.parse("20:59:59");
var t2 = parser.parse("21:02:13");
You can find out the elapsed time between these two time instances by subtracting, eg:
var elapsed = t2 - t1; /* Returns 134000 = 134 seconds */
elapsed will be the number of elapsed milliseconds.
The reason that works is that Date objects provide a valueOf() method which returns the number of milliseconds elapsed since Jan 1 1970. Subtracting the date objects automatically calls valueOf() and subtracts the returned values.
Note that if the time strings you are parsing do not include a date portion, then you will have problems if the starting time and ending time are not in the same day.
EDIT:
From the comments it becomes clear that the times you are working with are already lap durations, in H:M:S format, and you want to convert them to seconds. That does not require D3, it is easy enough:
var components = /^(\d+):(\d+):(\d+)$/.exec("20:59:59");
var elapsedSecs = components[1]*3600+components[2]*60+components[3];
You can use d3.time.format("%H:%M:%S") for your conversion.

Quickest way to add minutes and seconds to string formatted 'h:i:s: A'

I'm using Bootstrap-Timepicker to collect a time field. The user will select the the start time with the timepicker widget as well as a number of iterations and a time interval. I want to add the selected interval to the time for the given number of iterations.
For example: if the User select 11:40:00 PM and wishes to ad 10 minutes and 15 seconds 4 times, then I need to iterate through the following time values:
11:40:00 PM
11:50:15 PM
12:00:30 AM
12:10:45 AM
Bootstrap-timpicker doesn't provide an easy way to manipulate the time value like this that I know of, and when I get its value it returns a String formatted h:i:s A as in the example above.
I figure there must be an easier way to this than the way I plan to solve this problem currently which would be to parse the value and run if conditions to check to see if the hour or AM/PM roll over. Should I convert this string to a datetime, and if so, how? I'm open to other suggestions as well.
Put in the time, with a date, into the JavaScript date object like this:
var myDate = new Date('11:40:00 PM 2015-09-23');
Then, add minutes
myDate.setMinutes(myDate.getMinutes() + 10);
Then add seconds
myDate.setSeconds(myDate.getSeconds() + 15);
And this can be repeated 4 times
You can get the time back out in a format you'd like, just look at the docs
You can also add the time at once, but this is less pretty I think
myDate.setTime(myDate.getTime() + 615);

How do I display the next weekly recurring date in a schedule using JavaScript?

I need to use JavaScript to display the next event on a recurring weekly basis on a website. Let's say we start an event every 10am every Saturday - I'll need it to display that the next event begins on "Saturday, (Month) (Date) at 10am".
The only thing that I need to be dynamic on the website is the date of the next event (both month and date).
Idea 1: One way I started thinking about it I would need to and have some sort of a starting reference calendar date from where the schedule starts, and then some pattern of n-days to calculate the upcoming dates from that starting point and compare those against todays date, then display the result of the next in the sequence
Idea 2: Instead of using a pattern of N-days to calculate from a hard-coded reference point, what if I coded the day of the week the event occurs and check against that, calculating the date by comparing the days of the week and adding to todays date (would have to account for rollovers at 28/30/31 days and a way to account for which months max out at which number)
Maybe I'm way off-base in my thinking, but any help here would be appreciated. I'm learning JavaScript and coming from an HTML+CSS background using jQuery plugins if that helps frame your answer in a way I'll grasp.
Here is a rough solution that may work. It's just general code that you will need to debug but I think it's a good starting point! Date() is a built-in JavaScript object.
var today = new Date();
//var dd = today.getDate(); *just some sample functions of Date()*
//var mm = today.getMonth()+1; *January is 0!*
if(today.getDay() == 6) alert('it is saturday');
// today.getDate() < 8 *this can be used to check where in the month a day falls
// if you want only first, second, third, etc., Saturday
Please let me know if this helps at all!
You could use rSchedule for this (a javascript recurrence library which I maintain).
Example:
Let's say we start an event every 10am every Saturday
import { Schedule } from '#rschedule/rschedule';
import { StandardDateAdapter } from '#rschedule/standard-date-adapter';
const schedule = new Schedule({
rrules: [{
frequency: 'WEEKLY',
// the hypothetical start datetime of your recurring event
start: new Date(2019, 5, 15, 10),
}],
dateAdapter: StandardDateAdapter,
});
The only thing that I need to be dynamic on the website is the date of the next event (both month and date).
// get standard javascript iterator for occurrences starting after now
const iterator = schedule.occurrences({
start: new Date()
})
// the next date
const nextDate = iterator.next().value;
// or iterate over all future occurrences
for (const date of iterator) {
// do stuff...
}

Number change every 24 hours

I would like to provide a list of about 40 positive numbers and then have my home page display the first number. Then at midnight, the number will change to the next number in the list. When at the end of the list, the rotation starts over. So for instance, a user goes to my page several times today and sees the first number in the list. Then say 1:00am, they go back to the page and see the next number on the list and will do so until midnight tomorrow night...etc etc etc Is this possible?
I've tried several different javascripts that does change the number according to my list BUT when a user goes to the page, it starts the list over again.
I am so new to this, I don't know which part of my code you might need since what I have been trying does what its supposed to do...just not how I want it to do.
Do I make sense?
Unfortunately, I won't be able to use php for this webpage.
You can use new Date().getTime() to get the current time in milliseconds. If you convert that to days, and round down, you can find the number of days since the epoch. Take the modulo of that and use it to obtain the index of your array of numbers. Since you're always using the current time (according to the user), you don't need to store any cookies or server-side counter.
var nums = [1,1,2,3,5,8,13]; // and so on
d = new Date(), // today
days = d.getTime() / (1000*60*60*24),
idx = Math.floor(days) % nums.length;
alert(nums[idx]); // should change once a day
Define a start date (preferably in the past), calculate today's date, subtract the two, and then use modulo to squeeze it into the range of numbers you want to show:
var minDate = 15681, // days since 1-1-1970
nowDate = Math.ceil(new Date().getTime() / 1000 / 86400),
numbers = [1, 5, 1234, 6543, 1236456];
// get the number for today
console.log(numbers[(nowDate - minDate) % numbers.length]);
If the start date doesn't matter, you can simplify the expression to:
numbers[Math.ceil(new Date().getTime() / 86400000) % numbers.length];
Btw, this won't change at midnight for everyone btw, because .getTime() gives GMT time.

Categories