4

I have a timer I want to run every day at 4am. Thus, I put something like this in my configuration.

(defvar my/timer nil)
(unless my/timer 
  (setq my/timer (run-at-time "04:00am" (* 24 60 60) #'my/function)))

This works as expected, except that each time this snippet is evaluated for the first time (e.g., when launching emacs), my/function is executed immediately. In fact, evaluating (run-at-time "04:00am" (* 24 60 60) #'my/function) at any time in the day causes this effect.

Maybe I have a misunderstanding, but isn't it supposed to run only at the specified time ?

Firmin Martin
  • 1,265
  • 7
  • 23

1 Answers1

5

Unfortunately, you're telling it to run the function at 04:00am today. Unless you evaluated that code between midnight and 4am, that's a target time in the past, and so Emacs runs it ASAP.

The run-at-time code knows this is a bit dumb:

   ;; Handle "11:23pm" and the like.  Interpret it as meaning today
   ;; which admittedly is rather stupid if we have passed that time
   ;; already.  (Though only Emacs hackers hack Emacs at that time.)
   (if (stringp time)
       (progn
         (require 'diary-lib)
         (let ((hhmm (diary-entry-time time))
               (now (decode-time)))
           (if (>= hhmm 0)
               (setq time
                     (encode-time 0 (% hhmm 100) (/ hhmm 100)
                                  (decoded-time-day now)
                                  (decoded-time-month now)
                                  (decoded-time-year now)
                                  (decoded-time-zone now)))))))

You could figure out how many seconds there are between now and 4am, and use that instead; or else something like:

(unless my/timer 
  (let ((24hours (* 24 60 60)))
    (setq my/timer (run-at-time "04:00am" 24hours #'my/function))
    (when (> (timer-until my/timer (current-time)) 0)
      (timer-inc-time my/timer 24hours))))
phils
  • 48,657
  • 3
  • 76
  • 115
  • Thanks, it solves the issue! Could you just explain, in your last snippet, why the `run-at-time` in the varlist does not immediately call `#'my/function` ? On the other hand calling the let-form without the body invoke immediately `#'my/function`... – Firmin Martin Mar 20 '21 at 07:58
  • 1
    Timers only have an opportunity to trigger when Emacs isn't busy running code; so in this case we can schedule the timer to run in the past and then inspect and manipulate that scheduled time before it gets used. – phils Mar 20 '21 at 08:10