every is a personal calendar tool, heavily inspired by the program when. it reads from a file containing a list of one-time or recurrent events, like this:

.warn 3d
.stay 2d

on 2019-12-20 20:00, friend's party (bring gifts)

# short-term recurrent

.warn 6H
.stay 6H
.urgent true

every 10d from 2019-11-07 08:00 to 2020-01-27, prescribed medication

.urgent false

# holidays

.warn 10d
.stay 1d

every 1y from _-12-24, クリスマスイブ!

# birthdays

.warn 5d
.stay 3d

every 1y from 1989-09-05, person a
every 1y from 1992-12-18, person b

and prints out which of those events are upcoming (within their .warn period) or recent (within their .stay period):

Sat 2019 Dec 21   20:00


Today       18:00 prescribed medication

[ normal ]

Yesterday   20:00 friend's party (bring gifts)
2019 Dec 24 クリスマスイブ!

i wrote this program because when, useful as it is, can't handle recurrent events in the way i'd hoped it would, and in the process have also added a few other features that seemed useful, like an output mode for use in scripts.


$ every --help
Usage: ./dbg_every [-c CALENDAR_FILE] [-e TEXT_EDITOR] [COMMAND]

  every is a simple console-based event calendar

  -h --help             print this help message and exit
  -v --version          print the version of every in use and exit
  -c --calendar=FILE    path to calendar file
  -e --editor=CMD       text editor for editing calendars
     --line-term=CHAR   line terminator for scripting output
     --col-delim=CHAR   column delimiter for scripting output

  c  (default) print upcoming events in interactive console mode
  s  print upcoming events in a format appropriate for scripting
  e  open calendar with text editor

every has three basic commands:


every e is a shortcut for opening the default calendar using $EDITOR (or the specified calendar using the specified command). the default calendar is every/cal.every under $XDG_CONFIG_HOME (which falls back to $HOME/.config/ when not specified)

if you use vim, a syntax file for the .every file format is available here: https://git.airen-no-jikken.icu/ageha/every.vim


every c prints out the upcoming events in a human-readable format, suitable for interactive use. this is the default command, so just every is equivalent.


every s prints the upcoming events in an easily-parseable format, for use in scripting. each line (by-default terminated with \n) represents one upcoming event, with fields (by default) delimited with \t.

the fields are:

| year | month | day | hour | minute | second | urgent | local | message |

calendar format

the calendar's grammar looks something like this:


 CMD := .local BOOL | .urgent BOOL | .warn INTERVAL | .stay INTERVAL
BOOL := yes | true | on | no | false | off

       | on DATE , MESSAGE
       | every INTERVAL from DATE
       | every INTERVAL from DATE , MESSAGE
       | every INTERVAL from DATE to DATE
       | every INTERVAL from DATE to DATE , MESSAGE

a DATE represents a single point in time in the form y-m-d H:M:S, where trailing elements can be left off (so 2020-01-09 08:30 or even just 2020 are valid) and any element can be replaced with an underscore to substitute it with a default value (_-12-09 == 2000-12-09. _-_-_ _:_:_ == 2000-01-01 00:00:00)

an INTERVAL represents a duration of time, and is made of one to six numbers, in any order, representing a number of years, months, days, hours, minutes and seconds. each element uses a single char subscript to indicate its type (1y 3d 2m is one year, two months, three days. 1H3M2S is one hour, three minutes, two seconds). elements may not repeat (so 1y5H2d3y is invalid)

there are two types of calendar entries: on entries, for one-time events occurring on a given DATE, and every entries, for events that recur every INTERVAL amount of time.

time intervals are calculated such that only the elements listed change. an every 1y from _-03-12 event will always occur on march 12th. this means that the intervals 1y and 365d are not equivalent, because some years contain leap days, and it also means that every 1y from 2008-02-29 will occur only during leap years

a MESSAGE is an optional string describing the event that will be displayed when printing. if present, it must begin with a , and lasts until the end of the line

each CMD changes settings which are applied to any following events.

.local defaults to true, and dictates whether entries should be interpreted as local time, or else as UTC (which could be useful for e.g. people who change timezones often)

.urgent defaults to false, and dictates whether entries should be displayed as "[ URGENT ]" in interactive output and have the urgent flag set in scripting output

.warn defaults to 1d, and is the warning period before an event actually occurs during which it should be considered active

.stay defaults to 1d, and is the amount of time an event should still be considered active after its occurrence


this program requires a POSIX environment. it also makes use of the timegm function, which is a non-standard extension but commonly implemented.

because of how time.h defines struct tm, and of how active events are calculated, every will not work correctly on architectures where int is smaller than 32 bits