Chronos
Chronos: multiple simultaneous countdown / countup timers in Emacs.
Install / Use
/learn @dxknight/ChronosREADME
Chronos
Whats New
Version 1.2 (Internal) allows entry of multiple cumulative timers, e.g. 'M-x
chronos-add-timers-from-string' 25/Pomodoro: Work on helm-chronos + 5/Pomodoro: Rest will add two timers: one to go off in 25 minutes and one to go off in 30
(=25+5).
Version 1.1 (Internal) has extra configuration options and a revamped
notification system using the hook chronos-expiry-functions.
If you are upgrading from v1.0, you might need to change your configurations if you referred to chronos-shell-notify-command (which has been split into chronos-shell-notify-program and a list of strings chronos-shell-notify-parameters) or chronos-action-function (which has become the hook chronos-expiry-functions).
Also, new notification methods have been added to:
- Play a wav file with Emacs' built in play-sound (requires Emacs to be built with sound support).
- Send a desktop notification (requires Emacs built with libnotify plus a system with dbus, consolekit and a notification daemon).
- Tell you when and what expired using a text to speech program (requires a program like espeak).
Introduction
Chronos provides multiple countdown / countup timers, updated every second, shown sorted by expiry time in the special buffer *chronos*.
The *chronos* buffer might look like:
Expiry Elapsed To go Message
[17:02] --now--
[17:07] 9 4:51 Coffee
In this example, the time 'now' is 17:02. A five minute countdown timer was set up 9 seconds ago. It is expected to expire in 4 minutes 51 seconds at 17:07.
Various notification methods are available. Here is a picture of the in-buffer
notification method, which puts the message of an expired timer in the
chronos-notification face for chronos-notification-time seconds.

