6

How can I run a function at a given time and date?

Example: I have a function that needs to run on the 12th of each month at 10AM.

This page will be running 24/7, if this is important.

Obviously I'd have to compare against the current date, but I'm not sure how to check if the current date and time has been matched.

Shannon

Alexander Abakumov
  • 13,617
  • 16
  • 88
  • 129
Shannon Hochkins
  • 11,763
  • 15
  • 62
  • 95
  • 2
    how do you know that they'll be on your particular web page at that time and date? – Alnitak Sep 30 '13 at 06:55
  • check `new Date().getDate()` for the day, and `new Date().getHours()` for the hour. I hope you don't expect other people to write the entire code for you. – Joe Simmons Sep 30 '13 at 06:56
  • @Alnitak, by comparing it to the current date? – Shannon Hochkins Sep 30 '13 at 06:58
  • No I don't @JoeSimmons, I just don't know how to start this.. – Shannon Hochkins Sep 30 '13 at 06:59
  • 2
    @ShannonHochkins you're missing my point. You're asking for JS code. JS code runs in browsers. That implies that you're expecting this _specific_ page to be running if not 24x7, then at least at the time your "surprise" is supposed to happen. That would seem to be an, umm, unusual application of a web page. – Alnitak Sep 30 '13 at 06:59
  • In my case, that's exactly what will be happening, this isn't for public use, and I don't think that's relevant to my question anyway, I know what I asked. @Alnitak – Shannon Hochkins Sep 30 '13 at 07:02
  • it's relevant to the question, since it clarifies that you do indeed know what you asked. As written, that was far from obvious. – Alnitak Sep 30 '13 at 07:03
  • I'm not here to argue, just trying to understand the intent. Every answer here is deficient in some way, but an ideal answer depends on knowing how the page is going to work. For example I might give a different answer if the page might be sat untouched for days than if the page is only going to be loaded a few hours beforehand. – Alnitak Sep 30 '13 at 07:12

5 Answers5

19

It's not advised to use setInterval because it has non-deterministic behaviour - events can be missed, or fire all at once. Time will fall out of sync, too.

The code below instead uses setTimeout with a one minute period, where each minute the timer is resynchronised so as to fall as closely to the hh:mm:00.000s point as possible.

function surprise(cb) {
    (function loop() {
        var now = new Date();
        if (now.getDate() === 12 && now.getHours() === 12 && now.getMinutes() === 0) {
            cb();
        }
        now = new Date();                  // allow for time passing
        var delay = 60000 - (now % 60000); // exact ms to next minute interval
        setTimeout(loop, delay);
    })();
}
Alnitak
  • 334,560
  • 70
  • 407
  • 495
  • That's quite interesting! So I'm assuming that cb, that you're passing to the surprise function, is also a function? Meaning that you run this like so: `surprise(otherFunctionName);`. Do you mind me asking why you do it this way? – Shannon Hochkins Sep 30 '13 at 10:42
  • 1
    Yes, that's right - the "why" is called "separation of concerns" - separating the "when" you want something to happen from the "what". – Alnitak Sep 30 '13 at 10:44
  • Where you're using Modula to calculate the remainder of milliseconds, do you agree it would be more useful putting the minute value in milliseconds as a variable? It would read a bit nicer too I guess, but I do like your solution, it should would quite nicely. – Shannon Hochkins Oct 01 '13 at 04:50
  • 1
    @ShannonHochkins re: 60s - I'd say at your discretion. Personally I read 60000 and the conversion is instantaneous. Note that even this method is not without risk - if the system clock is adjusted underneath this code the timer could fire when the time isn't quite (yet) 10:00:xx. It's actually pretty hard to mitigate that, though! – Alnitak Oct 01 '13 at 14:58
  • Cool, no worries @Alnitak, thankyou for your post, just a quick question though, if I was to do this server side rather than client side, is there a way to still have a user interface on the webpage to 'pick' the time, and still correctly start a timer without having the page constantly open? Probably not right.. – Shannon Hochkins Oct 01 '13 at 21:52
  • Now take the final step and don't loop, but set only one timeout, to the exact moment you're waiting for. (And end up at my answer ;) ) – flup Oct 01 '13 at 22:25
  • @flup there are limits as to how far in the future you can do a single `setTimeout` call. – Alnitak Oct 01 '13 at 22:46
  • @ShannonHochkins that's getting towards the point I was making in my original comments - the page has to be open to do *anything*, because that's where the JS runs. Now, if you want to specify a time, and then have stuff work if the page is reloaded, you'd have to get into cookies, `localStorage`, that sort of thing, to restore the page's state on reload. – Alnitak Oct 01 '13 at 22:49
  • @Alnitak I didn't know that, thanks! It seems risky to me to aim for the exact changing of the minute. If you get scheduled a little early, your first check might hit 11:59:59.999 and decide it is not yet time. The second check lands at 12:00:00.001 and decides to wait for another minute. You should probably aim for the halfway point instead. – flup Oct 02 '13 at 08:59
  • @flup yeah, one approach is to check for "time < target" changing to "time >= target", but in extremis as you say it'll then wait to 10:01. I'll look at that again. – Alnitak Oct 02 '13 at 09:26
4

On the page where o want to do the check add this

setInterval(function () {
    var date = new Date();
    if (date.getDate() === 12 && date.getHours() === 10 && date.getMinutes === 0) {
        alert("Surprise!!")
    }
}, 1000)

FIDDLE


Update- add date.getSeconds == 0 to limit it to fire only one at 10:00:00. Thanks to comments below

bhb
  • 2,476
  • 3
  • 17
  • 32
  • 4
    this code has bugs, one visible, and one invisible. The invisible bug is that it'll repeatedly show the alert over and over so until the time reaches 10:01. – Alnitak Sep 30 '13 at 07:05
  • 3
    @Alnitak, again, you're commenting based on an assumption that I wanted to run the function only once, and also it's not upto the user to completely write the code, it's fairly obvious and simply to clear the interval once it's passed. – Shannon Hochkins Sep 30 '13 at 07:13
  • @ShannonHochkins if you clear the interval it won't fire _next_ month (assuming the page isn't reloaded between times). – Alnitak Sep 30 '13 at 07:55
  • I think this way you can be unlucky and miss the exact second you're waiting for, scheduling is not *that* precise. It could be the first one gets scheduled at 11:59:59.999 and the next one at 12:00:01.000 – flup Oct 01 '13 at 07:17
3

You can instantiate two Date objects. One for now and one for the next instance of the event. Now is easy: new Date(). For the next instance you can loop through the options till you find one larger than now. Or do some more sophisticated date time wizardry. Compare the getTime() of the both, and then do a setTimeout for the alert.

EDIT: Updated since @Alnitak points out that there's a maximum to the timeout, see setTimeout fires immediately if the delay more than 2147483648 milliseconds.

function scheduleMessage() {
    var today=new Date()

    //compute the date you wish to show the message
    var christmas=new Date(today.getFullYear(), 11, 25)
    if (today.getMonth()==11 && today.getDate()>25)
        christmas.setFullYear(christmas.getFullYear()+1)

    var timeout = christmas.getTime()-today.getTime();
    if( timeout > 2147483647 ){
        window.setTimeout( scheduleMessage(), 2147483647 )
    } else {
        window.setTimeout(function() {alert('Ho Ho Ho!'); scheduleMessage()}, timeout)
    }
}
Community
  • 1
  • 1
flup
  • 26,937
  • 7
  • 52
  • 74
2

You can use something like this

var runned = false;
var d = new Date();
if(d.getDate() == 12 && d.getHours() == 10 && !runned){
    //Do some magic
    runned = true;
}

If you want some with the minute (and not the whole hour you can add d.getMinutes()

-3

maybe use and iframe with meta refresh and workout content server side

<meta http-equiv="refresh" content="{CHANGE_THIS_TO_WHAT_YOU_CALCULATED_AT_SERVER}">

or use javascripts setInterval

var interval = 300000; // run in 5 minutes
window.setInterval("reloadRefresh();", interval);

function reloadRefresh() {
 // do whatever
}

and

gwillie
  • 1,893
  • 1
  • 12
  • 14