Motivation
There are a number of Emacs packages that provide timer functionality, such as job tracking, alarms or a countdown timer in the mode line. However, I wanted one that showed multiple simultaneous countdown and countup timers in a specific buffer.
Possible use cases include:
- Cooking timers (what I use it for most often).
- Presentation timers (when to start wrapping up, when to start questions etc.).
- Timing how long something takes (on a scale of seconds to hours).
Installation
Old school
Put chronos.el somewhere Emacs can find it and evaluate:
(require 'chronos)
MELPA
If you have set up access to MELPA http://melpa.org/#/getting-started, you can install using:
M-x package-install chronos
MELPA/use-package
If you have MELPA and use-package https://github.com/jwiegley/use-package,
you can install by evaluating:
(use-package chronos :ensure t)
Add that snippet to your init file if you want chronos for future sessions.
Quick start
M-x chronos-add-timer will start chronos and prompt you to add a timer. When
prompted for the time, enter an integer number of minutes for the timer to count
down from (see later for more sophisticated options). When prompted for the
message, enter a short description of the timer for display and notification.
Alternatively, you can use M-x chronos-add-timers-from-string. This function
prompts for a string, from which one or more timers can be parsed. See the
section Add timers from string.
Dependencies
Chronos should work on a stock Emacs install.
When a timer expires, various functions can be run to notify the user. Some of these can require particular builds of Emacs or system features. See the notifications section later.
Enhancements
There is a helm interface for conveniently adding timers: helm-chronos.
See https://github.com/dxknight/helm-chronos
Configuration
No configuration is required, but the defaults can be customized with
M-x customize-group chronos or set in your init file.
You may wish to bind chronos-add-timer, set the notification functions and
change the faces used in the chronos buffer.
Expiry time specification
The expiry time specification can be absolute or relative. Relative times are with respect to a base time that, depending on circumstances, could be:
- current time, or
- the expiry time of another timer
absolute time
Start with an =, followed by a 24+hr clock time. For example, =17:00 is an
expiry time of five o'clock in the afternoon. Times are for the current day.
If you want to refer to times tomorrow (i.e. past midnight), add 24 hours:
e.g. =25:30 specifies 1:30 tomorrow morning.
relative time
Up to three numbers can be given, separated with colons :. A minus
- can be prepended to indicate negative times.
-
No numbers (or all zeros) - the base time
-
One number - minutes
-
two numbers - hours and minutes and
-
three numbers - hours minutes and seconds.
Positive numbers indicate later expiry, negative ones earlier.
For example, if the base time is 17:00:
5gives an expiry time of 17:051:30gives 18:300:0:30gives 30 seconds after 17:00- empty or
0gives a count up timer starting now, at 17:00.
Negative relative times are more useful against existing timers. Here, a timer
was set for the absolute time 19:00, then with the cursor on this timer and
using (C-u a), two relative timers were set to expire earlier, one with -5
(five minutes before end, i.e. 18:55) and the other with -15 (fifteen
minutes before end, i.e. 18:45).
Expiry Elapsed To go Message
[18:04] --now--
[18:45] 45 41:10 Any questions
[18:55] 58 51:10 Thanks and goodbyes
[19:00] 2:01 56:10 Talk ends
Add timers from string
Although mainly useful for the helm-chronos helm interface,
chronos-add-timers-from-string parses a single string to set up one or more
consecutive timers. If the first timer in the string has a relative expiry
time, this is relative to current time or, with the prefix argument, to the
currently selected timer. Subsequent timers are relative to the previous timer
in the string.
The format of the string for a single timer is <expiry specification>/<message>.
You can delimit multiple consecutive timers with +.
For example, you could enter a pomodoro style timer with:
25/Pomodoro: Work on helm-chronos + 5/Pomodoro: Rest
which will give a timer to go off in 25 minutes and another in 30 (=25+5) minutes:
Expiry Elapsed To go Message
[13:10] --now--
[13:35] 25:00 Pomodoro: Work on helm-chronos
[13:40] 30:00 Pomodoro: Rest
A more complex example might be
=17:00/Drink coffee + -5/Brew coffee + -5/Boil kettle + 25/Finish break
Which will give a timer at 5 o'clock to drink coffee, a timer five minutes before that (16:55) to start brewing the coffee, a reminder five minutes before that (16:50) to put the kettle on and a reminder 25 minutes after that (17:15) to finish drinking coffee and get back to work.
Expiry Elapsed To go Message
[13:19] --now--
[16:50] 3:30:46 Boil kettle
[16:55] 3:35:46 Brew coffee
[17:00] 3:40:46 Drink coffee
[17:15] 3:55:46 Finish break
Controls
In the *chronos* buffer, new timers can be added, selected, paused, unpaused, lapped, adjusted or deleted. Default keybindings in this buffer are:
Key | Action
--- | ------------------------------------------------------------------------------------
a | add a timer by specifying expiry time and a message
A | add multiple consecutive timer(s) in one go
n/p | move selection down/up (also C-n/C-p <down>/<up>)
SPC | pause/unpause (pausing affects time to go and the expiry time, but not elapsed time)
d | delete selected timer
D | delete all expired timers
e | edit selected timer
l | lap selected timer
F | freeze/unfreeze the display
q | quit window
Whether relative times are against current time or the expiry time of the selected timer is controlled by the prefix.
-
Adding a timer with
ais relative to current time;(C-u a)will calculate expiry times relative to the selected timer. -
Editing (adjusting) the selected timer with
ewill calculate times relative to the currently set expiry time of that timer.(C-u e)will calculate relative times against the current time.
Add a timer
Adding a timer with a in the *chronos* buffer is the same as adding
a timer with M-x chronos-add-timer.
Adding with A is the same as M-x chronos-add-timers-from-string.
Move selection
A timer is selected when the cursor is on its line. The cursor can be
moved down and up the timer list, wrapping as necessary, with n and
p (also C-n, C-p, <down> and <up>).
The -now- timer can be selected, but cannot be paused, lapped or deleted.
Pause/Unpause
SPC pauses a running timer and unpauses a paused one. Pausing a
timer halts the time to/since expiry and pushes back the expected
expiry time. The time elapsed since setting up the timer continues to
increase. The timer is displayed in the chronos-paused face
Deleting timers
The selected timer can be deleted with d. Alternatively, all
expired timers can be deleted with D. Once deleted, they're gone.
Editing timers
When editing a timer, you will be prompted for an expiry time specification like
used when adding a new timer. If the edit is started with e, the new expiry
time will be calculated relative to the timer's existing expiry time. If the
edit is called with a prefix
