From fae5dd77610bfbc6ce9fd6c32b5ed20e53129d25 Mon Sep 17 00:00:00 2001 From: Paolo Donadeo Date: Sun, 28 Dec 2025 16:16:23 +0100 Subject: [PATCH] Aggiunto manuale di remind in formato Markdown --- contrib/remind/remind.md | 7234 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 7234 insertions(+) create mode 100644 contrib/remind/remind.md diff --git a/contrib/remind/remind.md b/contrib/remind/remind.md new file mode 100644 index 0000000..90c9681 --- /dev/null +++ b/contrib/remind/remind.md @@ -0,0 +1,7234 @@ +# NAME + +remind - a sophisticated reminder service + +# SYNOPSIS + +**remind \[*options*\] *filename* \[*date*\] \[*\*rep*\] \[*time*\]** + +# DESCRIPTION + +**Remind** reads the supplied *filename* and executes the commands found +in it. The commands are used to issue reminders and alarms. Each +reminder or alarm can consist of a message sent to standard output, or a +program to be executed. + +If *filename* is specified as a single dash \'-\', then **Remind** takes +its input from standard input. In this case, if **Remind** has been +compiled against the GNU Readline library, it will use Readline to give +you an interactive line-editing interface. + +If *filename* happens to be a directory rather than a plain file, then +**Remind** reads all of the files (but not any subdirectories!) in that +directory that match the pattern \"\*.rem\". The files are read in +sorted order; the sort order may depend on your locale, but should match +the sort order used by the shell to expand \"\*.rem\". + +**Remind** reads its files starting from the beginning to the end, or +until it encounters a line whose sole content is \"\_\_EOF\_\_\" +(without the quotes.) Anything after the \_\_EOF\_\_ marker is +completely ignored. + +# MODES OF OPERATION + +**Remind** has four major modes of operation: + +**Agenda Mode** + +: Agenda mode is the default mode. In this mode, **Remind** prints + today\'s reminders on standard output and exits. It may fork a + background process to pop up queued reminders for later in the day. + +**Calendar Mode** + +: In this mode, **Remind** generates a calendar either by drawing it + in the terminal or by sending data in computer-readable format to a + back-end program that actually draws the calendar. + +**Daemon Mode** + +: This is a special mode of operation in which **Remind** is invoked + by a front-end and runs as a daemon, accepting requests from the + front-end and sending messages back to the front-end. **TkRemind** + uses **Remind** in daemon mode. + +**Purge Mode** + +: In this mode, **Remind** produces no output, but creates new + versions of its input files with all expired reminders commented + out. + +# OPTIONS + +**Remind** has a slew of options. If you\'re new to the program, ignore +them for now and skip to the section \"REMINDER FILES\". + +**-n** + +: The **-n** option causes **Remind** to print the **next** occurrence + of each reminder in a simple calendar format. You can sort this by + date by piping the output through **sort(1)**. Note that the **-n** + option causes any **-g** option to be *ignored* and also implicitly + enables the **-o** option. + +**-j\[*n*\]** + +: Runs **Remind** in \"purge\" mode to get rid of expired reminders. + See the section PURGE MODE for details. + +**-r** + +: The **-r** option disables **RUN** directives and the **shell()** + function. + +**-c**_\[flags\]n_ + +: The **-c** option causes **Remind** to produce a calendar that is + sent to standard output. If you supply a number *n*, then a calendar + will be generated for *n* months, starting with the current month. + By default, a calendar for only the current month is produced. This + option implicitly enables the **-o** option. + + You can precede *n* (if any) with a set of flags. The flags are as + follows: + + **\'+\'** + + : causes a calendar for *n* weeks to be produced. + + **\'a\'** + + : causes **Remind** to display reminders on the calendar on the + day they actually occur *as well as* on any preceding days + specified by the reminder\'s *delta*. This *also* causes + **Remind** to include text outside %\"\...%\" sequences that + would otherwise be removed (though the actual %\" markers + themselves are removed.) + + **\'l\'** + + : causes **Remind** to use VT100 line-drawing characters to draw + the calendar. The characters are hard-coded and will only work + on terminals that emulate the VT00 line-drawing character set. + + **\'u\'** + + : is similar to \'l\', but causes **Remind** to use UNICODE + line-drawing characters to draw the calendar. The characters are + hard-coded and will only work on terminals that are set to UTF-8 + character encoding. This flag also enables the use of the + UNICODE \"left-to-right\" mark that can fix up formatting + problems with right-to-left languages in the calendar display. + + **\'z\'** + + : causes **Remind** to use escape sequences to turn reminders with + a \"URL\" info string into hyperlinks. In order to work, your + terminal must support the escape sequences documented at + https://gist.github.com/egmontkob/eb114294efbcd5adb1944c9f3cb5feda + + **\'c\'** + + : causes **Remind** to use VT100 escape sequences to approximate + SPECIAL COLOR reminders. Note that this flag is kept for + backwards-compatibility; you should use the + **-@**_\[n\]\[,m\]\[,b\]_ command-line option instead. + + In a UTF-8 locale, **Remind** will use \"left-to-right marks\" when + creating a calendar with the **-c** option. Some terminals don\'t + handle this correctly and garble the rendering of the calendar; see + the documentation of **\$SuppressLRM** in the section \"SYSTEM + VARIABLES\" for a workaround. + +**-@\[*n*\]\[,*m*\]\[,*b*\]** + +: Tells **Remind** to approximate SPECIAL COLOR and SHADE reminders + using VT100 escape sequences. The approximation is (of necessity) + very coarse, because the VT100 only has eight different color + sequences, each with one of two brightnesses. A color component + greater than 64 is considered \"on\", and if any of the three color + components is greater than 128, the color is considered \"bright\". + + If you supply the optional numeric parameters, the have the + following meanings: *n*=0 tells **Remind** to use the standard 16 + VT100 colors. *n*=1 tells it to use an extended 256-color palette + supported by many terminal emulators such as xterm. And *n*=2 tells + it to use escape sequences that support true 24-bit colors, again + supported by many terminal emulators such as xterm. + + If the optional *m* parameter is supplied following a comma, then + *m*=0 tells **Remind** that the terminal background is dark, and + **Remind** will brighten up dark colors to make them visible. If + *m*=1, then **Remind** assumes the terminal background is light and + it will darken bright colors to make them visible. If *m* is + specified as 2, then **Remind** does not perform any adjustments, + and some reminders may be hard or impossible to see if the color is + too close to the terminal background color. If you supply the letter + **t** rather than a number, then **Remind** attempts to guess the + background color of the terminal, *even if* stdout is not a + terminal. + + On startup, if the standard output is a terminal, **Remind** + attempts to determine if the terminal background is dark or light by + sending a special escape sequence to determine the background color. + The *m* parameter can override this check (or force it if *m* is + given as **t**.) + + If the optional *b* parameter is supplied following a comma, then + *b=0* tells **Remind** to ignore SPECIAL SHADE reminders (the + default) and *b=1* tells **Remind** to respect SPECIAL SHADE + reminders by emitting VT100 escape codes to color the background of + the calendar cell. Note that SHADE does not work well unless you are + using the extended 256-color palette (*n*=1) or the true 24-bit + colors (*n*=2). Note that for calendar cells that are shaded, the + clamping mechanism described earlier for *m=0* or *m=1* is skipped; + it is assumed that if you set *both* the foreground color of a + reminder and the background color of a cell, then you know what you + are doing. + +**-w*col*\[,*pad*\[,*spc*\]\]\]** + +: The **-w** option specifies the output width, padding and spacing of + the formatted calendar output. *Col* specifies the number of columns + in the output device. If *col* is not specified, or is specified as + 0, it defaults to the larger of 71 or the actual width of your + terminal, or to 80 if standard output is not a terminal. If *col* is + specified as the letter **t**, then **Remind** attempts to get the + width of the **/dev/tty** terminal device. This is useful, for + example, if you pipe calendar output into **less**; even though + standard output is a pipe, you want the calendar to be sized + correctly for your terminal window: + + remind -c -wt .reminders | less + +> Note that the value of *col* is also used to set the system variable +> \$FormWidth, which is initialized to *col* - 8. See \"SYSTEM +> VARIABLES\" for details. +> +> *Pad* specifies how many lines to use to \"pad\" empty calendar boxes. +> This defaults to 5. If you have many reminders on certain days that +> make your calendar too large to fit on a page, you can try reducing +> *pad* to make the empty boxes smaller. *Spc* specifies how many blank +> lines to leave between the day number and the first reminder entry. It +> defaults to 1. +> +> Any of *col*, *pad* or *spc* can be omitted, providing you provide the +> correct number of commas. Don\'t use any spaces in the option. + +**-s\[a\]n** + +: The **-s** option is very similar to the **-c** option, except that + the output calendar is not formatted. It is listed in a \"simple + format\" that can be used as input for more sophisticated + calendar-drawing programs. If *n* starts with \"+\", then it is + interpreted as a number of weeks. This option also implicitly + enables the **-o** option. + + If you immediately follow the **s** with the letter **a**, then + **Remind** displays reminders on the calendar on the day they + actually occur *as well as* on any preceding days specified by the + reminder\'s *delta*. + +**-p\[a\]\[p\]\[p\]\[q\]\[+\]n** + +: The **-p** option is very similar to the **-s** option, except that + the output contains additional information for use by a back-end + such as the **Rem2PS** program, which creates a PostScript calendar, + and various other back-end programs. If *n* starts with \"+\", then + it specifies a number of weeks rather than a number of months, and + back-ends are expected to produce weekly calendars. Note that not + all back-ends support weekly calendars; currently, only **rem2pdf** + and **rem2html** do. Specifying a weekly calendar implicitly enables + the pure JSON interchange format, similar to **-ppp**. + + The format of the **-p** output is described in the **rem2ps(1)** + man page. If you immediately follow the **p** with the letter **a**, + then **Remind** displays reminders on the calendar on the day they + actually occur *as well as* on any preceding days specified by the + reminder\'s *delta*. If you follow the **p** with another **p**, + then **Remind** uses a more comprehensive JSON-based format rather + than the \"simple calendar\" format. This format is also documented + in the **rem2ps(1)** man page. Finally, if you use three p\'s, as in + **-ppp**, then **Remind** uses a pure JSON format, again documented + in **rem2ps(1)**. If you include a **q** letter with this option, + then the usual calendar-mode substitution filter is disabled and the + %\"\...%\" sequences are preserved in the output. + + Note that to pass INFO strings to a back-end, you must use **-pp** + or **-ppp**. The simpler **-p** format is not capable of + transmitting the INFO strings to the back-end. + + The **-p**, **-pp** and **-ppp** options implicitly enable the + **-o** option. + + Note that the **-pp** or **-ppp** options also enable the **-l** + option. + +**-l** + +: If you use the -l option in conjunction with the -p option, then + **Remind** outputs additional information for back-end programs such + as **rem2ps**. This additional information lets the back-end + programs correlate a reminder with the source file and line number + that produced it. + +**-m** + +: The **-m** option causes the **-c** or **-p** options to produce a + calendar whose first column is Monday rather than Sunday. (This + conforms to the international standard.) + +**-v** + +: The **-v** option makes the output of **Remind** slightly more + verbose. Currently, this causes **Remind** to echo a bad line in + case of an error, and to print a security message if a script tests + the \$RunOff system variable. + +**-o** + +: The **-o** option causes **Remind** to ignore all **ONCE** + directives. Note that **ONCE** is also ignored if any of the **-c**, + **-n**, **-p**, or **-s** options is used, if a repetition factor + **\*n** is used, or if a date other than today\'s date is specified + on the command-line. + +**-t** + +: The **-t** option causes **Remind** to trigger all non-expired + reminders, regardless of the *delta* supplied for each reminder. + +**-tn** + +: If you supply a number *n* after the **-t** option, then **Remind** + pretends that every **REM** command has a delta of ++*n*, regardless + of any existing delta. + +**-tz** + +: If you supply the letter **z** after the **-t** option, then + **Remind** sets all REM statements\' deltas to zero, regardless of + the value supplied in the REM statement itself. In effect, this + disables all deltas of the form **+**_n_ and **++**_n_. + +**-tt\[*n*\]** + +: The **-tt** option causes **Remind** to assume a default delta of + *n* minutes for all timed reminders. If **-tt** is given with no + *n*, a default delta of 5 minutes is used. + +**-h** + +: The **-h** option (\"hush\...\") suppresses certain warning and + information messages. In particular, if no reminders are triggered, + this mode produces no output. + +**-a** + +: The **-a** option causes **Remind** not to immediately trigger timed + reminders that trigger on the current day. It also causes **Remind** + not to place timed reminders in a calendar. If you supply two or + more **-a** options, then **Remind** *will* trigger timed reminders + that are in the future, but will not trigger timed reminders whose + time has passed. (Regardless of how many **-a** options you supply, + **Remind** will not include timed reminders in the calendar if at + least one **-a** option is used.) + +```{=html} + +``` + +**-q** + +: The **-q** option causes **Remind** not to queue timed reminders for + later execution. + +**-f** + +: The **-f** option causes **Remind** to remain in the foreground when + processing queued reminders, rather than forking off a background + process to handle them. + +**-e** + +: The **-e** option diverts error messages (normally sent to the + standard error stream) to the standard output stream. + +**-d**_chars_ + +: The **-d** option enables certain debugging modes. The *chars* + specify which modes to enable: + + **e** + + : Echo all input lines + + **x** + + : Trace all expression evaluation + + **t** + + : Display all trigger date computation + + **v** + + : Dump the variable table after execution of the reminder script + + **l** + + : Echo lines when displaying error messages + + **f** + + : Trace the reading of reminder files + + **s** + + : Trace expression parsing and display the internal expression + node tree. This is unlikely to be useful unless you are working + on **Remind**\'s expression evaluation engine. + + **h** + + : Dump hash-table statistics on exit. + + **u** + + : When **Remind** exits, print a list of variables that were SET, + but not subsequently used. + + Note that the **u** debugging flag may produce spurious + warnings. For example, this sequence of commands: + + DEBUG +u + SET a 1 + IFTRIG Wed + MSG a = [a] + ENDIF + + will issue a warning about **a** being unused unless it is run + on a Wednesday. + + **n** + + : Print debugging information about why **Remind** considers an + expression to be \"non-constant\" + + **q** + + : Output a TRANSLATE command each time the **\_()** built-in + function is called or the **%(\...)** substitution sequence is + encountered. + +**-g**\[**a\|d**\[**a\|d**\[**a\|d**\[**a\|d**\]\]\]\] + +: Normally, reminders are issued in the order in which they are + encountered in the reminder script. The **-g** option cause + **Remind** to sort reminders by date and time prior to issuing them. + The optional **a** and **d** characters specify the sort order + (ascending or descending) for the date, time and priority fields. + See the section \"SORTING REMINDERS\" for more information. + + Note that **-g** is *ignored* if you use the **-n** option. + +**-b**\[*n*\] + +: Set the time format for the calendar and simple-calendar outputs. + *N* can range from 0 to 2, with the default 0. A value of 0 causes + times to be inserted in 12-hour (am/pm) format. 1 causes times to be + inserted in 24-hour format, and 2 inhibits the automatic insertion + of times in the calendar output. + +**-x**\[*n*\] + +: Sets the iteration limit for the **SATISFY** clause of a **REM** + command. Defaults to 1000. + +**-k**_cmd_ + +: Instead of simply printing **MSG**-type reminders, this causes them + to be passed to the specific *cmd*. You must use \'%s\' where you + want the body to appear, and may need to enclose this option in + quotes. Note that all shell characters in the body of the reminder + are escaped with a backslash, and the entire body of the reminder is + passed as a single argument. Note that this option **overrides** the + **-r** option and the **RUN OFF** command. + +> As an example, suppose you have an X Window program called +> **xmessage** that pops up a window and displays its invocation +> arguments. You could use: +> +> remind '-kxmessage %s &' ... +> +> to have all of your **MSG**-type reminders processed using xmessage. +> +> A word of warning: It is very easy to spawn dozens of xmessage +> processes with the above technique. So be very careful. Because all +> shell and whitespace characters are escaped, the program you execute +> with the **-k** option must be prepared to handle the entire message +> as a single argument. +> +> If you follow the **-k** option with a colon, then the command is +> applied only to queued timed reminders. Normal reminders are handled +> as usual. In the above example, if you want normal reminders to simply +> be displayed as usual, but queued reminders to be sent to notify-send, +> you could use: +> +> remind '-k:notify-send %s &' ... +> +> You use both **-k**_cmd1_ and **-k:**_cmd2_ to use different commands +> for queued versus non-queued reminders. + +**-z**\[*n*\] Runs **Remind** in \"daemon mode\". If *n* + +: is supplied, it specifies how often (in minutes) **Remind** should + wake up to check if the reminder script has been changed. *N* + defaults to 1, and can range from 1 to 60. Note that the use of the + **-z** option also enables the **-f** option. + +> If **Remind** is compiled on a system that supports **inotify**(7), +> then if the reminder script supplied on the command-line is actually a +> directory, **Remind** additionally checks if all files within that +> directory have been modified since startup. +> +> If you supply the option **-zj**, **Remind** runs in a special mode +> called **server mode**. This is documented in the tkremind man page; +> see tkremind(1). The older server mode option **-z0** still works, but +> is deprecated; it uses an ad-hoc method to communicate with the client +> rather than using JSON to communicate with the client. + +**-u**_name_ + +: Runs **Remind** with the uid and gid of the user specified by + *name*. The option changes the uid and gid as described, and sets + the environment variables HOME, SHELL and USER to the home + directory, shell, and user name, respectively, of the specified + user. LOGNAME is also set to the specified user name. This option is + meant for use in shell scripts that mail reminders to all users. + Note that as of **Remind** 3.00.17, using **-u** implies **-r** \-- + the RUN directive and shell() functions are disabled. However, if + you prefix *name* with a **+**-sign, then RUN and shell() are *not* + disabled. That is, **-uwhatever** switches the user to **whatever** + and disables RUN, whereas **-u+whatever** switches the user to + **whatever** but leaves RUN enabled. + +> Non-root users can also use the **-u** option. However, in this case, +> it only changes the environment variables as described above. It does +> not change the effective uid or gid. + +**-+**_username_ + +: Causes **Remind** to trust files owned by the user *username*. + Normally, if **Remind** reads a file that you do not own, it + disables RUN and the shell() function. This option causes it to also + trust files owned by *username*. You can supply multiple **-+** + options to trust multiple users, up to a limit of 20 trusted users. + +**-y** + +: Causes **Remind** to synthesize a tag for any reminder that lacks a + TAG clause. + +**-i**_var=expr_ + +: Sets the value of the specified *var* to *expr*, and **preserves** + *var*. *Expr* can be any valid **Remind** expression. See the + section \"INITIALIZING VARIABLES ON THE COMMAND LINE\" for more + details. If you omit the **=**_expr_ part, then *var* is initialized + to 0. In other words, **-i**_var_ is exactly the same as + **-i**_var=0_. + +**-i**_func(args)=definition_ + +: Allows you to define a function on the command line. + +If you supply a *date* on the command line, it must consist of *day +month year*, where *day* is the day of the month, *month* is at least +the first three letters of the English name of the month, and *year* is +a year (all 4 digits) from 1990 to about 2075. You can leave out the +*day*, which then defaults to 1. + +If you do supply a *date* on the command line, then **Remind** uses it, +rather than the actual system date, as its notion of \"today.\" This +lets you create calendars for future months, or test to see how your +reminders will be triggered in the future. Similarly, you can supply a +*time* to set **Remind**\'s notion of \"now\" to a particular time. +Supplying a *time* on the command line also implicitly enables the +**-q** option and disables the **-z** option. The *time* may be +specified in 24-hour format (e.g., 13:20) or common \"AM/PM\" format +(1:20pm). + +If you would rather specify the date more succinctly, you can supply it +as YYYY-MM-DD or YYYY/MM/DD. You can even supply a date and time on the +command line as one argument: YYYY-MM-DD@HH:MM. + +In addition, you can supply a *repeat* parameter, which has the form +\**rep*. This causes **Remind** to be run *rep* times, with the date +incrementing on each iteration. You may have to enclose the parameter in +quotes to avoid shell expansion. See the subsection \"Repeated +Execution\" in the section \"CALENDAR MODE\" for more information. + +# LONG OPTIONS + +**Remind** supports the following long options, which *are* +case-sensitive: + +**\--version** + +: The **\--version** option causes **Remind** to print its version + number to standard output and then exit. + +**\--hide-completed-todos** + +: In Calendar Mode, **Remind** normally shows all TODOs. If you supply + this option, the it will not show TODOs that have been marked as + completed. + +**\--only-todos** + +: Only issue TODO-type reminders. + +**\--only-events** + +: Do not issue TODO-type reminders. + +**\--json** + +: In Agenda Mode, output JSON instead of the normal text-mode output. + **\--json** also disables sorting (the **-g** option) and disables + queueing (the **-q** option). See the section \"AGENDA MODE JSON + OUTPUT\" for more details. The **\--json** option is ignored in + Calendar Mode. Note that in JSON mode, the output from any + **RUN**-type reminder that would normally appear on standard output + is redirected to standard error instead; this is so that + **RUN**-type reminders don\'t mess up the output and cause invalid + JSON to be produced on standard output. + +**\--print-errs** + +: The **\--print-errs** option causes **Remind** to print all possible + error messages to standard output and then exit. The messages are + printed in a format suitable for the first argument of a TRANSLATE + command. If you TRANSLATE the error messages, then **Remind** will + use the translated versions when outputting error and warning + messages. See also TRANSLATE GENERATE in the section \"THE + TRANSLATION TABLE\". + + Note that if an untranslated message contains printf-style + formatting sequences like \"%s\" or \"%d\", then the translated + message *must* contain the same sequences in the same order, or + **Remind** will ignore it and use the original untranslated message. + +**\--print-config-cmd** + +: This option causes **Remind** to print the exact **./configure** + command that was used when **Remind** was built. You can use this to + build a new version of **Remind** using the same configuration as an + existing one by running: + + eval `remind --print-config-cmd` + + from the top-level **Remind** source directory. (However, it\'s + safer to simply run **remind \--print-config-cmd** and then type in + the command once you\'ve verified that it looks OK.) + +```{=html} + +``` + +**\--print-tokens** + +: The **\--print-tokens** option causes **Remind** to print the tokens + used by the parser, built-in function names, and system variable + names to standard output and then exit. This output is designed to + make it easy to create a syntax-highlighting file for various text + editors. The output can be modified by hand or by a script into a + syntax-highlighting file with relative ease. + +**\--max-execution-time**=_n_ + +: Limit the total execution time (as measured by the wall clock) to + *n* seconds. This is useful if **Remind** is invoked on + potentially-untrustworthy files that could attempt to use a lot of + resources. Note that the limit *n* is approximate and **Remind** + might execute for one or two more seconds before it is killed. If + *n* is specified as zero, then no limit is applied, just as if the + option had not been used at all. + + If a limit is applied, it applies only to the foreground run of + **Remind**. If **Remind** finishes processing the script and then + starts handling queued reminders, the time limit is reset to no + limit. + +**\--max-expr-complexity**=_n_ + +: Limit the total complexity of expression valuation for a given line + in a script to *n* nodes. Roughly speaking, each function call, + operator, constant, variable reference, etc corresponds to one + expression node. By default, the limit is set to 10000000 (ten + million). You can explicitly set it to zero if you don\'t want any + limit to apply. The default limit of ten million should never be + triggered by any sensible Remind script, however, and we don\'t + recommend changing the limit. + +**\--test** + +: The **\--test** long option is only for use by the acceptance tests + run by \"make test\". Do not use this option in production. + +**\--flush** + +: The **\--flush** long option makes **Remind**\'s standard output and + standard error streams unbuffered. It is mostly used for testing and + should probably not be used in production. + +# REMINDER FILES + +**Remind** uses scripts to control its operation. You can use any text +editor capable of creating plain-text files to create a **Remind** +script. The commands inside a script can range from the very simple and +almost immediately understandable: + + REM Mar 31 MSG International Transgender Day of Visibility + +to the baroque and obscure: + + REM [date(thisyear, 1, 1) + 180] ++5 OMIT \ + sat sun BEFORE MSG [ord(thisyear-1980)] payment due %b! + +A reminder file consists of commands, with one command per line. Several +lines can be continued using the backslash character, as in the above +example. In this case, all of the concatenated lines are treated as a +single line by **Remind**. Note that if an error occurs, **Remind** +reports the line number of the last line of a continued line. + +**Remind** ignores blank lines, and lines beginning with the \'#\' or +\';\' characters. You can use the semicolon as a comment character if +you wish to pass a **Remind** script through the C pre-processor, which +interprets the \'#\' character as the start of a pre-processing +directive. + +Note that **Remind** processes line continuations before anything else. +For example: + + # This is a comment \ + This line is part of the comment because of line continuation \ + and so on. + REM MSG This line is not ignored (no \ above) + +**Remind** is not case sensitive; you can generally use any mixture of +upper- or lower-case for commands, parameters, invocation options, etc. + +# THE REM COMMAND + +The most powerful command in a **Remind** script is the **REM** command. +This command is responsible for issuing reminders. Its syntax is: + +> **REM** \[**ONCE**\] \[*date_spec*\] \[*back*\] \[*delta*\] +> \[*repeat*\] \[**TODO**\] \[**MAX-OVERDUE** *n*\] +> \[**COMPLETE-THROUGH** *complete_date*\] \[**PRIORITY** *prio*\] +> \[**SKIP** \| **BEFORE** \| **AFTER**\] \[**OMIT** *omit_list*\] +> \[**ADDOMIT**\] \[**NOQUEUE**\] \[**OMITFUNC** *omit_function*\] +> \[**AT** *time* \[*tdelta*\] \[*trepeat*\]\] \[**SCHED** +> *sched_function*\] \[**WARN** *warn_function*\] \[**UNTIL** +> *expiry_date* \| **THROUGH** *last_date*\] \[**SCANFROM** *scan_date* +> \| **FROM** *start_date*\] \[**DURATION** *duration*\] \[**TAG** +> *tag*\] \[**TZ** *timezone*\] \[**INFO** \"*info_string*\"\] **MSG** +> \| **MSF** \| **RUN** \| **CAL** \| **SATISFY** \| **SPECIAL** +> *special* \| **PS** \| **PSFILE** *body* + +The parts of the **REM** command can be specified in any order, except +that the *body* must come immediately after the **MSG**, **RUN**, +**CAL**, **PS**, **PSFILE** or **SATISFY** keyword. The portion of the +**REM** command before the **MSG**, **MSF** **RUN**, **CAL** or +**SATISFY** clause is called a *trigger*. + +In earlier versions of **Remind**, the **REM** token was optional +providing that the remainder of the command cannot be mistaken for +another **Remind** command. However, this use is deprecated and will now +cause a warning to be issued. All of your reminder lines should be +written to start with the REM command. + +**MSG, MSF, RUN, CAL, SPECIAL, PS and PSFILE** + +These keywords denote the *type* of the reminder. (**SATISFY** is more +complicated and will be explained later.) A **MSG**-type reminder +normally prints a message to the standard output, after passing the +*body* through a special substitution filter, described in the section +\"THE SUBSTITUTION FILTER.\" However, if you have used the **-k** +command-line option, then **MSG**-type reminders are passed to the +appropriate program. Note that the options **-c**, **-s**, **-p** and +**-n** disable the **-k** option. + +Earlier versions of **Remind** let you omit the reminder type, in which +case it defaulted to **MSG**. However, this usage is deprecated and will +cause a warning. Something like: + + REM 6 January Dianne's Birthday + +will issue the warning \"Missing REM type; assuming MSG\" + +The **MSF** keyword is almost the same as the **MSG** keyword, except +that the reminder is formatted to fit into a paragraph-like format. +Three system variables control the formatting of **MSF**-type +reminders - they are **\$FirstIndent**, **\$SubsIndent** and +**\$FormWidth**. They are discussed in the section \"SYSTEM VARIABLES.\" +The **MSF** keyword causes the spacing of your reminder to be altered - +extra spaces are discarded, and two spaces are placed after periods and +other characters, as specified by the system variables **\$EndSent** and +**\$EndSentIg**. Note that if the body of the reminder includes newline +characters (placed there with the %\_ sequence), then the newlines are +treated as the beginnings of new paragraphs, and the **\$FirstIndent** +indentation is used for the next line. You can use two consecutive +newlines to have spaced paragraphs emitted from a single reminder body. + +A **RUN**-type reminder also passes the *body* through the substitution +filter, but then executes the result as a system command. A **CAL**-type +reminder is used only to place entries in the calendar produced when +**Remind** is run with the **-c**, **-s** or **-p** options. When +**Remind** runs a command, it sets the command\'s standard input to come +from /dev/null. + +A **PS** or **PSFILE**-type reminder is used to pass PostScript code +directly to the printer when producing PostScript calendars. This can be +used to shade certain calendar entries (see the psshade() function), +include graphics in the calendar, or almost any other purpose you can +think of. You should not use these types of reminders unless you are an +expert PostScript programmer. The **PS** and **PSFILE** reminders are +ignored unless **Remind** is run with the **-p** option. See the section +\"More about PostScript\" for more details. + +A **SPECIAL**-type reminder is used to pass \"out-of-band\" information +from **Remind** to a calendar-producing back-end. It should be followed +by a word indicating the type of special data being passed. The type of +a special reminder depends on the back-end. For the **Rem2PS** back-end, +**SPECIAL PostScript** is equivalent to a **PS**-type reminder, and +**SPECIAL PSFile** is equivalent to a **PSFILE**-type reminder. The body +of a **SPECIAL** reminder is obviously dependent upon the back-end. A +back-end *must* ignore a **SPECIAL** that it does not recognize. + +**DATE SPECIFICATIONS** + +A *date_spec* consists of zero to four parts. These parts are *day* (day +of month), *month* (month name), *year* and *weekday.* *Month* and +*weekday* are the English names of months and weekdays. At least the +first three characters must be used. The following are examples of the +various parts of a *date_spec:* + +*day:* + +: 1, 22, 31, 14, 3 + +*month:* + +: JANUARY, feb, March, ApR, may, Aug + +*year:* + +: 1990, 1993, 2030. The year can range from 1990 to 2075. + +*weekday:* + +: Monday, tue, Wed, THU, Friday, saturday, sundAy + +Note that there can be several *weekday* components separated by spaces +in a *date_spec.* + +**INTERPRETATION OF DATE SPECIFICATIONS** + +The following examples show how date specifications are interpreted. + +1\. Null date specification - the reminder is triggered every day. The +trigger date for a specific run is simply the current system date. For +example: + + REM MSG This is triggered every time Remind runs + +2\. Only *day* present. The reminder is triggered on the specified day +of each month. The trigger date for a particular run is the closest such +day to the current system date. For example: + + REM 1 MSG First of every month. + REM 31 MSG 31st of every month that has 31 days. + +3\. Only *month* present. The reminder is triggered every day of the +specified month. Example: + + REM Feb MSG Every day in February + +4\. *day* and *month* present. Examples: + + REM 6 Jan MSG Every 6th of January + REM Feb 29 MSG Every 29th of February + +5\. Only *year* present. Example: + + REM 1991 MSG Every day in 1991 + +6\. *year* and *day* present. Examples: + + REM 1 1990 MSG 1st of every month in 1990 + REM 1992 23 MSG 23rd of every month in 1992 + +7\. *year* and *month* present. Examples: + + REM Feb 1991 MSG Every day in Feb 1991 + REM 1992 September MSG Every day in Sept 1992 + +8\. *year, month* and *day* present. Examples: + + REM 8 Jan 1991 MSG 8th January 1991. + REM 1992 March 9 MSG 9th March 1992. + +9\. *weekday* only. Examples: + + REM Sat MSG Every Saturday + REM Mon Tue Wed Thu Fri MSG Every working day + REM Monday Wednesday MSG Every Monday and Wednesday + +10\. *weekday* and *day* present. Examples: + + REM Sat 1 MSG First Saturday of every month + REM Mon Tue Wed Thu Fri 15 \ + MSG 1st working day on or after 15th of every month + +11\. *weekday* and *month* present. Examples: + + REM Mon March MSG Every Monday in March + REM Mon Tue Wed Thu Fri Feb MSG Every working day in February + +12\. *weekday, month* and *day* present. Examples: + + REM Mon 1 March MSG First Monday in March + REM Sat Sun 15 July MSG First Sat or Sun on or after 15 July + +13\. *weekday* and *year* present. Example: + + REM Sat Sun 1991 MSG Every Saturday and Sunday in 1991 + +14\. *weekday, day* and *year* present. Examples: + + REM Mon 15 1990 MSG 1st Mon after 15th of every month in 1990 + REM Mon Tue Wed Thu Fri 1 1990 \ + MSG 1st working day of every month in 1990 + +15\. *weekday, month* and *year* present. Example: + + REM Mon Wed 1991 Feb MSG Every Mon and Wed in Feb 1991. + +16\. *weekday, day, month* and *year* present. Example: + + REM Mon Tue Wed Thu Fri 28 Oct 1990 \ + MSG 1st working day on or after 28 October 1990. + +Note that when both *weekday* and *day* are specified, **Remind** +chooses the first date on or after the specified *day* that also +satisfies the *weekday* constraint. It does this by picking the first +date on or after the specified *day* that is listed in the list of +*weekdays.* Thus, a reminder like: + + REM Mon Tue 28 Oct 1990 MSG Hi + +would be issued only on Monday, 29 October, 1990. It would not be issued +on Tuesday, 30 October, 1990, since the 29th is the first date to +satisfy the *weekday* constraints. + +**SHORT-HAND DATE SPECIFICATIONS** + +In addition to spelling out the day, month and year separately, you can +specify YYYY-MM-DD or YYYY/MM/DD. For example, the following statements +are equivalent: + + REM 5 June 2010 MSG Cool! + REM 2010-06-05 MSG Cool! + +You can also specify a date and time as YYYY-MM-DD@HH:MM. These +statements are equivalent: + + REM 19 Dec 2010 AT 16:45 MSG Hi + REM 2010-12-19@16:45 MSG Hi + +There\'s one subtlety with short-hand date specifications: The following +statements are *not* equivalent: + + REM 19 Dec 2010 AT 16:45 +60 MSG Hi + REM 2010-12-19@16:45 +60 MSG Hi + +In the second statement, the \"+60\" is a *delta* that applies to the +date rather than a *tdelta* that applies to the time. We recommend +explicitly using the AT keyword with timed reminders. + +**THE REMIND ALGORITHM** + +**Remind** uses the following algorithm to compute a trigger date: +Starting from the current date, it examines each day, one at a time, +until it finds a date that satisfies the date specification, or proves +to itself that no such date exists. (Actually, **Remind** merely +*behaves* as if it used this algorithm; it would be much too slow in +practice. Internally, **Remind** uses much faster techniques to +calculate a trigger date.) See DETAILS ABOUT TRIGGER COMPUTATION for +more information. + +**BACKWARD SCANNING** + +Sometimes, it is necessary to specify a date as being a set amount of +time before another date. For example, the last Monday in a given month +is computed as the first Monday in the next month, minus 7 days. The +*back* specification in the reminder is used in this case: + + REM Mon 1 -7 MSG Last Monday of every month. + +A *back* is specified with one or two dashes followed by an integer. +This causes **Remind** to move \"backwards\" from what would normally be +the trigger date. The difference between \--7 and -7 will be explained +when the **OMIT** keyword is described. + +**ADVANCE WARNING** + +For some reminders, it is appropriate to receive advance warning of the +event. For example, you may wish to be reminded of someone\'s birthday +several days in advance. The *delta* portion of the **REM** command +achieves this. It is specified as one or two \"+\" signs followed by a +number *n*. Again, the difference between the \"+\" and \"++\" forms +will be explained under the **OMIT** keyword. **Remind** will trigger +the reminder on computed trigger date, as well as on each of the *n* +days before the event. Here are some examples: + + REM 6 Jan +5 MSG Remind me of birthday 5 days in advance. + +The above example would be triggered every 6th of January, as well as +the 1st through 5th of January. + +**PERIODIC REMINDERS** + +We have already seen some built-in mechanisms for certain types of +periodic reminders. For example, an event occurring every Wednesday +could be specified as: + + REM Wed MSG Event! + +However, events that do not repeat daily, weekly, monthly or yearly +require another approach. The *repeat* component of the **REM** command +fills this need. To use it, you must completely specify a date (year, +month and day, and optionally weekday); this is the start date of the +repetition period. The *repeat* component is an asterisk followed by a +number specifying the repetition period in days. + +For example, suppose you get paid every second Wednesday, and your last +payday was Wednesday, 28 October, 1992. You can use: + + REM 28 Oct 1992 *14 MSG Payday + +This issues the reminder every 14 days, starting from 28 Oct 1992. You +can use *delta* and *back* with *repeat.* Note, however, that the *back* +is used only to compute the starting date; thereafter, the reminder +repeats with the specified period. Similarly, if you specify a weekday, +it is used only to calculate the starting date, and does not affect the +repetition period. + +**SCANFROM and FROM** + +The **SCANFROM** and **FROM** keywords are for advanced **Remind** +programmers only, and will be explained in the section \"DETAILS ABOUT +TRIGGER COMPUTATION\" near the end of this manual. Note that +**SCANFROM** is available only in versions of **Remind** from 03.00.04 +up. **FROM** is available only from 03.01.00 and later. + +**PRIORITY** + +The **PRIORITY** keyword must be followed by a number from 0 to 9999. It +is used in calendar mode and when sorting reminders. If two reminders +have the same trigger date and time, then they are sorted by priority. +If the **PRIORITY** keyword is not supplied, a default priority of 5000 +is used. (This default can be changed by adjusting the system variable +**\$DefaultPrio**. See the section \"SYSTEM VARIABLES\" for more +information.) + +**EXPIRY DATES** + +Some reminders should be issued periodically for a certain time, but +then expire. For example, suppose you have a class every Friday, and +that your last class is on 11 December 1992. You can use: + + REM Fri UNTIL 11 Dec 1992 MSG Class today. + +Another example: Suppose you have jury duty from 30 November 1992 until +4 December 1992. The following reminder will issue the message every day +of your jury duty, as well as 2 days ahead of time: + + REM 1992-11-30 *1 +2 UNTIL 1992-12-04 MSG Jury duty + +Note that the *repeat* of \*1 is necessary; without it, the reminder +would be issued only on 30 November (and the two days preceding.) + +As a special case, you can use the **THROUGH** keyword instead of \*1 +and **UNTIL**. The following two **REM** commands are equivalent: + + REM 1992-11-30 *1 +2 UNTIL 1992-12-04 MSG Jury duty + + REM 1992-11-30 +2 THROUGH 1992-12-04 MSG Jury duty + +If you have an expiry date via the use of **THROUGH** or **UNTIL**, then +**Remind** will *never* trigger the reminder after the expiry date. For +example, if you have this: + + OMIT 2021-01-08 + REM 2021-01-01 THROUGH 2021-01-08 AFTER MSG Test + +the reminder will not be triggered on 2021-01-08, and nor will it be +triggered on 2021-01-09; even though the AFTER keyword would normally +move the 8th\'s reminder to the 9th, the expiry date of 2021-01-08 +overrides that. + +**THE ONCE KEYWORD** + +Sometimes, it is necessary to ensure that reminders are run only once on +a given day. For example, compare the following two reminders: + + REM Fri RUN do_backup + REM Fri ONCE RUN do_backup + +The first will be run every time you invoke **Remind** on a Friday, +whereas the second will be run only the first time you invoke **Remind** +on a given Friday. + +If you run **Remind** from your .login script, for example, and log in +several times per day, the *do_backup* program in the first reminder +will be run each time you log in. If, however, you use the **ONCE** +keyword in the reminder, the **Remind** checks the last access date of +the reminder script. If it is the same as the current date, **Remind** +assumes that it has already been run, and will not issue reminders +containing the **ONCE** keyword. + +Note that if you view or edit your reminder script, the last access date +will be updated, and the **ONCE** keyword will not operate properly. You +can fix this by setting a timestamp file for **Remind** to track the +last-run date; see the documentation of **\$OnceFile** in the **SYSTEM +VARIABLES** section. If you use standard input as your **Remind** input +file, then you *must* use **\$OnceFile** for the **ONCE** keyword to +work properly. + +If you start **Remind** with the **-o** option, then the **ONCE** +keyword will be ignored and any **\$OnceFile** will be ignored. + +**LOCALLY OMITTING WEEKDAYS** + +The **OMIT** portion of the **REM** command is used to \"omit\" certain +days when counting the *delta* or *back*. It is specified using the +keyword **OMIT** followed by a list of weekdays. Its action is best +illustrated with examples: + + REM 1 +1 OMIT Sat Sun MSG Important Event + +This reminder is normally triggered on the first of every month, as well +as the day preceding it. However, if the first of the month falls on a +Sunday or Monday, then the reminder is triggered starting from the +previous Friday. This is because the *delta* of +1 does not count +Saturday or Sunday when it counts backwards from the trigger date to +determine how much advance warning to give. + +Contrast this with the use of \"++1\" in the above command. In this +case, the reminder is triggered on the first of each month, as well as +the day preceding it. The omitted days are counted. + + REM 1 -1 OMIT Sat Sun MSG Last working day of month + +Again, in the above example, the *back* of -1 normally causes the +trigger date to be the last day of the month. However, because of the +**OMIT** clause, if the first of the month falls on a Sunday or Monday, +the trigger date is moved backwards past the weekend to Friday. (If you +have globally omitted holidays, the reminder will be moved back past +them, also. See \"The OMIT command\" for more details.) + +By comparison, if we had used \"\--1\", the reminder would be triggered +on the last day of the month, regardless of the **OMIT**. + +If you locally omit weekdays but also have globally-omitted weekdays, +then the list of omitted weekdays is the union of the two. Consider this +example: + + OMIT Sat Sun + REM 15 OMIT Fri Sat MSG Whatever + +In the REM command, the effective list of omitted weekdays will be +Friday, Saturday and Sunday. + +**COMPUTED LOCAL OMITS** + +The **OMITFUNC** phrase of the **REM** command allows you to supply a +function that determines whether or not a date is omitted. Note that +**OMITFUNC** must be given just the name of a user-defined function; it +can\'t take an arbitrary expression or the name of a built-in function. + +The function is passed a single parameter of type **DATE**, and must +return a non-zero integer if the date is considered \"omitted\" and 0 +otherwise. Here\'s an example: + + FSET _third(x) (day(x) % 3) || \ + (wkdaynum(x) == 0) || \ + (wkdaynum(x) == 6) + REM OMITFUNC _third AFTER MSG Working day divisible by 3 + +In the example above, the reminder is triggered every Monday to Friday +whose day-of-month number is divisible by three. Here\'s how it works: + +**o** + +: The **OMITFUNC \_third** portion causes all days for which + **\_third(x)** returns non-zero to be considered \"omitted\". This + causes all days whose day-of-month number is *not* a multiple of + three to be omitted. Note that \_third also returns non-zero if the + weekday is Sunday or Saturday. + +**o** + +: The **AFTER** keyword causes the reminder to be moved after a block + of omitted days. + +The combination of OMITFUNC and AFTER keyword causes the reminder to be +issued on all days whose day-of-month number is divisible by three, but +not on Saturday or Sunday. + +Note that if you use **OMITFUNC**, then a local **OMIT** is *ignored* as +are *all global OMITs*. If you want to omit specific weekdays, your omit +function will need to test for them specifically. If you want to take +into account the global **OMIT** context, then your omit function will +need to test for that explicitly (using the **isomitted()** function.) + +Note that an incorrect **OMITFUNC** might cause all days to be +considered omitted. For that reason, when **Remind** searches through +omitted days, it terminates the search after the **SATISFY** iteration +limit (command-line option **-x**.) + +**ADDING TRIGGER DATES TO THE OMIT CONTEXT** + +If the **ADDOMIT** keyword appears in a **REM** command, then the +trigger date (if one could be calculated) is automatically added to the +list of global OMITs. + +The command: + + REM ... whatever ... ADDOMIT MSG Foo + +is identical in behavior to the sequence: + + REM ... whatever ... SATISFY 1 + IF trigvalid() + OMIT [trigdate()] MSG Foo + ENDIF + +**TIMED REMINDERS** + +Timed reminders are those that have an **AT** keyword followed by a +*time* and optional *tdelta* and *trepeat*. The *time* may be specified +in 24-hour format, with 0:00 representing midnight, 12:00 representing +noon, and 23:59 representing one minute to midnight. Alternatively, it +may be specified in common \"AM/PM\" format; in this case, the hour must +range from 1 to 12. 12:00am represents midnight, 12:00pm represents +noon, and 11:59pm represents one minute to midnight. The \"am\" and +\"pm\" portions are case-insensitive and the \"m\" is optional. + +You can use either a colon or a period to separate the hours from the +minutes. That is, 13:39 and 13.39 are equivalent. + +**Remind** treats timed reminders specially. If the trigger date for a +timed reminder is the same as the current system date, the reminder is +queued for later activation. When **Remind** has finished processing the +reminder file, it puts itself in the background, and activates timed +reminders when the system time reached the specified time. Note that if +you use the **NOQUEUE** modifier in the **REM** command, then this +queuing and background activation is *not* performed. **NOQUEUE** is +useful if you want a time to be associated with a reminder (e.g., in the +calendar) but are not interested in a popup reminder happening at the +specified time. + +If the trigger date is *not* the same as the system date, the reminder +is not queued. + +For example, the following reminder, triggered every working day, will +emit a message telling you to leave at 5:00pm: + + REM Mon Tue Wed Thu Fri AT 17:00 MSG Time to leave! + +The following reminder will be triggered on Thursdays and Fridays, but +will only be queued on Fridays: + + REM Fri ++1 AT 1:00PM MSG Lunch at 1pm Friday. + +The *tdelta* and *trepeat* have the same form as a *repeat* and *delta*, +but are specified in minutes. For example, this reminder will be +triggered at 12:00pm as well as 45 minutes before: + + REM AT 12:00 +45 MSG Example + +The following will be issued starting at 10:45, every half hour until +11:45, and again at noon. + + REM AT 12:00 +75 *30 MSG Example2 + +The \"+75\" means that the reminder is issued starting 75 minutes before +noon; in other words, at 10:45. The \*30 specifies that the reminder is +subsequently to be issued every 30 minutes. Note that the reminder is +always issued at the specified time, even if the *tdelta* is not a +multiple of the *trepeat*. So the above example is issued at 10:45am, +11:15am, 11:45am, and 12:00pm. Note that in the time specification, +there is no distinction between the \"+\" and \"++\" forms of *tdelta*. + +Normally, **Remind** will issue timed reminders as it processes the +reminder script, as well as queuing them for later. If you do not want +**Remind** to issue the reminders when processing the script, but only +to queue them for later, use the **-a** command-line option. If you do +not want reminders to be queued for later, use the **-q** command-line +option. + +Normally, **Remind** forks a background process to handle queued +reminders. If you want **Remind** to remain in the foreground, use the +**-f** command-line option. This is useful, for example, in .xinitrc +scripts, where you can use the command: + + remind -fa myreminders & + +This ensures that when you exit X-Windows, the **Remind** process is +killed. + +**WARNING ABOUT TIMED REMINDERS** + +Note: If you use user-defined functions or variables (described later) +in the bodies of timed reminders, then when the timed reminders are +activated, the variables and functions have the definitions that were in +effect at the end of the reminder script. These definitions may *not* +necessarily be those that were in effect at the time the reminder was +queued. In addition, the OMIT context is whatever was in effect at the +end of the reminder script, which may not necessarily be the same as +when the **REM** command was first processed. + +**THE SCHED AND WARN KEYWORDS** + +The **SCHED** keyword allows more precise control over the triggering of +timed reminders, and the **WARN** keyword allows precise control over +the advance triggering of all types of reminders. However, discussion +must be deferred until after expressions and user-defined functions are +explained. See the subsection \"PRECISE SCHEDULING\" further on. + +**TAG, INFO AND DURATION** + +The **TAG** keyword lets you \"tag\" certain reminders. This facility is +used by certain back-ends or systems built around **Remind**, such as +**TkRemind**. These back-ends have specific rules about tags; see their +documentation for details. + +The **TAG** keyword is followed by a tag consisting of up to 48 +characters. You can have as many TAG clauses as you like in a given REM +statement. A tag can contain any character except for whitespace and a +comma. + +If you supply the **-y** option to **Remind**, then any reminder that +lacks a **TAG** will have one synthesized. The synthesized tag consists +of the characters \"\_\_syn\_\_\" followed by the hexadecimal +representation of the MD5 sum of the REM command line. This lets you +give a more-or-less unique identifier to each distinct REM command. + +The **INFO** keyword is similar to **TAG** but is intended to convey +metadata about an event, such as its location. Back-ends will have their +own rules about which *info_string*s they recognize, and must ignore +*info_string*s they don\'t recognize. Note that **INFO** must be +followed by a quoted string; you can include newlines in the string by +supplying them as \"\\n\". + +An INFO string *must* be of the form \"Header: Value\". The header can +consist of any printable character, but cannot contain whitespace. The +value can consist of any characters you like. Space may not appear +before the colon, but can appear afterwards; such space is not +considered to be part of the value. If there is more than one INFO +string for a given reminder, there cannot be any duplicate headers. Case +is ignored when determining if a header is a duplicate of an existing +one. + +For example, a hypothetical back-end might let you set the location and +description of a reminder like this: + + REM 26 Feb 2025 INFO "Location: Boardroom #2" \ + INFO "Description: Go over latest pull requests" \ + MSG Engineering meeting + +While back-ends can choose which INFO strings to support, all back-ends +should endeavor to support three standard ones: **Location:**, +**Description:** and **Url:**. TkRemind supports all three of these, +turning reminders with a **Url:** INFO string into hyper-links, and +popping up information windows for the **Location:** and +**Description:** INFO strings. **Location:** and **Url:** are +self-explanatory; **Description:** is meant for a longer, more in-depth +description of the reminder than the summary that is normally displayed. + +The **DURATION** keyword makes sense only for timed reminders; it +specifies the duration of an event. For example, if you have a 90-minute +meeting starting at 1:00pm, you could use any of the following: + + REM 5 March 2021 AT 13:00 DURATION 1:30 MSG Meeting + REM 5 March 2021 AT 13:00 DURATION 90 MSG Meeting + REM 5 March 2021 AT 1:00pm DURATION 1:30 MSG Meeting + REM 5 March 2021 AT 1:00pm DURATION 90 MSG Meeting + +For long-duration reminders, it is convenient to use expressions to +simplify writing the DURATION. For example, if you are away from 20 Feb +2023 through 23 Feb 2023 (a total of 4 days) you could write: + + REM 20 Feb AT 00:00 DURATION [4*24]:00 MSG away + REM 20 Feb AT 00:00 DURATION [4*24*60] MSG away + +Note that *duration* is specified either as *hours*:*minutes* or just as +*minutes* specified as an *integer*. + +If you specify a duration of 00:00 or 0, then **Remind** behaves exactly +as if no **DURATION** at all had been present. Although durations +specified as *hours*:*minutes* look superficially like a time-of-day, +they are not; the *hours* component is not limited to the range 00-23. + +# SYNTACTIC SUGAR FOR REM + +The REM command has syntactic sugar to let you express common reminders. +The following pairs of reminders are equivalent: + + REM First Monday April MSG Foo + REM Mon 1 April MSG Foo + + REM Second Monday May MSG Bar + REM Mon 8 May MSG Bar + + REM Third Monday MSG Third Monday of every month + REM Mon 15 MSG Third Monday of every month + + REM Fourth Sunday June 2025 MSG Fourth Sunday in June 2025 + REM Sun 22 June 2025 MSG Fourth Sunday in June 2025 + + REM Last Monday MSG Last Monday of every month + REM Mon 1 --7 MSG Last Monday of every month + + REM Last Monday April MSG Last Monday of every April + REM Mon 1 May --7 MSG Last Monday of every April + + REM Last Monday December 2025 MSG Last Monday of Dec 2025 + REM Monday 1 Jan 2026 --7 MSG Last Monday of Dec 2025 + +Note that **Last** effectively adjusts the month and year, if necessary, +to make the reminder trigger on the correct date. + +The keyword **IN** is completely ignored, so you can write (for +example): + + REM Second Monday in May MSG foo + REM Last Monday in December 2025 MSG Bar + +An alternate form of *back* makes writing reminders easier. The +following groups of reminders are equivalent: + + REM ~~1 MSG Last day of every month + REM Lastday MSG Last day of every month + REM 1 --1 MSG Last day of every month + + REM May ~~1 MSG Last day of May + REM Lastday May MSG Last day of May + REM 1 June --1 MSG Last day of May + + REM Dec 2025 ~~1 MSG Last day of December 2025 + REM Lastday Dec 2025 MSG Last day of December 2025 + REM 1 Jan 2026 --1 MSG Last day of December 2025 + + REM Apr ~1 OMIT SAT SUN MSG Last workday of April + REM Lastworkday April OMIT SAT SUN MSG Last workday of April + REM 1 May -1 OMIT SAT SUN MSG Last workday of April + + REM Apr ~~7 MSG Seventh-last day of April + REM 1 May --7 MSG Seventh-last day of April + + REM Apr ~2 OMIT SAT SUN MSG Second-last workday of April + REM 1 May -2 OMIT SAT SUN MSG Second-last workday of April + +As we see, \"Lastday\" is equivalent to \~\~1 and \"Lastworkday\" to +\~1. + +Note that the First/Second/Third/Fourth/Last keywords and the \~ and +\~\~ form of *back* imply a value for the day of the month; as such, +they cannot be combined with a day. Additionally, +First/Second/Third/Fourth/Last must have at least one weekday name. The +following are illegal: + + REM First Monday 3 June MSG Huh? + REM April 3 ~~1 MSG What? + REM Second June MSG Where's the weekday??? + +# THE SUBSTITUTION FILTER + +Before being processed, the body of a **REM** command is passed through +a substitution filter. The filter scans for sequences \"%x\" (where +\"x\" is any letter and certain other characters) and performs +substitutions as shown below. (All dates refer to the trigger date of +the reminder.) + +**%a** + +: is replaced with \"on *weekday, day month, year*\" + + For example, consider the reminder: + + REM 18 Oct 1990 +4 MSG Meeting with Bob %a. + + On 16 October 1990, it would print \"Meeting with Bob on Thursday, + 18 October, 1990.\" + + On 17 October 1990, it would print \"Meeting with Bob tomorrow.\" + + On 18 October 1990, it would print \"Meeting with Bob today.\" + +**%b** + +: is replaced with \"in *diff* day\'s time\" where *diff* is the + **actual** number of days between the current date and the trigger + date. (**OMITs** have no effect.) + + For example, consider: + + REM 18 Oct 1990 +4 MSG Meeting with Bob %b. + + On 16 October 1990, it would print \"Meeting with Bob in 2 days\' + time.\" + + On 17 October 1990, it would print \"Meeting with Bob tomorrow.\" + + On 18 October 1990, it would print \"Meeting with Bob today.\" + +**%c** + +: is replaced with \"on *weekday*\" + + Example: REM 18 Oct 1990 +4 MSG Meeting with Bob %c. + + On 16 October 1990, it would print \"Meeting with Bob on Thursday.\" + + On 17 October 1990, it would print \"Meeting with Bob tomorrow.\" + + On 18 October 1990, it would print \"Meeting with Bob today.\" + +**%d** + +: is replaced with \"*day*\", the day of the month. + +**%e** + +: is replaced with \"on *dd-mm-yyyy*\" + +**%f** + +: is replaced with \"on *mm-dd-yyyy*\" + +**%g** + +: is replaced with \"on *weekday, day month*\" + +**%h** + +: is replaced with \"on *dd-mm*\" + +**%i** + +: is replaced with \"on *mm-dd*\" + +**%j** + +: is replaced with \"on *weekday, month day-th, year*\" This form + appends the characters \"st\", \"nd\", \"rd\" or \"th\" to the day + of the month, as appropriate. + +**%k** + +: is replaced with \"on *weekday, month day-th*\" + +**%l** + +: is replaced with \"on *yyyy-mm-dd*\" + +**%m** + +: is replaced with \"*month*\", the name of the month. + +**%n** + +: is replaced with the number (1 to 12) of the month. + +**%o** + +: is replaced with \" (today)\" if and only if the current system date + is the same as the date being used by **Remind** as the current + date. Recall that you can specify a date for **Remind** to use on + the command line. This substitution is not generally useful in a + **REM** command, but is useful in a **BANNER** command. (See \"The + BANNER Command.\") + +**%p** + +: is replaced with \"s\" if the *diff* between the current date and + the trigger date is not 1. You can use this to construct reminders + like: + + REM 1 Jan +4 MSG %x day%p to go before New Year! + +**%q** + +: is replaced with \"\'s\" if the *diff* between the trigger date and + the current date is 1. Otherwise, it is replaced with \"s\'\" This + can be used as follows: + + REM 1 Jan +4 MSG New Year in %x day%q time! + +**%r** + +: is replaced with the day of the month (01 to 31) padded with a + leading zero if needed to pad to two digits. + +**%s** + +: is replaced with \"st\", \"nd\", \"rd\" or \"th\" depending on the + day of the month. + +**%t** + +: is replaced with the number of the month (01 to 12) padded to two + digits with a leading zero. + +**%u** + +: is replaced with \"on *weekday, day-th month, year*\" This is + similar to **%a** except that \"st\", \"nd\", \"rd\" or \"th\" is + added to the *day* as appropriate. + +**%v** + +: is replaced with \"on *weekday, day-th month*\" + +**%w** + +: is replaced with \"*weekday*\", the name of the day of the week. + +**%x** + +: is replaced with the *diff* between the current date and the trigger + date. The *diff* is defined as the actual number of days between + these two dates; **OMITs** are not counted. (Strict date subtraction + is performed.) + +**%y** + +: is replaced with \"*year*\", the year of the trigger date. + +**%z** + +: is replaced with \"*yy*\", the last two digits of the year. + +**%(**_any_text_**)** + +: is replaced with the lookup of *any_text* in the translation table. + It is the equivalent of \[\_(\"any_text\")\] but is more convenient + to type. + +**%\<**_any_text_**\>** + +: is replaced with the INFO value associated with the header + *any_text* or the empty string if no such INFO value exists. It is + the equivalent of \[triginfo(\"any_text\")\] but is more convenient + to type. + +**%\_** + +: (percent-underscore) is replaced with a newline. You can use this to + achieve multi-line reminders. Note that calendar back-ends vary in + how they handle multi-line reminders: + + **o** + + : Running **remind -c** preserves newlines in the terminal + calendar output. + + **o** + + : **rem2pdf** preserves newlines if **remind** is invoked with the + **-pp** or **-ppp** option. + + **o** + + : **rem2html** preserves newlines if **remind** is invoked with + the **-pp** option. + + **o** + + : **tkremind** preserves newlines. + + **o** + + : **rem2ps** converts newlines to spaces. But **rem2ps** is + deprecated; use **rem2pdf** instead. + + **o** + + : The \"simple calendar\" formats (i.e., **remind**\'s **-s**, + **-n** and **-p** options) convert newlines to spaces. + + All calendar back-ends collapse multiple spaces to a single space + and multiple newlines to a single newline. + +**%1** + +: is replaced with \"now\", \"*m* minutes from now\", \"*m* minutes + ago\", \"*h* hours from now\", \"*h* hours ago\", \"*h* hours and + *m* minutes from now\" or \"*h* hours and *m* minutes ago\", as + appropriate for a timed reminder. Note that unless you specify the + **-a** option, timed reminders will be triggered like normal + reminders, and thus a timed reminder that occurred earlier in the + day may be triggered. This causes the need for the \"\...ago\" + forms. + +**%2** + +: is replaced with \"at *hh*:*mm*am\" or \"..pm\" depending on the + **AT** time of the reminder. + +**%3** + +: is replaced with \"at *hh*:*mm*\" in 24-hour format. + +**%4** + +: is replaced with \"*mm*\" where *mm* is the number of minutes + between \"now\" and the time specified by **AT**. If the **AT** time + is earlier than the current time, then the result is negative. + +**%5** + +: is replaced with \"*ma*\" where *ma* is the absolute value of the + number produced by **%4**. + +**%6** + +: is replaced with \"ago\" or \"from now\", depending on the + relationship between the **AT** time and the current time. + +**%7** + +: is replaced with the number of hours between the **AT** time and the + current time. It is always non-negative. + +**%8** + +: is replaced with the number of minutes between the **AT** time and + the current time, after the hours (**%7**) have been subtracted out. + This is a number ranging from 0 to 59. + +**%9** + +: is replaced with \"s\" if the value produced by **%8** is not 1. + +**%0** + +: is replaced with \"s\" if the value produced by **%7** is not 1. + +**%!** + +: is replaced with \"is\" if the current date and time is before the + trigger date and the **AT** time, or \"was\" if it is after. The + **%!** sequence may be used in a non-timed reminder, in which case + only dates are compared. + +**%?** + +: is replaced with \"are\" if the current date and time is before the + trigger date and the **AT** time, or \"were\" if it is after. The + **%?** sequence may be used in a non-timed reminder, in which case + only dates are compared. + +**%@** + +: is similar to **%2** but displays the current time. + +**%#** + +: is similar to **%3** but displays the current time. + +**%:** + +: is replaced with \" (done)\" for a TODO reminder whose trigger date + is on or after its COMPLETE-THROUGH date. It is replaced with the + empty string in any other situation. Note that because **Remind** + does not display completed TODO reminders in Agenda Mode, this + escape sequence is really only useful in Calendar Mode. + +**%\"** + +: (percent-doublequote) is removed. This sequence is not used by the + substitution filter, but is used to tell **Remind** which text to + include in a calendar entry when the **-c**, **-s** or **-p** option + is chosen. See \"CALENDAR MODE\" + +Notes: + +o + +: **Remind** normally prints a blank line after each reminder; if the + last character of the body is \"%\", the blank line will not be + printed. You can globally suppress the extra blank lines by setting + **\$AddBlankLines** to 0. + +o + +: Substitutions a, b, c, e, f, g, h, i, j, k, l, u and v all are + replaced with \"today\" if the current date equals the trigger date, + or \"tomorrow\" if the trigger date is one day after the current + date. Thus, they are **not** the same as substitutions built up from + the simpler %w, %y, etc. sequences. + +o + +: The a, c, e, f, g, h, i, j, k, l, u, v, 2, and 3 substitutions may + be preceded by an asterisk (for example, **%\*c**) which causes the + word \"at\" or \"on\" that would normally be included in the output + to be omitted. + +o + +: The ! and ? substitutions may be preceded by an asterisk (**%\*!** + or **%\*?**), in which case the comparison is made between the + trigger date and realtoday() instead of today(). + +o + +: Any of the substitutions dealing with time (0 through 9) produce + undefined results if used in a reminder that does not have an **AT** + keyword. Also, if a reminder has a *delta* and may be triggered on + several days, the time substitutions ignore the date. Thus, the + **%1** substitution may report that a meeting is in 15 minutes, for + example, even though it may only be in 2 days time, because a + *delta* has triggered the reminder. It is recommended that you use + the time substitutions only in timed reminders with no *delta* that + are designed to be queued for timed activation. + +o + +: Capital letters can be used in the substitution sequence, in which + case the first character of the substituted string is capitalized + (if it is normally a lower-case letter.) + +o + +: All other characters following a \"%\" sign are simply copied. In + particular, to get a \"%\" sign out, use \"%%\" in the body. To + start the body of a reminder with a space, use \"% \", since + **Remind** normally scans for the first non-space character after a + **MSG,** **CAL** or **RUN** token. + +# EVENTS AND TODOS + +The **REM** command is normally used to create an *EVENT*. This is +something that happens at a certain time, possibly recurring, and +happens no matter what action you take. Events include things like +birthdays, holidays, meetings, etc\... pretty much everything that +occurs on a particular schedule. Once the date of an event has passed, +**Remind** will no longer remind you about the event. + +A *TODO* is different; it is a task that you have to complete by a +specific date or date and time. If you don\'t explicitly mark a TODO as +done, **Remind** will keep reminding you about it *even past* the due +date.. + +To mark a **REM** as a TODO, simply include the TODO keyword. For +example: + + REM TODO 15 August 2025 ++5 MSG Buy cat food %b. + +In Agenda Mode, **Remind** will start warning you on 10 Aug 2025 that +you have to but cat food in 5 days\' time, 4 days\' time, etc\... + +However, on 16 Aug 2025, **Remind** will say \"Buy cat food yesterday.\" +and it will keep reminding you of your need to buy cat food until the +end of time, or until you mark the TODO as done. + +# MARKING TODOS AS DONE + +There are two ways to mark a TODO as done. If the TODO is not recurring, +the simplest way is simply to remove the TODO designator from the REM +command (or indeed, to completely delete it.) The former keeps the +reminder on the calendar while the latter completely removes it. + +If a TODO is recurring, use the **COMPLETE-THROUGH** clause to mark +which recurrences have been completed. For example: + + REM TODO 30 April ++15 COMPLETE-THROUGH 2025-04-30 MSG File taxes + +Canadian income taxes must be filed every 30 April. The above command +will remind you to pay taxes in 2026. If you don\'t update the +COMPLETE-THROUGH date, then after 30 April 2026, **Remind** will keep +reminding you until the end of time that your taxes were due on 30 April +2026. To indicate that you\'ve paid them, simply update the +COMPLETE-THROUGH date to 2026-04-30 and then **Remind** will start +reminding you of your 2027 taxes (starting 15 days before the due date.) + +It is an error to specify COMPLETE-THROUGH without also specifying TODO. + +# LIMITING REMINDERS ABOUT OVERDUE TODOS + +Although **Remind** is happy to keep reminding you about overdue TODOs +for hundreds of years, for some things this may be pointless. If you +want **Remind** to *stop* nagging you about an overdue TODO after a +certain number of days, use the MAX-OVERDUE *n* clause. In this case, +**Remind** stops reminding you of a TODO that is overdue by more than +*n* days. Here is an example. + + REM TODO 2025-08-13 ++5 MAX-OVERDUE 5 MSG Task: %b. + +**Remind** *starts* reminding you of the task on 2025-08-08, because of +the ++5 back value. It keeps reminding you of the task after the due +date. However, the last time it will remind you will be on 2025-08-18, +because of the MAX-OVERDUE clause. Starting on 2025-08-19, **Remind** +will no longer remind you of the task since it\'s probably +pointless\-\--it has passed the MAX-OVERDUE period. + +# MORE DETAILS ABOUT TODOS + +TODOs are treated specially only in Agenda Mode. In Calendar Mode, they +appear in the calendar exactly as a normal event would. + +TODOs are implemented internally by using the COMPLETE-THROUGH date plus +one day as the starting point for **Remind**\'s date-scanning algorithm. +If you have a recurring TODO without a COMPLETE-THROUGH clause, then +**Remind** starts scanning from the beginning of time, which we all know +is 1 January 1990. Consider this command: + + REM TODO Wed +7 MSG Take out the trash %a (%b) + +Running that command in Agenda Mode on 2025-08-13 yields the following +output: + + Take out the trash on Wednesday, 3 January, 1990 (13006 days ago) + +**Remind** is very persistent about reminding you of tasks! If you take +out the trash and mark it done: + + REM TODO Wed +7 COMPLETE-THROUGH 2025-08-13 MSG Take out the trash %a (%b) + +then you get this: + + Take out the trash on Wednesday, 20 August, 2025 (in 7 days' time) + +You can use **FROM** and **UNTIL** to limit the recurrence interval of +tasks just as you can with events. For example, if you are house-sitting +for the month of August and need to water plants every Wednesday: + + REM TODO Wed +7 FROM 2025-08-06 UNTIL 2025-08-27 MSG Plants %b. + +Running that command on 13 Aug yields: \"Plants 7 days ago.\" because +you have not told **Remind** that you completed the first watering. If +you finish your duties and add a COMPLETE-THROUGH date of 2025-08-27, +then **Remind** never reminds you of that task in the future. + +In Purge Mode, **Remind** will not purge TODOs unless they have been +marked as complete. In the case of a recurring TODO, **Remind** will not +purge it until the last occurrence is marked as complete. + +# THE OMIT COMMAND + +In addition to being a keyword in the **REM** command, **OMIT** is a +command in its own right. Its syntax is: + +> **OMIT** *weekday* \[*weekday*\...\] +> +> or: +> +> **OMIT** \[*day*\] *month* \[*year*\] +> +> or: +> +> **OMIT** \[*day1*\] *month1* \[*year1*\] **THROUGH** \[*day2*\] +> *month2* \[*year2*\] + +The **OMIT** command is used to \"globally\" omit certain days (usually +holidays). These globally-omitted days are skipped by the \"-\" and +\"+\" forms of *back* and *delta*, but not by the \"\--\" and \"++\" +forms. Some examples: + + OMIT Saturday Sunday + OMIT 1 Jan + OMIT 7 Sep 1992 + OMIT 15 Jan THROUGH 14 Feb + OMIT May # Equivalent to OMIT May 1 THROUGH May 31 + OMIT 25 Dec THROUGH 4 Jan + OMIT 2023-05-03 THROUGH 2023-05-12 + OMIT Jun THROUGH July # Equivalent to OMIT Jun 1 THROUGH July 31 + +The first example omits every Saturday and Sunday. This is useful for +reminders that shouldn\'t trigger on weekends. + +The second example specifies a holiday that occurs on the same date each +year - New Year\'s Day. + +The third example specifies a holiday that changes each year - Labour +Day. For these types of holidays, you must create an **OMIT** command +for each year. (Later, in the description of expressions and some of the +more advanced features of **Remind**, you will see how to automate this +for some cases.) + +As with the REM command, you can use shorthand specifiers for dates; the +following are equivalent: + + OMIT 7 Sep 1992 + OMIT 1992-09-07 + +For convenience, you can use a *delta* and **MSG** or **RUN** keyword in +the **OMIT** command. The following sequences are equivalent: + + OMIT 1 Jan + REM 1 Jan +4 MSG New year's day is %b! + + and + + OMIT 1 Jan +4 MSG New year's day is %b! + +The **THROUGH** keyword lets you conveniently OMIT a range of days. For +example, the following sequences are equivalent: + + OMIT 3 Jan 2011 + OMIT 4 Jan 2011 + OMIT 5 Jan 2011 + + and + + OMIT 3 Jan 2011 THROUGH 5 Jan 2011 + +Note that **Remind** has a compiled-in limit to the number of full +OMITs. If you omit a range of *N* fully-specified (i.e., year included) +days, then *N* full OMITs are used up. Trying to omit a very large range +may result in the error \"Too many full OMITs\" + +You can make a THROUGH **OMIT** do double-duty as a **REM** command as +long as both dates are fully specified + + OMIT 6 Sep 2010 THROUGH 10 Sep 2010 MSG Vacation + +If you use a THROUGH clause, then either the year must be supplied +before and after the THROUGH, or it must be missing before and after the +THROUGH. The following are legal: + + OMIT 25 Dec THROUGH 6 Jan + OMIT 25 Dec 2024 THROUGH 6 Jan 2025 + +But the following are not: + + OMIT 25 Dec THROUGH 6 Jan 2025 + OMIT 25 Dec 2024 THROUGH 6 Jan + + You can debug your global OMITs with the following command: + + OMIT DUMP + +The OMIT DUMP command prints the current global omits to standard +output. + +**THE BEFORE, AFTER AND SKIP KEYWORDS** + +Normally, days that are omitted, whether by a global **OMIT** command or +the local **OMIT** or **OMITFUNC** keywords in a **REM** statement, only +affect the counting of the -*back* or the +*delta*. For example, suppose +you have a meeting every Wednesday. Suppose, too, that you have +indicated 11 Nov as a holiday: + + OMIT 11 Nov +4 MSG Remembrance Day + REM Wed +1 MSG Code meeting %b. + +The above sequence will issue a reminder about a meeting for 11 November +1992, which is a Wednesday. This is probably incorrect. There are three +options: + +**BEFORE** + +: This keyword moves the reminder to before any omitted days. Thus, in + the above example, use of **BEFORE** would cause the meeting + reminder to be triggered on Tuesday, 10 November 1992. + +**AFTER** + +: This keyword moves the reminder to after any omitted days. In the + above example, the meeting reminder would be triggered on Thursday, + 12 November 1992. + +**SKIP** + +: This keyword causes the reminder to be skipped completely on any + omitted days. Thus, in the above example, the reminder would not be + triggered on 11 November 1992. However, it would be triggered as + usual on the following Wednesday, 18 November 1992. + +The **BEFORE** and **AFTER** keywords move the trigger date of a +reminder to before or after a block of omitted days, respectively. +Suppose you normally run a backup on the first day of the month. +However, if the first day of the month is a weekend or holiday, you run +the backup on the first working day following the weekend or holiday. +You could use: + + REM 1 OMIT Sat Sun AFTER RUN do_backup + +Let\'s examine how the trigger date is computed. The **1** specifies the +first day of the month. The local **OMIT** keyword causes the **AFTER** +keyword to move the reminder forward past weekends. Finally, the +**AFTER** keyword will keep moving the reminder forward until it has +passed any holidays specified with global **OMIT** commands. + +# TIMEZONE SUPPORT + +The **REM** command supports an optional **TZ** keyword, which should be +followed by the *case-sensitive* time zone name in which the command is +to be interpreted. Note that if you use the **TZ** keyword, then you +*must also* use an **AT** clause. Here are some examples: + + REM Wednesday AT 14:00 TZ America/Toronto MSG 2PM Eastern (%2). + REM Wednesday AT 23:59 TZ America/Los_Angeles SATISFY [$Td == 13] MSG Foo %b %2. + +Within a **SATISFY** clause and an **OMITFUNC** function, all trigger +functions and the trigger date are interpreted _in the time zone_ +_specified in the_ **REM** _command_. Outside the **REM** command, +however, trigger functions are adjusted to the local time zone. If the +local time zone is UTC and we feed **Remind** the following file on +2025-09-04 UTC: + + SET $AddBlankLines 0 + BANNER % + REM Wednesday AT 14:00 TZ America/Toronto MSG 2PM Eastern (%2). + set a $T + set b $Tt + REM MSG a = [a], b = [b] + REM Wednesday AT 23:59 TZ America/Los_Angeles SATISFY [$Td == 13] MSG Foo %b %2. + set c $T + set d $Tt + REM MSG c = [c], d = [d] + +Then the output is as follows: + + a = 2025-09-10, b = 18:00 + c = 2026-05-14, d = 06:59 + +That is because the trigger date of the first (Wednesday, 2025-09-10 at +14:00 Eastern time) is 2025-09-10 at 18:00 UTC. In the second case, +Wednesday, 13 May 2026 is the SATISFied trigger date, which is adjusted +to Thursday, 14 May 2026 at 06:59 UTC because of the time zone +adjustment. + +If you use an invalid time zone name after the **TZ** keyword, results +are undefined. As mentioned previously, time zone names *are* +case-sensitive; America/Toronto is valid, but america/toronto is not. + +If you are on a system that stores time zone data in +/usr/share/zoneinfo, then **Remind** will attempt to validate the time +zone name and will warn you if it appears to be invalid. If you want to +specify a time zone that lacks a file under /usr/share/zoneinfo anyway, +and want to suppress the warning, prefix the time zone name with \"!\". +For example: + + REM Thursday AT 14:45 TZ !EST+5EDT,M3.2.0/2,M11.1.0/2 MSG Old TZ format + +In a reminder with the **TZ** keyword, OMIT dates are evaluated in the +specified time zone. Here\'s an example: Suppose the local time zone is +America/Toronto and you have this script: + + # A Friday + OMIT 2025-09-05 + + # A Saturday + OMIT 2025-09-13 + + REM Saturday AT 01:00 TZ Europe/Amsterdam SKIP MSG Early Sat AM + +On 2025-09-05 in the America/Toronto time zone, the reminder *will* +trigger. Even though 2025-09-05 has been OMITted, the SKIP keyword +evaluates the date in the Europe/Amsterdam time zone, and 2025-09-06 +(the trigger date) is *not* omitted. + +Conversely, on 2025-09-12 in the America/Toronto time zone, the reminder +will *not* trigger. Even though 2025-09-12 is not OMITted, 2025-09-13 +is, and that is the trigger date in the Europe/Amsterdam time zone. + +# THE DO, INCLUDE AND SYSINCLUDE COMMANDS + +**Remind** allows you to include other files in your reminder script, +similar to the C preprocessor #include directive. For example, you might +organize different reminders into different files like this: + + INCLUDE holidays.rem + INCLUDE birthdays.rem + INCLUDE "quote files with spaces.rem" + +**INCLUDE** files can be nested up to a depth of 8. As shown above, if a +filename has spaces in it (not recommended!) you can use double-quotes +around the filename. + +If you specify a filename of \"-\" in the **INCLUDE** command, +**Remind** will begin reading from standard input. + +If you specify a *directory* as the argument to **INCLUDE**, then +**Remind** will process all files (but not subdirectories!) in that +directory that match the shell pattern \"\*.rem\". The files are +processed in sorted order; the sort order matches that used by the shell +when it expands \"\*.rem\". + +Note that the file specified by an **INCLUDE** command is interpreted +relative to the _current working directory of the_ **Remind** process. +If you want to include a file relative to the directory containing the +currently-processing file, use **DO** instead. For example, if the +current file is **/home/user/.reminders/foo.rem** and Remind\'s working +directory is **/home/user**, then: + + # Read /home/user/.reminders/bar.rem + DO bar.rem + + # Read /usr/share/bar.rem - absolute path + DO /usr/share/bar.rem + + # Read /home/user/bar.rem + INCLUDE bar.rem + + # Read /usr/share/bar.rem - absolute path + INCLUDE /usr/share/bar.rem + +Arguably, the **INCLUDE** command should have worked the way **DO** does +right from the start, but changing it would have broken +backward-compatibility, hence the introduction of **DO**. + +Note that if the currently-processing reminders file was specified as a +symbolic link to a file that is not in the same directory as the +symbolic link itself, **DO** will fail. **Remind** does *not* resolve +the real path of symbolic links, so you should avoid using symbolic +links to files. + +The **SYSINCLUDE** command is similar to **DO**, but it looks for +relative pathnames under the system directory containing standard +reminder scripts. For this installation of **Remind**, the system +directory is \"/usr/local/share/remind\". + +# THE RETURN COMMAND + +The **RETURN** command causes **Remind** to ignore the remaining +contents of the file currently being processed. It can be used as a +quick way to \"exit\" from an included file (though it also works at the +top-level.) + +Here is an example of how **RETURN** might be used: + + IF already_done + RETURN + ENDIF + set already_done 1 + preserve already_done + # Do expensive processing here + +# THE RUN COMMAND + +If you include other files in your reminder script, you may not always +entirely trust the contents of the other files. For example, they may +contain **RUN**-type reminders that could be used to access your files +or perform undesired actions. The **RUN** command can restrict this: If +you include the command **RUN OFF** in your top-level reminder script, +any reminder or expression that would normally execute a system command +is disabled. **RUN ON** will re-enable the execution of system commands. +Note that the **RUN ON** command can *only* be used in your top-level +reminder script; it will *not* work in any files accessed by the +**INCLUDE** command. This is to protect you from someone placing a **RUN +ON** command in an included file. However, the **RUN OFF** command can +be used at top level or in an included file. + +If you run **Remind** with the **-r** command-line option, **RUN**-type +reminders and the **shell()** function will be disabled, regardless of +any **RUN** commands in the reminder script. However, any command +supplied with the **-k** option will still be executed. + +In addition, **Remind** contains a few other security features. It will +not read a file that is group- or world-writable. It will not run +set-uid. If it reads a file you don\'t own, it will disable RUN and the +shell() function. And if it is run as *root*, it will only read files +owned by *root*. + +Note that if **Remind** reads standard input, it does *not* attempt to +check the ownership of standard input, even if it is coming from a file, +and hence does *not* disable RUN and shell() in this situation. + +# THE EXPR COMMAND + +**Remind** lets you completely disable expression evaluation. This could +be useful if you are running **Remind** on a somewhat-untrustworthy file +that is not expected to contain expressions. To disable expression +evaluation, use: + + EXPR OFF + +If **Remind** encounters an expression while EXPR OFF is in effect, it +returns an error + +To re-enable expression evaluation, use: + + EXPR ON + +As with **RUN ON, EXPR ON** can be used only in the top-level script, +not in an included file. + +# THE INCLUDECMD COMMAND + +**Remind** allows you to execute a shell command and evaluate the output +of that command as if it were an included file. For example, you could +have scripts that extract reminders out of a database and print them on +stdout as REM commands. Here is an example: + + INCLUDECMD extract_reminders_for dfs + +We assume that the command \"extract_reminders_for\" extracts reminders +out of a central database for the named user. Another use-case of +INCLUDECMD is if you have your reminders stored in a file in some +non-Remind format; you can write a command that transforms them to +**Remind** format and then **Remind** can \"include\" the file with an +appropriate INCLUDECMD command. + +Note that if RUN is disabled, then INCLUDECMD will fail with the error +message \"RUN disabled\" + +**Remind** arranges so that when the command specified by INCLUDECMD is +executed, its standard input is opened to /dev/null. + +INCLUDECMD passes the rest of the line to **popen**(3), meaning that the +command is executed by the shell. As such, shell meta-characters may +need escaping or arguments quoting, depending on what you\'re trying to +do. **Remind** itself does not perform any modification of the command +line (apart from the normal \[expr\] expression-pasting mechanism). + +If the command passed to INCLUDECMD begins with an exclamation mark +\"!\", then **Remind** disables **RUN** for the output of the command. +If you are running a command whose output you don\'t quite trust, you +should prefix it with \"!\" so that any RUN commands it emits fail. + +An **INCLUDECMD** command counts towards the INCLUDE nesting depth. For +any given **Remind** run, a given INCLUDECMD command is only executed +once and the results are cached. For example, if you generate a +calendar, each unique INCLUDECMD command is run just once, not once for +each day of the produced calendar. \"Uniqueness\" is determined by +looking at the command that will be passed to the shell, so if (for +example) your INCLUDECMD uses expression-pasting that results in +differences depending on the value of **today()**, then each *unique* +version of the command will be executed once. + +If a given reminder file contains more than one identical +**INCLUDECMD**, only the first one will actually be executed. All +subsequent identical ones will use the cached output from the first one. + +# THE BANNER COMMAND + +When **Remind** first issues a reminder, it prints a message like this: + + Reminders for Friday, 30th October, 1992 (today): + +(The banner is not printed if any of the calendar-producing options is +used, or if the **-k** option is used.) + +The **BANNER** command lets you change the format. It should appear +before any **REM** commands. The format is: + +> **BANNER** *format* + +The *format* is similar to the *body* of a **REM** command. It is passed +through the substitution filter, with an implicit trigger of the current +system date. Thus, the default banner is equivalent to: + + BANNER Reminders for %w, %d%s %m, %y%o: + +You can disable the banner completely with BANNER %. Or you can create a +custom banner: + + BANNER Hi - here are your reminders for %y-%t-%r: + +# CONTROLLING THE OMIT CONTEXT + +Sometimes, it is necessary to temporarily change the global **OMITs** +that are in force for a few reminders. Three commands allow you to do +this: + +**PUSH-OMIT-CONTEXT** + +: This command saves the current global **OMITs** on an internal + stack. + +**CLEAR-OMIT-CONTEXT** + +: This command clears all of the global **OMITs**, starting you off + with a \"clean slate.\" + +**POP-OMIT-CONTEXT** + +: This command restores the global **OMITs** that were saved by the + most recent **PUSH-OMIT-CONTEXT**. + +For example, suppose you have a block of reminders that require a clear +**OMIT** context, and that they also introduce unwanted global **OMITs** +that could interfere with later reminders. You could use the following +fragment: + + PUSH-OMIT-CONTEXT # Save the current context + CLEAR-OMIT-CONTEXT # Clean the slate + # Block of reminders goes here + POP-OMIT-CONTEXT # Restore the saved omit context + +# EXPRESSIONS + +In certain contexts, to be described later, **Remind** will accept +expressions for evaluation. **Remind** expressions resemble C +expressions, but operate on different types of objects. + +**DATA TYPES** + +**Remind** expressions operate on five types of objects: + +**INT** + +: The **INT** data type consists of the integers representable in one + machine word. The **INT** data type corresponds to the C \"int\" + type. + +**STRING** + +: The **STRING** data type consists of strings of characters. It is + somewhat comparable to a C character array, but more closely + resembles the string type in BASIC. + + **Remind** normally expects to be running in a UTF-8 environment. In + this environment, there is a difference between *bytes* and + *characters* since in UTF-8, a character may be represented by a + sequence of more than one byte. For example, in a UTF-8 environment, + the string \"🙂\" contains one character but four bytes. And the + string \"één\" contains three characters but five bytes. + + **Remind** has a set of functions that work on *bytes*, namely + **index**, **strlen** and **substr**. These are not safe to use on + multi-byte strings; instead use **mbindex**, **mbstrlen** and + **mbsubstr**. If you know *for sure* that a string contains only + single-byte characters, then the byte-oriented versions may be used + and are faster than the multi-byte versions. + + Some ancient or embedded systems may lack the C library functions + needed to deal with multi-byte strings. In that case, the + **mb**_xxx_ functions all return an error. + +**TIME** + +: The **TIME** data type is used for two different purposes: To + represent a time of day with one-minute precision or to represent a + duration with one-minute precision. The context of where a **TIME** + is used determines whether it is interpreted as a time of day or a + duration. + + In contexts where a **TIME** represents a time of day, it may range + from 00:00 to 23:59 and is stored internally as an integer from 0 to + 1439 representing the number of minutes since midnight. + + In contexts where a **TIME** represents a duration, there is no + upper limit on the hour component (beyond that imposed by the + restriction that a duration expressed in minutes must fit into the + signed integer type of your CPU architecture.) Internally, a + duration is stored as an integer number of minutes. + +```{=html} + +``` + +**DATE** + +: The **DATE** data type consists of dates (later than 1 January + 1990.) Internally, **DATE** objects are stored as the number of days + since 1 January 1990. + +**DATETIME** + +: The **DATETIME** data type consists of a date and time together. + Internally, **DATETIME** objects are stored as the number of minutes + since midnight, 1 January 1990. You can think of a **DATETIME** + object as being the combination of **DATE** and **TIME** parts. + +**CONSTANTS** + +The following examples illustrate constants in **Remind** expressions: + +**INT constants** + +: 12, 36, -10, 0, 1209, 0x1F, 0xfe00 (the last two demonstrate the use + of hexadecimal constants) + +**STRING constants** + +: \"Hello there\", \"This is a test\", \"\\nHello\\tThere\", \"\" + +> Note that the empty string is represented by \"\". **Remind** supports +> the escape sequences \"\\a\", \"\\b\", \"\\f\", \"\\n\", \"\\r\", +> \"\\t\" and \"\\v\" which have the same meanings as their counterparts +> in C. It also supports \"\\xAB\" where A and B are hexadecimal digits; +> this operates just as in C. The \"\\x\" must be followed by one or two +> hex digits; the escape sequence \"\\x00\" is not permitted. +> +> To include a quote in a string, use \"\\\"\". Any other character +> preceded by a backslash is inserted into the string as-is, but the +> backslash itself is removed. To include a backslash in a string, use +> \"\\\\\". + +**TIME constants** + +: 12:33, 0:01, 14:15, 16:42, 12.16, 13.00, 1.11, 4:30PM, 12:20am + +> Note that **TIME** constants may be written in 24-hour format or in +> common \"AM/PM\" format. If you use \"AM/PM\" format, then the hour +> can range from 1 to 12. Either a period or colon can be used to +> separate the minutes from the hours. However, **Remind** will +> consistently output times in 24-hour format using only one separator +> character. (The output separator character is chosen at compile-time.) +> +> If the **TIME** is used where **Remind** expects a time-of-day (for +> example, in an **AT** clause), then it can be written in 24-hour +> format (ranging from 00:00 to 23:59) or 12-hour format (ranging from +> 12:00am to 11:59pm). If the **TIME** is used where **Remind** expects +> a duration, it must not have an *am* or *pm* suffix and the hour can +> be as large as you want, so long as the total number of minutes in the +> duration fits in a signed integer variable. +> +> For convenience, a **TIME** constant may be surrounded by single +> quotes to match **DATE** and **DATETIME** constants, but these quotes +> are optional. That is, 12:56 and \'12:56\' represent the same **TIME** +> constant. + +**DATE constants** + +: **DATE** constants are expressed as \'yyyy/mm/dd\' or + \'yyyy-mm-dd\', and the single quotes *must* be supplied. This + distinguishes date constants from division or subtraction of + integers. Examples: + +> \'1993/02/22\', \'1992-12-25\', \'1999/01/01\' +> +> Note that **DATE** values are *printed* without the quotes. Although +> either \'-\' or \'/\' is accepted as a date separator on input, when +> dates are printed, only one will be used. The choice of whether to use +> \'-\' or \'/\' is made at compile-time. Note also that versions of +> **Remind** prior to 03.00.01 did not support date constants. In those +> versions, you must create dates using the **date()** function. Also, +> versions prior to 03.00.02 did not support the \'-\' date separator. + +**DATETIME constants** + +: **DATETIME** constants are expressed similarly to **DATE** constants + with the addition of an \"@HH:MM\" part, optionally followed by + \"am\" or \"pm\". For example: + +> \'2008-04-05@23:11\', \'1999/02/03@14:06\', \'2001-04-07@08:30\', +> \'2020-01-01@3:20pm\' +> +> **DATETIME** values are printed without the quotes. Notes about date +> and time separator characters for **DATE** and **TIME** constants +> apply also to **DATETIME** constants. + +**ZERO VALUES AND TRUE/FALSE** + +All types have an associated *zero value*, which is treated as *false* +by the IF command, the IIF function, and the logical operators. The zero +values are: + +> **INT** - 0 +> +> **DATE** - \'1990-01-01\' +> +> **TIME** - 00:00 +> +> **DATETIME** - \'1990-01-01@00:00\' +> +> **STRING** - \"\" (the empty string) + +Any value other than the *zero value* is treated as *true*. + +**OPERATORS** + +**Remind** has the following operators. Operators on the same line have +equal precedence, while operators on lower lines have lower precedence +than those on higher lines. The operators approximately correspond to C +operators. + + ! - (unary logical negation and arithmetic negation) + * / % (multiplication, division, modulus) + + - (addition/concatenation, subtraction) + < <= > >= (comparisons) + == != (equality and inequality tests) + && (logical AND) + || (logical OR) + +**DESCRIPTION OF OPERATORS** + +**!** + +: Logical negation. Can be applied to any type. If the operand is + non-zero, returns 0. Otherwise, returns 1. + +**-** + +: Unary minus. Can be applied to an **INT**. Returns the negative of + the operand. + +**\*** + +: Multiplication. Returns the product of two **INT**s. Alternatively, + if one argument is a **STRING** and the other an **INT**, returns a + **STRING** consisting of the INT number of repeats of the original + STRING. In this case, the INT argument cannot be negative. + +**/** + +: Integer division. Returns the quotient of two **INT**s, discarding + the remainder. + +**%** + +: Modulus. Returns the remainder upon dividing one **INT** by another. + +**+** + +: Has several uses. These are: + +> **INT** + **INT** - returns the sum of two **INT**s. +> +> **INT** + **TIME** or **TIME** + **INT** - returns a **TIME** obtained +> by adding **INT** minutes to the original **TIME**. The result will +> always range from 00:00 through 23:59. +> +> **TIME** + **TIME** treats the second **TIME** parameter as a +> duration, converting it to an integer number of minutes past midnight, +> and then performs addition as with **TIME** + **INT**. +> +> **INT** + **DATE** or **DATE** + **INT** - returns a **DATE** obtained +> by adding **INT** days to the original **DATE**. +> +> **INT** + **DATETIME** or **DATETIME** + **INT** - returns a +> **DATETIME** obtained by adding **INT** minutes to the original +> **DATETIME**. +> +> **DATETIME** + **TIME** or **TIME** + **DATETIME** treats the **TIME** +> parameter as a duration, converting it to an integer number of minutes +> past midnight, and then performs addition as with **DATETIME** + +> **INT**. +> +> **STRING** + **STRING** - returns a **STRING** that is the +> concatenation of the two original **STRING**s. +> +> **STRING** + anything or anything + **STRING** - converts the +> non-**STRING** argument to a **STRING**, and then performs +> concatenation. See the **coerce()** function. + +**-** + +: Has several uses. These are: + +> **INT** - **INT** - returns the difference of two **INT**s. +> +> **DATE** - **DATE** - returns (as an **INT**) the difference in days +> between two **DATE**s. +> +> **TIME** - **TIME** - returns (as an **INT**) the difference in +> minutes between two **TIME**s. +> +> **DATETIME** - **DATETIME** - returns (as an **INT**) the difference +> in minutes between two **DATETIME**s. +> +> **DATE** - **INT** - returns a **DATE** that is **INT** days earlier +> than the original **DATE**. +> +> **TIME** - **INT** - returns a **TIME** that is **INT** minutes +> earlier than the original **TIME**. +> +> **DATETIME** - **INT** - returns a **DATETIME** that is **INT** +> minutes earlier than the original **DATETIME**. +> +> **DATETIME** - **TIME** - coerces the **TIME** to an **INT** and then +> performs subtraction as above. + +**\<, \<=, \>, and \>=** + +: These are the comparison operators. They can take operands of any + type, but both operands must be of the same type. The comparison + operators return 1 if the comparison is true, or 0 if it is false. + Note that string comparison is done following the lexical ordering + of characters on your system, and that upper and lower case *are* + distinct for these operators. + +**==, !=** + +: == tests for equality, returning 1 if its operands are equal, and 0 + if they are not. != tests for inequality. + +> If the operands are not of the same type, == returns 0 and != +> returns 1. Again, string comparisons are case-sensitive. + +**&&** + +: This is the logical AND operator. Returns the second operand if both + operands are non-zero. Otherwise, returns whichever operand is zero. + Operands can be any type and \"zero\" is interpreted as appropriate + for each operand\'s type. + +**\|\|** + +: This is the logical OR operator. It returns the first operand that + is non-zero; if both operands are zero, then returns the second + operand. Operands can be any type and \"zero\" is interpreted as + appropriate for each operand\'s type. + +**NOTES** + +If the result of an addition, subtraction or multiplication operation +would not fit in a C \"int\" type, **Remind** issues a \"Number too +high\" error. Unlike C, integer operations will not simply give the +wrong answer in case of overflow. + +Operators of equal precedence are *always* evaluated from left to right, +except where parentheses dictate otherwise. This is important, because +the enhanced \"+\" operator is not necessarily associative. For example: + + 1 + 2 + "string" + 3 + 4 yields "3string34" + 1 + (2 + "string") + (3 + 4) yields "12string7" + 12:59 + 1 + "test" yields "13:00test" + 12:59 + (1 + "test") yields "12:591test" + +The logical operators are so-called short-circuit operators, as they are +in C. This means that if the first operand of \|\| is true, then the +second operand is *not* evaluated. Similarly, if the first operand of && +is false, then the second operand is *not* evaluated. + +**VARIABLES** + +**Remind** allows you to assign values to variables. The **SET** command +is used as follows: + +**SET** *var* *expr* + +*Var* is the name of a variable. It must start with a letter or +underscore, and consist only of letters, digits and underscores. Only +the first 64 characters of a variable name are significant. Variable +names are *not* case sensitive; thus, \"Afoo\" and \"afOo\" are the same +variable. Examples: + + SET a 10 + (9*8) + SET b "This is a test" + SET mydir getenv("HOME") + SET time 12:15 + SET date today() + +Note that variables themselves have no type. They take on the type of +whatever you store in them. + +Variables set with SET or on the command-line with +**-i**_var=expr_ have global scope. + +To delete a variable, use the **UNSET** command: + +**UNSET** *var* \[*var*\...\] + +For example, to delete all the variables declared above, use: + + UNSET a b mydir time date + +# SYSTEM VARIABLES + +In addition to the regular user variables, **Remind** has several +\"system variables\" that are used to query or control the operating +state of **Remind**. System variables are available starting from +version 03.00.07 of **Remind**. + +All system variables begin with a dollar sign \'\$\'. They can be used +in **SET** commands and expressions just as regular variables can. All +system variables always hold values of a specified type. In addition, +some system variables cannot be modified, and you cannot create new +system variables. System variables can be initialized on the command +line with the **-i** option, but you may need to quote them to avoid +having the shell interpret the dollar sign. System variable names are +not case-sensitive. + +The following system variables are defined. Those marked \"read-only\" +cannot be changed with the **SET** command. All system variables hold +values of type **INT**, unless otherwise specified. + +**\$AddBlankLines** + +: If set to 1 (the default), then **Remind** normally prints a blank + line after the banner and each reminder. (This can be suppressed by + ending the reminder or banner with a single percent sign.) If + \$AddBlankLines is set to 0, then **Remind** does not print the + blank line. In this case, ending a reminder with % has no effect. If + you *do* want a blank line after a reminder, end it with **%\_** to + insert a newline. + +```{=html} + +``` + +**\$CalcUTC** + +: If 1 (the default), then **Remind** uses C library functions to + calculate the number of minutes between local and Universal Time + Coordinated. This affects astronomical calculations (**sunrise()** + for example.) If 0, then you must supply the number of minutes + between local and Universal Time Coordinated in the + **\$MinsFromUTC** system variable. + +**\$CalMode (read-only)** + +: If non-zero, then the **-c** option was supplied on the command + line. + +**\$CalType (read-only, STRING type)** + +: If the **-c**, **-s** or **-p** command-line options were used, then + this variable has the value \"monthly\". If **-c+**, **-s+** or + **-p+** were used, then \"weekly\". Otherwise, \"none\". + +**\$Daemon (read-only)** + +: If \"daemon mode\" **-z** was invoked, contains the number of + minutes between wakeups. If not running in daemon mode, contains 0. + In server mode (either **-z0** or **-zj**), contains -1. + +**\$DateSep (STRING type)** + +: This variable can be set only to \"/\" or \"-\". It holds the + character used to separate portions of a date when **Remind** prints + a DATE or DATETIME value. + +**\$DedupeReminders** + +: If this variable is set to 1, then **Remind** will suppress + duplicate reminders. A given reminder is considered to be a + duplicate of a previous one if it has the *exact* same trigger date, + trigger time, and body. By default, this variable is set to 0 and + **Remind** does not suppress duplicate reminders. + + As an example, consider the following reminder file: + + SET $DedupeReminders 1 + REM Wednesday MSG Phooey + REM 20 MSG Phooey + + On Wednesday, 20 November 2024, only *one* \"Phooey\" will be + issued. In December of 2024, \"Phooey\" will be issued every + Wednesday as well as on Friday, 20 December 2024 + + If you set \$DedupeReminders to 0, then **Remind** does not even + track reminders to detect duplicates. Consider the following + example: + + SET $DedupeReminders 0 + REM Wednesday MSG Hello + SET $DedupeReminders 1 + REM Wednesday MSG Hello + + Every Wednesday, **Remind** will issue *two* \"Hello\" reminders. + Because \$DedupeReminders was 0 when the first \"Hello\" was issued, + it won\'t be tracked for potential duplicates. + + Duplicates are detected after all variable expansion and + substitutions have been done. Consider the following: + + SET $DedupeReminders 1 + set a "foo" + REM MSG [a] + set a "bar" + REM MSG [a] + set a "foo" + REM MSG [a] + + The first REM will trigger and print \"foo\". The second will + trigger and print \"bar\". The third will not trigger because it\'s + a duplicate of the first \"foo\". + +**\$DefaultColor (STRING type)** + +: This variable can be set to a string that has the form of three + space-separated numbers. Each number must be an integer from 0 to + 255, or all three numbers must be -1. The default value of + **\$DefaultColor** is \"-1 -1 -1\", which suppresses default + coloring of MSG-type reminders. If you set **\$DefaultColor** to any + other value, then all MSG-, MSF- and CAL-type reminders are + effectively converted into SPECIAL COLOR reminders whose color value + is specified by **\$DefaultColor**. + + Unlike other system variables, the value of **\$DefaultColor** is + *not* preserved between calendar iterations; rather, it is reset to + \"-1 -1 -1\" at the start of each iteration. + +**\$DefaultPrio** + +: The default priority assigned to reminders without a **PRIORITY** + clause. You can set this as required to adjust the priorities of + blocks of reminders without having to type priorities for individual + reminders. At startup, **\$DefaultPrio** is set to 5000; it can + range from 0 to 9999. + +**\$DefaultDelta** + +: You can set this variable to a number from 0 through 10000. If set + to a non-zero number, then **Remind** triggers any **REM** statement + that lacks a delta as if it had a delta of **++\$DefaultDelta**. By + default, **\$DefaultDelta** is zero. + +**\$DefaultTDelta** + +: The default time delta used if no +N is given in an AT clause. This + is normally 0, but can be set with the **-tt** option or explicitly + set in your script. If **\$DefaultDelta** is non-zero, you can use + an explicit delta of +0 in an AT clause to countermand the default + delta. + +**\$DeltaOverride (read-only)** + +: If non-zero, corresponds to the *n* argument given to a **-t**_n_ + command-line option. + +**\$DontFork (read-only)** + +: If non-zero, then the **-c** option was supplied on the command + line. + +**\$DontTrigAts (read-only)** + +: The number of times that the **-a** option was supplied on the + command line. + +**\$DontQueue (read-only)** + +: If non-zero, then the **-q** option was supplied on the command + line. + +**\$EndSent (STRING type)** + +: Contains a list of characters that end a sentence. The **MSF** + keyword inserts two spaces after these characters. Initially, + **\$EndSent** is set to \".!?\" (period, exclamation mark, and + question mark.) + +**\$EndSentIg (STRING type)** + +: Contains a list of characters that should be ignored when **MSF** + decides whether or not to place two spaces after a sentence. + Initially, is set to \"\'\>)\]}\"+CHAR(34) (single-quote, + greater-than, right parenthesis, right bracket, right brace, and + double-quote.) + +> For example, the default values work as follows: +> +> MSF He said, "Huh! (Two spaces will follow this.)" Yup. +> +> because the final parenthesis and quote are ignored (for the purposes +> of spacing) when they follow a period. + +**\$ExpressionTimeLimit** + +: If set to a non-zero value *n*, than any expression that takes + longer than *n* seconds to evaluate will be aborted and an error + returned. This is to prevent maliciously-crafted expressions for + creating a denial-of-service. In an included file, + \$ExpressionTimeLimit can only be lowered from its current value. In + the top-level file, it can be set to any value, including zero to + disable the time limit. + +**\$FirstIndent** + +: The number of spaces by which to indent the first line of a + **MSF**-type reminder. The default is 0. + +**\$FoldYear** + +: The standard Unix library functions may have difficulty dealing with + dates later than 2037. If this variable is set to 1, then the UTC + calculations \"fold back\" years later than 2037 before using the + Unix library functions. For example, to find out whether or not + daylight saving time is in effect in June, 2077, the year is + \"folded back\" to 2027, because both years begin on a Friday, and + both are non-leapyears. The rules for daylight saving time are thus + presumed to be identical for both years, and the Unix library + functions can handle 2027. By default, this variable is 0. Set it to + 1 if the sun or UTC functions misbehave for years greater than 2037. + See also the section \"MACHINES WITH A 32-BIT TIME_T TYPE\" + +**\$FormWidth** + +: The maximum width of each line of text for formatting **MSF**-type + reminders. The default is the width of the terminal in columns, + minus 8, but clamped at a minimum of 20 and a maximum of 500. If + standard output is not a terminal, then the default is 72.If an + **MSF**-type reminder contains a word too long to fit in this width, + it will not be truncated - the width limit will be ignored. + +**\$HideCompletedTodos (read-only)** + +: If non-zero, then the **\--hide-completed-todos** option was + supplied on the command line. + +**\$HushMode (read-only)** + +: If non-zero, then the **-h** option was supplied on the command + line. + +**\$IgnoreOnce (read-only)** + +: If non-zero, then the **-o** option was supplied on the command + line, or implicitly enabled for some other reason. In this case, + **ONCE** directives will be ignored. + +**\$InfDelta (read-only)** + +: If non-zero, then the **-t** option was supplied on the command + line, with no *n* argument. + +**\$IntMax (read-only)** + +: The largest representable **INT**. On a machine with 32-bit signed + integers using twos-complement representation, this will be + 2147483647. + +**\$IntMin (read-only)** + +: The smallest representable **INT**. On a machine with 32-bit signed + integers using twos-complement representation, this will be + -2147483648. + +**\$Latitude (STRING type)** + +: The latitude of your location, expressed as a string that is a + floating-point number. Because **Remind** does not have a native + floating-point type, we need to express it as a string. \$Latitude + can range from \"-90.0\" to \"90.0\", with positive numbers + representing points north of the equator and negative numbers + representing south. Note that regardless of your locale, \$Latitude + is always interpreted in the \"C\" locale and as such, the decimal + point must be a period (\".\"). + +**\$Longitude (STRING type)** + +: The longitude of your location, expressed as a string that is a + floating-point number. Because **Remind** does not have a native + floating-point type, we need to express it as a string. \$Longitude + can range from \"-180.0\" to \"180.0\", with positive numbers + representing points east of the Greenwich Meridian and negative + numbers representing west. Note that regardless of your locale, + \$Longitude is always interpreted in the \"C\" locale and as such, + the decimal point must be a period (\".\"). + + For example, the coordinates of the Statue of Liberty in New York + City are approximately set by: + + SET $Latitude "40.68933" + SET $Longitude "-74.04454" + +**\$LatDeg, \$LatMin, \$LatSec (DEPRECATED)** + +: These specify the latitude of your location. **\$LatDeg** can range + from -90 to 90, and the others from -59 to 59. Northern latitudes + are positive; southern ones are negative. For southern latitudes, + all three components should be negative. These three variables are + deprecated; you should use **\$Latitude** instead. + +**\$Location (STRING type)** + +: This is a string specifying the name of your location. It is usually + the name of your town or city. It can be set to whatever you like, + but good style indicates that it should be kept consistent with the + latitude and longitude system variables. + +**\$LongDeg, \$LongMin, \$LongSec (DEPRECATED)** + +: These specify the longitude of your location. **\$LongDeg** can + range from -180 to 180. Western longitudes are positive; eastern + ones are negative. Note that all three components should have the + same sign: All positive for western longitudes and all negative for + eastern longitudes. Note that for historical reasons, the sign for + longitude is *different* from the usual convention! If you find the + longitude of your location from a search engine, you will most + likely *need to invert the sign to have it work correctly with* + Remind. These three variables are deprecated; you should use + **\$Longitude** instead. Note also that **\$Longitude** uses the + standard convention of negative for western longitudes and positive + for eastern ones. + + The latitude and longitude information is required for the functions + **sunrise()** and **sunset()**. Default values can be compiled into + **Remind**, or you can **SET** the correct values at the start of + your reminder scripts. + + Note that setting any of **\$LongDec**, **\$LongMin** and + **\$LongSec** updates **\$Longitude** correspondingly, and setting + **\$Longitude** updates **\$LongDeg**, **\$LongMin** and + **\$LongSec**. Similar rules apply to **\$Latitude**, **\$LatDeg**, + **\$LatMin** and **\$LatSec**. + +**\$JSONMode (read-only)** + +: If non-zero, then the **\--json** command-line option was supplied. + +**\$MaxLateMinutes** + +: This variable controls how **Remind** reacts to a computer being + suspended and then woken. Normally, if a timed reminder is queued + and then the computer suspended, and then the computer is woken + *after* the timed reminder\'s trigger time, **Remind** will trigger + the timer anyway, despite the fact that the trigger time has already + passed. + + If you set **\$MaxLateMinutes** to a non-zero integer between 1 and + 1440, then **Remind** will *not* trigger a timed reminder whose + trigger time is more than **\$MaxLateMinutes** minutes in the past. + + Note that **Remind** uses the value of **\$MaxLateMinutes** that is + in effect when it has finished reading the reminder file and puts + itself in the background. Generally, you should set + **\$MaxLateMinutes** once near the beginning of the file and not + change it after that. + +**\$MaxSatIter** + +: The maximum number of iterations for the **SATISFY** clause + (described later.) Must be at least 10. + +**\$MaxStringLen** + +: A limit on the longest string that **Remind** will allow you to + create. The default is 65535. If you set **\$MaxStringLen** to 0 or + to -1, then **remind** will allow you to create arbitrarily-long + strings, at least until it runs out of memory. We do not recommend + setting **\$MaxStringLen** to 0 or -1 because it is very easy to + write code that DOSes **Remind** in that case. + +**\$MinsFromUTC** + +: The number of minutes between Universal Time Coordinated and local + time. If **\$CalcUTC** is non-zero, this is calculated upon startup + of **Remind**. Otherwise, you must set it explicitly. If + **\$CalcUTC** is zero, then **\$MinsFromUTC** is used in the + astronomical calculations. You must adjust it for daylight saving + time yourself. Also, if you want to initialize **\$MinsFromUTC** + using the **-i** command-line option, you must also set + **\$CalcUTC** to 0 with the **-i** option. + +**\$NextMode (read-only)** + +: If non-zero, then the **-n** option was supplied on the command + line. + +**\$MaxFullOmits (read-only)** + +: The maximum number of full OMITs allowed (a compiled-in constant.) + +**\$MaxPartialOmits (read-only)** + +: The maximum number of partial OMITs allowed (a compiled-in + constant.) + +**\$NumFullOmits (read-only)** + +: The number of full OMITs in the current OMIT context. + +**\$NumPartialOmits (read-only)** + +: The number of partial OMITs in the current OMIT context. + +**\$NumQueued (read-only)** + +: Contains the number of reminders queued so far for background timed + triggering. + +**\$NumTrig (read-only)** + +: Contains the number of reminders triggered for the current date. One + use for this variable is as follows: Suppose you wish to shade in + the box of a PostScript calendar whenever a holiday is triggered. + You could save the value of **\$NumTrig** in a regular variable + prior to executing a block of holiday reminders. If the value of + **\$NumTrig** after the holiday block is greater than the saved + value, then at least one holiday was triggered, and you can execute + the command to shade in the calendar box. (See the section + \"Calendar Mode\".) + +> Note that **\$NumTrig** is affected *only* by **REM** commands; +> triggers in **IFTRIG** commands do not affect it. + +**\$OnceFile (STRING type)** + +: If you set this variable to a non-empty string, then rather than + using the file access date to determine whether or not to run a + ONCE-type reminder, **Remind** will maintain a timestamp in the file + **\$OnceFile**. This is more reliable than using the access date of + the reminder file. + + If **\$OnceFile** does not exist, then it will be created the first + time a **ONCE** keyword is processed. The file must be writable by + the current user. If you try to set **\$OnceFile** *after* a + **ONCE** reminder has already been processed, **Remind** will issue + a warning and ignore the attempt to set **\$OnceFile**. + +**\$ParseUntriggered** + +: A flag indicating whether or not **Remind** should fully parse + **REM** statements that are not triggered. 0 (the default) means to + skip parsing them and 1 means to parse them. + +> For example, if we have the following **REM** statement: +> +> REM 2020-01-01 MSG ["bad_expression" / 2] +> +> Then if **\$ParseUntriggered** is set to 1, **Remind** will fully +> parse the line and issue a \"Type mismatch\" error even if the +> reminder is not triggered. However, if **\$ParseUntriggered** is set +> to 0, the default, then **Remind** will not issue the error except on +> 2020-01-01, when the reminder is triggered. +> +> Keeping **\$ParseUntriggered** at 0 may slightly improve performance, +> at the risk of not catching errors until a reminder is triggered. We +> recommend leaving it set to 0. + +**\$PrefixLineNo (read-only)** + +: If non-zero, then the **-l** option was supplied on the command + line. + +**\$PSCal (read-only)** + +: If non-zero, then the **-p** option was supplied on the command + line. + +**\$RunOff (read-only)** + +: If non-zero, the **RUN** directives are disabled. + +**\$SimpleCal (read-only)** + +: Set to a non-zero value if *either* of the **-p** or **-s** + command-line options was supplied. + +**\$SortByDate (read-only)** + +: Set to 0 if no **-g** option is used, 1 if sorting by date in + ascending order, or 2 if sorting by date in descending order. + +**\$SortByPrio (read-only)** + +: Set to 0 if no **-g** option is used, 1 if sorting by priority in + ascending order, or 2 if sorting by priority in descending order. + +**\$SortByTime (read-only)** + +: Set to 0 if no **-g** option is used, 1 if sorting by time in + ascending order, or 2 if sorting by time in descending order. + +**\$SubsIndent** + +: The number of spaces by which all lines (except the first) of an + **MSF**-type reminder should be indented. The default is 0. + +**\$SuppressImplicitWarnings** + +: Normally, **Remind** issues a warning if a line begins with an + unknown token and is treated as a **REM** command, or if a **REM** + command is missing a type and is treated as a **MSG**-type reminder. + Setting **\$SuppressImplicitWarnings** to 1 suppresses these + warnings. The default is 0 and we do not recommend disabling the + warnings. + +**\$SuppressLRM** + +: Normally, when **Remind** is run with the **-c** option in a UTF-8 + locale, it emits a left-to-right mark sequence after printing day + names or reminders. Some terminals render this incorrectly, so you + can use: + + SET $SuppressLRM 1 + + at the top of your reminder file to suppress the LRM sequences, or + you can invoke **Remind** with the option **\'-i\$SuppressLRM=1\'**. + +**\$SysInclude (read-only, STRING type)** + +: A directory path containing standard reminder scripts. Currently, + **Remind** ships with some standard holiday files and language + packs. The value of **\$SysInclude** is \"/usr/local/share/remind\" + on this installation. + +**\$T (read-only, DATE or INT type)** + +: Equivalent to **trigdate()**. (See BUILT-IN FUNCTIONS.) + +**\$Tb (read-only, DATE or INT type)** + +: Equivalent to **trigbase()**. + +**\$Td (read-only)** + +: Equivalent to **day(trigdate())**. + +**\$Tm (read-only)** + +: Equivalent to **monnum(trigdate())**. + +**\$Tu (read-only, DATE or INT type)** + +: Equivalent to **triguntil()**. + +**\$Tw (read-only)** + +: Equivalent to **wkdaynum(trigdate())**. + +**\$Ty (read-only)** + +: Equivalent to **year(trigdate())**. + +**\$Tt (read-only, TIME or INT type)** + +: Equivalent to **trigtime()**. + +**\$TimeSep (STRING type)** + +: This variable can be set only to \":\" or \".\". It holds the + character used to separate portions of a time when **Remind** prints + a TIME or DATETIME value. + +**\$TimetIs64bit (read-only)** + +: This variable returns 1 if the internal C **time_t** type is at + least 64 bits long. If it returns 0, then the internal C library is + unable to represent dates after about 2038, and **Remind** will use + a workaround to avoid problems. See also the section \"MACHINES WITH + A 32-BIT TIME_T TYPE\" + +**\$TodoFilter (read-only)** + +: If 0, then both events and TODOs are being output. If 1, then the + **\--only-todos** command-line option was supplied. If 2, then the + **\--only-events** command-line option was supplied. + +**\$UntimedFirst (read-only)** + +: Set to 1 if the **-g** option is used with a fourth sort character + of \"d\"; set to 0 otherwise. + +**\$U (read-only, DATE type)** + +: Exactly equivalent to **today()**. (See BUILT-IN FUNCTIONS.) + +**\$Ud (read-only)** + +: Equivalent to **day(today())**. + +**\$Um (read-only)** + +: Equivalent to **monnum(today())**. + +**\$Uw (read-only)** + +: Equivalent to **wkdaynum(today())**. + +**\$Uy (read-only)** + +: Equivalent to **year(today())**. + +**\$UseVTColors (read-only)** + +: Set to 1 if the **-@** or **-cc** options were used; 0 otherwise. + +**\$UseBGVTColors (read-only)** + +: Set to 1 if the **-@,,1** option was used; 0 otherwise. + +**\$Use256Colors (read-only)** + +: Set to 1 if the **-@1** option was used; 0 otherwise. + +**\$UseTrueColors (read-only)** + +: Set to 1 if the **-@2** option was used; 0 otherwise. + +**\$TerminalBackground (read-only)** + +: Returns -1 if the terminal background color could not be determined, + 0 if it was found to be dark (or was specified as dark with the + **-@,0** option) or 1 if it was found to be light (or specified as + light with the **-@,1** option.) The terminal background is + considered to be \"dark\" if the average of the red, green and blue + components is at most 85 out of 255, and if the maximum of any + component is at most 128 out of 255. + +**\$WarningLevel (STRING type)** + +: As new versions of **Remind** are released, new warnings may be + added. If your formerly-fine scripts suddenly start issuing warnings + when you upgrade **Remind**, then as a *stopgap* measure, you may + set **\$WarningLevel** to a string of the form *AB*.*CD*.*EF* where + *AB*, *CD* and *EF* are pairs of decimal digits. This will suppress + any warnings that were introduced *after* **Remind** version + *AB*.*CD*.*EF*. If you do not set **\$WarningLevel**, then it + defaults to the current version of **Remind**, meaning all warnings + will be issued. + + For example, if you want the warnings you receive limited to what + Remind 05.00.00 would have produced, use: + + SET $WarningLevel "05.00.00" + + We do *not* recommend setting **\$WarningLevel** as a matter of + course; you should use it to suppress warnings only for as long as + it takes for you to fix your remind scripts so they no longer cause + warnings to be emitted. + +Note: If any of the calendar modes are in effect, then the values of +\$Daemon, \$DontFork, \$DontTrigAts, \$DontQueue, \$HushMode, +\$IgnoreOnce, \$InfDelta, and \$NextMode are not meaningful. + +# SAVING AND RESTORING VARIABLES + +Just as with the OMIT context, you can save and restore the values of +global variables and all (modifiable) system variables. For example: + + SET a 1 + UNSET b + + # Save variables + PUSH-VARS $DefaultColor $AddBlankLines a b + + SET $DefaultColor "255 0 0" + SET $AddBlankLines 0 + SET a 3 + SET b 4 + REM MSG Hello + + # Restore all the variables we pushed earlier + POP-VARS + + # Now the changes to $DefaultColor and $AddBlankLines + # have been undone. Additionally, a is restored to 1 + # and b is unset + +As you see from the example, PUSH-VARS takes a list of global variable +names and/or system variable names. You can save the values of any +*modifiable* system variables and any global variable. You can even push +global variables that are not set. + +The POP-VARS command restores the values of all system variables and +global variables that were pushed by the most recent PUSH-VARS command. +If you push an undefined global variable, then that variable is unset by +the POP-VARS command. + +# BUILT-IN FUNCTIONS + +**Remind** has a plethora of built-in functions. The syntax for a +function call is the same as in C - the function name, followed a +comma-separated list of arguments in parentheses. Function names are not +case-sensitive. If a function takes no arguments, it must be followed by +\"()\" in the function call. Otherwise, **Remind** will interpret it as +a variable name, and probably not work correctly. + +In the descriptions below, short forms are used to denote acceptable +types for the arguments. The characters \"i\", \"s\", \"d\", \"t\" and +\"q\" denote **INT**, **STRING**, **DATE**, **TIME** and **DATETIME** +arguments, respectively. If an argument can be one of several types, the +characters are concatenated. For example, \"di_arg\" denotes an argument +that can be a **DATE** or an **INT**. \"x_arg\" denotes an argument that +can be of any type. The type of the argument is followed by an +underscore and an identifier naming the argument. + +The built-in functions are: + +**\_(s_message)** + +: Returns the translation table entry for *message*. If there is no + such translation table entry, then returns *message* unmodified. For + example, consider this sequence: + + TRANSLATE "Goodbye" "Tot ziens" + SET a _("Goodbye") + + After those two lines have been executed, the variable **a** will be + set to \"Tot ziens\". See the section THE TRANSLATION TABLE for more + information. + + In the body of a reminder, the substitution sequence + **%(**_text_**)** is (almost) the equivalent of + **\[\_(\"**_text_**\")\]**. Therefore, the following reminders are + almost equivalent: + + + REM MSG %(Goodbye) + REM MSG [_("Goodbye")] + + The only difference is that if \_(\"Goodbye\") contains a **%** + sign, then that result will be run through the substitution filter, + whereas in the first reminder, it will not. That is because the + second **REM** command performs expression pasting followed by a + substitution filter pass, while the first one performs the + translation as part of the substitution filter (and does not make a + second substitution filter pass.) + +**abs(i_num)** + +: Returns the absolute value of *num*. + +**access(s_file, si_mode)** + +: Tests the access permissions for the file *file*. *Mode* can be a + string, containing a mix of the characters \"rwx\" for read, write + and execute permission testing. Alternatively, *mode* can be a + number as described in the UNIX **access**(2) system call. The + function returns 0 if the file can be accessed with the specified + *mode*, and -1 otherwise. + +**adawn(\[dq_date\])** + +: Returns the time of \"astronomical dawn\" on the specified *date*. + If *date* is omitted, defaults to **today()**. If a *datetime* + object is supplied, only the date component is used. + +**adusk(\[dq_date\])** + +: Returns the time of \"astronomical twilight\" on the specified + *date*. If *date* is omitted, defaults to **today()**. + +**ampm(tq_time \[,s_am \[,s_pm \[,i_lz\]\]\])** + +: Returns a **STRING** that is the result of converting *time* (which + is either a **TIME** or a **DATETIME** object) to \"AM/PM\" format. + The optional arguments *am* and *pm* are the strings to append in + the AM and PM case, respectively; they default to \"AM\" and \"PM\". + The optional argument *lz* specifies whether or not the hour should + be padded to two digits with a leading zero. If *lz* is zero, then a + leading 0 is not added; otherwise, the hour is padded out to two + digits with a leading zero. If not supplied, *lz* defaults to zero. + + The function obeys the system variables \$DateSep, \$TimeSep and + \$DateTimeSep when formatting its output. Here are some examples of + its output: + + ampm(0:22) returns "12:22AM" + ampm(17:45, "am", "pm") returns "5:45pm" + ampm(17:45, "am", "pm", 1) returns "05:45pm" + ampm('2020-03-14@21:34') returns "2020-03-14@9:34PM" + +**ansicolor(i_red, i_green, i_blue \[,i_bg \[,i_clamp\]\])** + +: Returns a **STRING** that contains an ANSI escape sequence for + changing the terminal text color. The parameters *red*, *green* and + *blue* are integers from 0 to 255 specifying the value of the + respective color component. As a special case, all three values can + be -1, in which case the ANSI sequence \"ESC\[0m\" is returned, + which resets all text attributes to normal. + + The string returned by **ansicolor** depends on the color mode that + **Remind** is running in, as specified by the **-@** option. If + color mode is not enabled, then **ansicolor** always returns the + empty string. Otherwise, it returns the escape sequence that best + approximates the color according to the **-@** color mode. + + The optional *bg* argument is either 0 or 1. If 0 (the default), + then the foreground color is set. If 1, then the background color is + set. Note that setting the background color only works in 256-color + or true-color mode. + + The optional *clamp* argument is either 0 or 1. If 0 (the default), + then colors are not adjusted based on the terminal background color. + If 1, then **Remind** attempts to adjust dark or bright colors so + they have enough contrast to be visible in the terminal. + + The first three arguments may alternatively be specified as a string + consisting of three space-separated numbers, as in this example: + \"128 128 0\" + + As a special case, **ansicolor(\"\")** is equivalent to + **ansicolor(-1,-1,-1)** and returns the ANSI sequence to reset all + text attributes to normal. + + Note that inserting ANSI color sequences in calendar mode *will* + produce garbled results. Therefore, we recommend defining functions + such as the ones below that return the empty string in calendar + mode: + + IF $CalMode + FSET fg(r,g,b) "" + FSET bg(r,g,b) "" + ELSE + FSET fg(r,g,b) ansicolor(r,g,b) + FSET bg(r,g,b) ansicolor(r,g,b,1) + ENDIF + REM [fg(255,0,0)][bg(64,64,64)]Red on Gray[fg(-1,-1,-1)] in agenda mode + REM SPECIAL COLOR 0 255 0 Green in agenda and calendar mode + + If you use the **ansicolor** function, don\'t forget to reset the + color back to normal with **ansicolor(-1,-1,-1)** or subsequent + reminders will continue to be colored. + +**args(s_fname)** + +: Returns the number of arguments expected by the user-defined + function *fname*, or -1 if no such user-defined function exists. + Note that this function examines only user-defined functions, not + built-in functions. Its main use is to determine whether or not a + particular user-defined function has been defined previously. The + **args()** function is available only in versions of **Remind** from + 03.00.04 and up. + +**asc(s_string)** + +: Returns an **INT** that is the ASCII code of the first byte in + *string*. As a special case, **asc(\"\")** returns 0. For UTF-8 + strings, this will return the UTF-8 byte with which the string + begins, which is not likely to be very useful. + +**codepoint(s_string)** + +: Returns an **INT** that is the code point of the first character in + *string*, treating multi-byte characters correctly. As a special + case, **codepoint(\"\")** returns 0. + +**baseyr()** + +: Returns the \"base year\" that was compiled into **Remind** + (normally 1990.) All dates are stored internally as the number of + days since 1 January of **baseyr()**. + +**catch(x_arg1, x_arg2)** + +: Evaluates **arg1** and if no error occurs, returns the resulting + value. If an error occurs during the evaluation of **arg1**, + evaluates and returns **arg2**. Note that **catch()** can only catch + run-time *evaluation* errors. It cannot catch *syntax* errors. + + Here are some examples: + + catch(4/2, 33) => 2, because 4/2 evaluates without error + catch(4/0, 33) => 33, because 4/0 gives a divide-by-zero error + catch(2, 1/0) => 2, and the second argument is not evaluated + catch(4/0, 1/0) results in a divide-by-zero error + + Note in the last example that catch does *not* protect you from + errors in the evaluation of **arg2**. + +**catcherr()** + +: Returns a string representing the error message from the most + recently-evaluated **catch()** function whose first argument yielded + an error. Note that the error message is always in English even if + **Remind** has been localized; this lets you reliably test the + return value. Here are some examples: + + # No "catch" invocations yet: sets a to "Ok" + set a catcherr() + + # Sets b to "oops" and a to "Division by zero" + set b catch(1/0, "oops") + set a catcherr() + + # Sets b to 1 and a to "Division by zero". The catch error + # is never cleared by a non-error catch() + set b catch(4-3, "not-evaluated") + set a catcherr() + +**char(i_i1 \[,i_i2\...\])** + +: This function can take any number of **INT** arguments. It returns a + **STRING** consisting of the bytes specified by the arguments. It is + easy to create invalid UTF-8 sequences; **char** does not check for + this. Note that none of the arguments can be 0, unless there is only + one argument. As a special case, **char(0)** returns \"\". + +**mbchar(i_i1 \[,i_i2\...\])** + +: This function can take any number of **INT** arguments. It returns a + **STRING** consisting of the characters specified by the arguments. + Any codepoint may be supplied and a correct multi-byte character + string will be returned. Note that none of the arguments can be 0, + unless there is only one argument. As a special case, **mbchar(0)** + returns \"\". Additionally, no argument may be a negative number. + +**choose(i_index, x_arg1 \[,x_arg2\...\])** + +: **Choose** must take at least two arguments, the first of which is + an **INT**. If *index* is *n*, then the *n*th subsequent argument is + returned. If *index* is less than 1, then *arg1* is returned. If + *index* is greater than the number of subsequent arguments, then the + last argument is returned. Examples: + +```{=html} + +``` + choose(0, "foo", 1:13, 1000) returns "foo" + choose(1, "foo", 1:13, 1000) returns "foo" + choose(2, "foo", 1:13, 1000) returns 1:13 + choose(3, "foo", 1:13, 1000) returns 1000 + choose(4, "foo", 1:13, 1000) returns 1000 + +> Note that only the first argument and the chosen result are evaluated. +> Any non-chosen arguments will not be evaluated. + +**coerce(s_type, x_arg)** + +: This function converts *arg* to the specified *type*, if such + conversion is possible. *Type* must be one of \"INT\", \"STRING\", + \"DATE\", \"TIME\" or \"DATETIME\" (case-insensitive). The + conversion rules are as follows: + + If *arg* is already of the *type* specified, it is returned + unchanged. + + If *type* is \"STRING\", then *arg* is converted to a string + consisting of its printed representation. + + If *type* is \"DATE\", then an **INT** *arg* is converted by + interpreting it as the number of days since 1 January **baseyr()**. + A **STRING** *arg* is converted by attempting to read it as if it + were a printed date. A **DATETIME** is converted to a date by + dropping the time component. A **TIME** *arg* cannot be converted to + a date. + + If *type* is \"TIME\", then an **INT** *arg* is converted by + interpreting it as the number of minutes since midnight. A + **STRING** *arg* is converted by attempting to read it as if it were + a printed time. A **DATETIME** is converted to a time by dropping + the date component. A **DATE** *arg* cannot be converted to a time. + + If *type* is \"DATETIME\", then an **INT** *arg* is converted by + interpreting it as the number of minutes since midnight, 1 January + **baseyr()**. A **STRING** is converted by attempting to read it as + if it were a printed datetime. Other types cannot be converted to a + datetime. + + If *type* is \"INT\", then **DATE**, **TIME** and **DATETIME** + arguments are converted using the reverse of procedures described + above. A **STRING** *arg* is converted by parsing it as an integer. + +**columns(\[s_arg\])** + +: If called with no arguments, **columns()** behaves as follows: If + standard output is a TTY, returns the width of the terminal in + columns. If standard output is not a TTY, attempts to open + \"/dev/tty\" to obtain the terminal size. If this fails, returns -1. + + If called with a single string argument, **columns(str)** returns + the number of columns **str** will occupy if printed to a terminal. + ANSI color-changing sequences occupy zero columns whereas some + Unicode characters occupy two columns. **columns(str)** takes all of + that into account. Note that if **Remind** was compiled without + Unicode support, **columns(str)** returns a type mismatch error. + + The result of **columns(str)** may be less than, equal to, or + greater than the result of **mbstrlen(str)**. This is because some + Unicode characters are so-called combining characters that add one + to the character length, but don\'t occupy any columns on their own. + And other Unicode characters are double-width characters that add + one to the character length, but two to the number of display + columns. + +**const(x_arg)** + +: Evaluates *arg* and returns it, but also ensures that the result is + marked \"constant\". Use with caution; incorrect use could result in + unwanted reminders being purged in Purge Mode. See also the section + \"NON-CONSTANT EXPRESSIONS\" + +**current()** + +: Returns the current date and time as a DATETIME object. This may be + the actual date and time, or may be the date and time supplied on + the command line. + +**date(i_y, si_m, i_d)** + +: The **date()** function returns a **DATE** object with the year, + month and day components specified by *y*, *m* and *d*. The month + can be specified as an integer from 1 to 12, or a string that is the + name of a month. + +**datepart(dq_datetime)** + +: Returns a **DATE** object representing the date portion of + *datetime*. + +**datetime(args)** + +: The **datetime()** function can take anywhere from two to five + arguments. It always returns a DATETIME generated from its + arguments. + + If you supply two arguments, the first must be a DATE and the second + a TIME. + + If you supply three arguments, the first must be a DATE and the + second and third must be INTs. The second and third arguments are + interpreted as hours and minutes and converted to a TIME. + + If you supply four arguments, they are interpreted as year, month, + day and time. Year and day must be INTs. Month may be an INT from 0 + to 12 or a string naming a month. Time must be a TIME. + + Finally, if you supply five arguments, they must all be INTs and are + interpreted as year, month, day, hour and minute. (Actually, month + can also be a string that is the name of a month.) + +**dawn(\[dq_date\])** + +: Returns the time of \"civil dawn\" on the specified *date*. If + *date* is omitted, defaults to **today()**. If a *datetime* object + is supplied, only the date component is used. + +**day(dq_date)** + +: This function takes a **DATE** or **DATETIME** as an argument, and + returns an **INT** that is the day-of-month component of *date*. + +**daysinmon(si_m, i_y) or daysinmon(dq_date)** + +: Returns the number of days in month *m* (1-12) of the year *y*. The + first argument can either be an integer from 1 to 12, or a string + that is the name of a month. If given a single DATE or DATETIME + argument, returns the number of days in the month containing the + argument. + +**defined(s_var)** + +: Returns 1 if the variable named by *var* is defined, or 0 if it is + not. + + Note that **defined()** takes a **STRING** argument; thus, to check + if variable X is defined, use: + + defined("X") + + and not: + + defined(X) + + The second example will attempt to evaluate X, and will return an + error if it is undefined or not of type **STRING**. + +**dosubst(s_str \[,d_date \[,t_time\]\]) or dosubst(s_str \[,q_datetime\])** + +: Returns a **STRING** that is the result of passing *str* through the + substitution filter described earlier. The parameters *date* and + *time* (or *datetime*) establish the effective trigger date and time + used by the substitution filter. If *date* and *time* are omitted, + they default to **today()** and **now()**. + + Note that if *str* does not end with \"%\", a newline character will + be added to the end of the result. Also, calling **dosubst()** with + a *date* that is in the past (i.e., if *date* \< **today()**) will + produce undefined results. + + **Dosubst()** is only available starting from version 03.00.04 of + **Remind**. + +**dusk(\[dq_date\])** + +: Returns the time of \"civil twilight\" on the specified *date*. If + *date* is omitted, defaults to **today()**. + +**easterdate(\[dqi_arg\])** + +: If *arg* is an **INT**, then returns the date of Easter Sunday for + the specified year. If *arg* is a **DATE** or **DATETIME**, then + returns the date of the next Easter Sunday on or after *arg*. (The + time component of a datetime is ignored.) If *arg* is omitted, then + it defaults to **today()**. + + Note that **easterdate** computes the Western Easter. For the + Orthodox Easter date, see **orthodoxeaster**. + +**escape(s_string \[,i_add_quotes\])** + +: Returns a **STRING** that is the same as the input string, but with + all special characters backslashed-escaped. For example, the + following command: + + set a escape("foo" + char(10) + "bar") + + will set a to \"foo\\nbar\" where \"\\n\" is the literal sequence + \"\\\" followed by \"n\". This is useful if you want to compute a + string in a pasted-in expression that **Remind** will then parse as + a quoted string again, such as in a TRANSLATE command or an INFO + clause. + + If the optional *add_quotes* argument is supplied and is non-zero, + then the return value from **escape** will include surrounding + double-quotes. + +**eval(s_arg)** + +: Parses the string *arg* as an expression and evaluates it, returning + the result. Note that any variable names in the parsed expression + refer to global variables, not any local variables. For example, + consider this: + +> SET x 2 +> FSET f(x) x + eval("1 + x") +> REM MSG F is [f(9)] +> +> The result will be \"F is 12\" because the reference to *x* inside the +> **eval()** argument refers to the *global* variable *x* and not the +> function argument. +> +> Note that for safety, **RUN** is disabled during the evaluation of +> **eval()**, which means you can\'t use the **shell()** function from +> within an **eval()**. + +**evaltrig(s_trigger \[,dq_start\])** + +: Evaluates *trigger* as if it were a REM or IFTRIG trigger + specification and returns the trigger date as a **DATE** (or as a + **DATETIME** if there is an **AT** clause.) Returns a negative + **INT** if no trigger could be computed. + + Normally, **evaltrig** finds a trigger date on or after today. If + you supply the *start* argument, then it scans starting from there. + + For example, the expression: + + evaltrig("Mon 1", '2008-10-07') + + returns \'2008-11-03\', since that is the first date on or after 7 + October 2008 that satisfies \"Mon 1\". + + If you want to see how many days it is from the first Monday in + October, 2008 to the first Monday in November, 2008, use: + + evaltrig("Mon 1", '2008-11-01') - evaltrig("Mon 1", '2008-10-01') + + and the answer is 28. The trigger argument to **evaltrig** can have + all the usual trigger clauses (**OMIT**, **AT**, **SKIP**, etc.) but + *cannot* have a **SATISFY**, **MSG**, etc. reminder-type clause. + +**filedate(s_filename)** + +: Returns the modification date of *filename*. If *filename* does not + exist, or its modification date is before the year **baseyr()**, + then 1 January of **baseyr()** is returned. + +**filedatetime(s_filename)** + +: Returns the modification date and time of *filename*. If *filename* + does not exist, or its modification date is before the year + **baseyr()**, then midnight, 1 January of **baseyr()** is returned. + +**filedir()** + +: Returns the directory that contains the current file being + processed. It may be a relative or absolute pathname, but is + guaranteed to be correct for use in an **INCLUDE** command as + follows: + +```{=html} + +``` + INCLUDE [filedir()]/stuff + +> This includes the file \"stuff\" in the same directory as the current +> file being processed. Note that this workaround is no longer necessary +> because **DO stuff** will achieve the same goal. +> +> Note that if the currently-processing reminders file was specified as +> a symbolic link, then **filedir()** returns the directory containing +> the symbolic link and *not* the directory containing the target of the +> symbolic link. You should avoid using symbolic links to files unless +> both the symbolic link and its target happen to be in the same +> directory. + +**filename()** + +: Returns (as a **STRING**) the name of the current file being + processed by **Remind**. Inside included files, returns the name of + the included file. + +**getenv(s_envvar)** + +: Similar to the **getenv**(2) system call. Returns a string + representing the value of the specified environment variable. + Returns \"\" if the environment variable is not defined. Note that + the names of environment variables are generally case-sensitive; + thus, getenv(\"HOME\") is not the same as getenv(\"home\"). + +**hebdate(i_day, s_hebmon \[,idq_yrstart \[,i_jahr \[,i_aflag\]\]\])** + +: Support for Hebrew dates - see the section \"THE HEBREW CALENDAR\" + +**hebday(dq_date)** + +: Support for Hebrew dates - see the section \"THE HEBREW CALENDAR\" + +**hebmon(dq_date)** + +: Support for Hebrew dates - see the section \"THE HEBREW CALENDAR\" + +**hebyear(dq_date)** + +: Support for Hebrew dates - see the section \"THE HEBREW CALENDAR\" + +**hex(i_n)** + +: Returns a **STRING** that is the hexadecimal representation of *n*. + There is no \"0x\" prefix and any letters in the returned value are + uppper-case. + +**hour(tq_time)** + +: Returns the hour component of *time*. + +**htmlescape(s_str)** + +: Returns a modified copy of *str* where \"\<\" is replaced with + \"<\"; \"\>\" is replaced with \">\" and \"&\" is replaced + with \"&\" + +**htmlstriptags(s_str)** + +: Returns a modified copy of *str* where HTML tags are stripped out. + The stripping algorithm is fairly naive; the function starts + stripping characters when it encounters a \"\<\" and it stops + stripping when it encounters a \"\>\". + +**iif(x_test1, x_arg1, \[x_test2, x_arg2,\...\], x_default)** + +: If *test1* is true, returns *arg1*. Otherwise, if *test2* is true, + returns *arg2*, and so on. If all of the *test* arguments are false, + returns *default*. Note that only those arguments needed to + determine the final result are evaluated. This function accepts an + odd number of arguments - note that prior to version 03.00.05 of + **Remind**, it accepted 3 arguments only. The 3-argument version of + **iif()** is compatible with previous versions of **Remind**. + +**index(s_search, s_target \[,i_start)** + +: Returns an **INT** that is the location of *target* in the string + *search*. Note that **index** uses *byte* positions, not character + positions, so should not be used on non-ASCII strings. Use + **mbindex** for non-ASCII strings. + +The first byte of a string is numbered 1. If *target* does not exist in +*search*, then 0 is returned. + +> The optional parameter *start* specifies the position in *search* at +> which to start looking for *target*. + +**mbindex(s_search, s_target \[,i_start\])** + +: Similar to **index()** but returns the *character* position rather + than the *byte* position. Also, *start* is interpreted as a 1-based + character index rather than a byte index. + +**isany(arg1 \[,arg2, \..., argN\]);** + +: Returns 1 if the first argument *arg1* is equal to any of the + subsequent arguments *arg2* through *argN*; returns 0 otherwise. + Also returns 0 if called with only one argument. + + As an example, the following two expressions are equivalent: + + (a == b) || (a == c) || (a == d) || (a == e) + + isany(a, b, c, d, e) + +**isconst(x_any)** + +: Evaluates its argument and then *throws away* the value, returning 1 + if the expression is constant or 0 if it is non-constant. Note that + **isconst** does not take into account the context; if *arg* is a + constant expression but evaluated in a non-constant context, + **isconst** will still return 1. For details about constant vs. + non-constant expressions, see the section \"NON-CONSTANT + EXPRESSIONS\" + +**isdst(\[d_date \[,t_time\]\]) or isdst(q_datetime)** + +: Returns a positive number if daylight saving time is in effect on + the specified date and time. *Date* defaults to **today()** and + *time* defaults to midnight. + + Note that this function is only as reliable as the C run-time + library functions. It is available starting with version 03.00.07 of + **Remind**. + +**isleap(idq_arg)** + +: Returns 1 if *arg* is a leap year, and 0 otherwise. *Arg* can be an + **INT**, **DATE** or **DATETIME** object. If a **DATE** or + **DATETIME** is supplied, then the year component is used in the + test. + +**isomitted(dq_date)** + +: Returns 1 if *date* is omitted, given the current global **OMIT** + context. Returns 0 otherwise. (If a datetime is supplied, only the + date part is used.) Note that any local **OMIT** or **OMITFUNC** + clauses are *not* taken into account by this function. + +**language()** + +: Returns a **STRING** naming the compiled-in language supported by + **Remind**. **Remind** used to support compiled-in support for other + languages, but now all localization is done at run-time. As such, + this function always returns \"English\". However, the expression + **\_(\"LANGID\")** returns the two-character ISO 639 language code + of any language pack in effect, assuming the language pack author + has written the localization correctly! + +**localtoutc(q_datetime)** + +: Given a **DATETIME** object interpreted in the local time zone, + return a **DATETIME** object that expresses the same time in UTC. + +**lower(s_string)** + +: Returns a **STRING** with all upper-case characters in *string* + converted to lower-case. + +**max(x_arg1 \[,x_arg2\...)** + +: Can take any number of arguments, and returns the maximum. The + arguments can be of any type, but must all be of the same type. They + are compared as with the \> operator. + +**min(x_arg1 \[,x_arg2\...)** + +: Can take any number of arguments, and returns the minimum. The + arguments can be of any type, but must all be of the same type. They + are compared as with the \< operator. + +**minsfromutc(\[d_date \[,t_time\]\]) or minsfromutc(q_datetime)** + +: Returns the number of minutes from Universal Time Coordinated + (formerly GMT) to local time on the specified date and time. *Date* + defaults to **today()** and *time* defaults to midnight. If local + time is before UTC, the result is negative. Otherwise, the result is + positive. + + Note that this function is only as reliable as the C run-time + library functions. It is available starting with version 03.00.07 of + **Remind**. + +**minute(tq_time)** + +: Returns the minute component of *time*. + +**mon(dqis_arg)** + +: If *arg* is of **DATE** or **DATETIME** type, returns a string that + names the month component of the date. If *arg* is an **INT** from 1 + to 12, returns a string that names the month. If *arg* is a + **STRING**, returns the name of the month. This last case might + sound silly, but for example: + + mon("Mar") + + will return \"March\", the full name of the month. + +**monnum(dq_date)** or **monnum(s_str)** + +: Returns an **INT** from 1 to 12, representing the month component of + *date*. If a **STRING** is supplied rather than a **DATE** or + **DATETIME**, then if *str* is a valid month name (or minimum + 3-character abbreviation of a month name) then the corresponding + month number is returned. If *str* is not a valid month name, then + an error occurs. + +**moondate(i_phase \[,d_date \[,t_time\]\]) or moondate(i_phase, q_datetime)** + +: This function returns the date of the first occurrence of the phase + *phase* of the moon on or after *date* and *time*. *Phase* can range + from 0 to 3, with 0 signifying new moon, 1 first quarter, 2 full + moon, and 3 third quarter. If *date* is omitted, it defaults to + **today()**. If *time* is omitted, it defaults to midnight. + + For example, the following returns the date of the next full moon: + + SET fullmoon moondate(2) + +**moontime(i_phase \[,d_date \[,t_time\]\]) or moontime(i_phase, q_datetime)** + +: This function returns the time of the first occurrence of the phase + *phase* of the moon on or after *date* and *time*. *Phase* can range + from 0 to 3, with 0 signifying new moon, 1 first quarter, 2 full + moon, and 3 third quarter. If *date* is omitted, it defaults to + **today()**. If *time* is omitted, it defaults to midnight. + **Moontime()** is intended to be used in conjunction with + **moondate()**. The **moondate()** and **moontime()** functions are + accurate to within a couple of minutes of the times in \"Old + Farmer\'s Almanac\" for Ottawa, Ontario. + + For example, the following returns the date and time of the next + full moon: + + MSG Next full moon at [moontime(2)] on [moondate(2)] + +**moondatetime(i_phase \[,d_date \[,t_time\]\]) or moondatetime(i_phase, q_datetime)** + +: This function is similar to **moondate** and **moontime**, but + returns a DATETIME result. + +**moonphase(\[d_date \[,t_time\]\]) or moonphase(q_datetime)** + +: This function returns the phase of the moon on *date* and *time*, + which default to **today()** and midnight, respectively. The + returned value is an integer from 0 to 359, representing the phase + of the moon in degrees. 0 is a new moon, 180 is a full moon, 90 is + first-quarter, etc. + +**moonrise(\[d_date\])** + +: This function returns a DATETIME result giving the date and time of + the first moonrise on or after midnight on *date*. If *date* is not + supplied, it defaults to **today()**. + + Note that it is not uncommon for a day to have no moonrise, so the + date part of the return value may not be the same as the *date* + argument. So if you want a calendar of moonrise times, you could use + something like this: + + SET mr moonrise() + IF datepart(mr) == today() + REM NOQUEUE [mr] MSG Moon rises at %3. + ELSE + REM MSG No moonrise today + ENDIF + +**moonrisedir(\[d_date\])** + +: This function returns an INT result giving the direction from which + the moon will rise on the first moonrise on or after midnight on + *date*. If *date* is not supplied, it defaults to **today()**. The + return value ranges from 0 to 359, where 0 is North, 90 is East, 180 + is South and 270 is West. + +**moonset(\[d_date\])** + +: This function is analogous to **moonrise()** but returns the + DATETIME of the next moonset on or after midnight on *date*. + +**moonsetdir(\[d_date\])** + +: This function is analogous to **moonrisedir()** but returns the + direction of moonset. + +**multitrig(s_trig1 \[,s_trig2, \[\... s_trigN\]\])** + +: **multitrig** evaluates each string as a trigger, similar to + **evaltrig**, and returns the *earliest* trigger date that is on or + after **today()**. **multitrig** is similar to **trig** but has the + following difference: + + **trig** returns the *first* trigger date that would have triggered + today, whereas **multitrig** returns the *earliest* trigger date + later than today, regardless of whether it would have triggered + today. + + If no trigger can be computed that is later than **today()**, then + **multitrig** returns 1990-01-01. + + Consider the following examples, assuming that today is Sunday, 24 + March 2024: + + # Returns 1990-01-01 because neither would trigger today + SET a trig("Mon", "Wed") + + # Returns 2024-03-25 because it's the earlier trigger date + SET a multitrig("Mon", "Wed") + + # Returns 2024-03-27 because it's the first that would trigger today + SET a trig("Wed +3", "Mon +3") + + # Returns 2024-03-25 because it's the earlier trigger date + SET a multitrig("Wed +3", "Mon +3") + + # Returns 1990-01-01 because all triggers have expired + SET a multitrig("2000", "2022", "1998", "2023") + + In general, **multitrig** works better with the **Remind** algorithm + than **trig** and should be used most of the time. As an example, + this reminder is issued at the end of each quarter: + + REM [multitrig("Mar 31", "Jun 30", "Sep 30", "Dec 31")] +7 MSG \ + %"End of [ord($Tm/3)] quarter%" is %b. + + If you want the last working day of each quarter, you could use: + + PUSH-OMIT-CONTEXT + OMIT Sat Sun + REM [multitrig("Mar ~1", "Jun ~1", "Sep ~1", "Dec ~1")] +7 MSG \ + %"Last working day of [ord($Tm/3)] quarter%" is %b. + POP-OMIT-CONTEXT + + Note that unlike **evaltrig**, **multitrig** always returns a + **DATE** and never a **DATETIME**. Including an **AT** clause in a + trigger supplied to **multitrig** will result in an error. + +**ndawn(\[dq_date\])** + +: Returns the time of \"nautical dawn\" on the specified *date*. If + *date* is omitted, defaults to **today()**. If a *datetime* object + is supplied, only the date component is used. + +**ndusk(\[dq_date\])** + +: Returns the time of \"nautical twilight\" on the specified *date*. + If *date* is omitted, defaults to **today()**. + +**nonomitted(dq_start, dq_end \[, i_step\] \[,s_wkday\...\])** + +: This function returns the number of *non-*omitted days between + *start* and *end*. If *start* is non-omitted, then it is counted. + *end* is never counted. + + Note that if *end* is less than *start*, the arguments are + effectively swapped, so counting always begins from the older date. + + If the third argument to **nonomitted** is an **INT**, then it must + be greater than zero, and is considered to be the *step* by which + **nonomitted** counts. For example the following expression: + + nonomitted('2023-07-01', '2023-07-29', 7) + + returns the number of non-omitted Saturdays from 2023-07-01 up to + (but not including) 2023-07-29. (Both 2023-07-01 and 2023-07-29 are + Saturdays.) + + If no *step* argument is supplied, then a step of 1 is used. + + In addition to using the global OMIT context, you can supply + additional arguments that are names of weekdays to be omitted. + However, in a **REM** command, any local **OMITFUNC** clause is + *not* taken into account by this function. + + For example, the following line sets a to 11 (assuming no global + OMITs): + + set a nonomitted('2007-08-01', '2007-08-16', "Sat", "Sun") + + because Thursday, 16 August 2007 is the 11th working day (not + counting Saturday and Sunday) after Wednesday, 1 August 2007. + + **nonomitted** has various uses. For example, many schools run on a + six-day cycle and the day number is not incremented on holidays. + Suppose the school year starts with Day 1 on 4 September 2007. The + following reminder will label day numbers in a calendar: + + IF today() >= '2007-09-04' + set daynum nonomitted('2007-09-04', today(), "Sat", "Sun") + REM OMIT SAT SUN SKIP CAL Day [(daynum % 6) + 1] + ENDIF + + Obviously, the answer you get from **nonomitted** depends on the + global OMIT context. If you use movable OMITs, you may get + inconsistent results. + + Here is a more complex use for **nonomitted**. My garbage collection + follows two interleaved 14-day cycles: One Friday, garbage and paper + recycling (\"Black Box\") are collected. The next Friday, garbage + and plastic recycling (\"Blue Box\") are collected. If any of + Monday-Friday is a holiday, collection is delayed until the + Saturday. Here\'s a way to encode these rules: + + fset _garbhol(x) wkdaynum(x) == 5 && nonomitted(x-4, x+1) < 5 + REM 12 November 1999 *14 AFTER OMITFUNC _garbhol MSG Black Box + REM 19 November 1999 *14 AFTER OMITFUNC _garbhol MSG Blue Box + + Here\'s how it works: The \_garbhol(x) user-defined function returns + 1 if and only if (1) *x* is a Friday and (2) there is at least one + OMITted day from the previous Monday up to and including the Friday. + + The first REM statement sets up the 14-day black-box cycle. The + AFTER keyword makes it move collection to the Saturday if \_garbhol + returns 1. The second REM statement sets up the 14-day blue-box + cycle with a similar adjustment made by AFTER in conjunction with + \_garbhol. + +**nonconst(x_arg)** + +: Returns the argument *arg* unchanged, but forces the expression to + be considered *non-constant*. For details, see the section + \"NON-CONSTANT EXPRESSIONS\" + +**now()** + +: Returns the current system time, as a **TIME** type. This may be the + actual time, or a time supplied on the command line. + +**ord(i_num)** + +: Returns a string that is the ordinal number *num*. For example, + **ord(2)** returns \"2nd\", and **ord(213)** returns \"213th\". + + In order to help with localization, if you define a function called + **ordx** that takes a single parameter, then calling **ord**(*n*) + invokes **ordx**(*n*) and returns whatever it does. During the + callback to **ordx**, **RUN** will be disabled. + +**orthodoxeaster(\[dqi_arg\])** + +: If *arg* is an **INT**, then returns the date of Orthodox Easter + Sunday for the specified year. If *arg* is a **DATE** or + **DATETIME**, then returns the date of the next Orthodox Easter + Sunday on or after *arg*. (The time component of a datetime is + ignored.) If *arg* is omitted, then it defaults to **today()**. + + Note that **orthodoxeaster** computes the Orthodox Easter. For the + Western Easter date, see **easterdate**. + +**ostype()** + +: Returns \"UNIX\". **Remind** used to run on OS/2 and MS-DOS, but + does not any longer. + +**pad(x_arg, s_padstr, i_len \[, i_right\])** + +: Converts the first argument *arg* to a string if necessary, and then + if it is shorter than *len* characters, pads to to *len* characters + using as many copies (including partial copies) of *padstr* as + necessary. By default, the string is left-padded, but if *right* is + supplied and non-zero, the string will be right-padded. + + Here are some examples: + + pad(3, "0", 2) --> "03" + pad(465, "0", 2) --> "465" + pad("foo", " ", 5) --> " foo" + pad("foo", " ", 5, 1) --> "foo " + pad("foo", "bar", 11) --> "barbarbafoo" + +```{=html} + +``` + +**plural(i_num \[,s_str1 \[,s_str2\]\])** + +: Can take from one to three arguments. If one argument is supplied, + returns \"s\" if *num* is not 1, and \"\" if *num* is 1. + + If two arguments are supplied, returns *str1* + \"s\" if *num* is + not 1. Otherwise, returns *str1*. + + If three arguments are supplied, returns *str1* if *num* is 1, and + *str2* otherwise. + +**psmoon(i_phase \[,i_size \[,s_note \[,i_notesize\]\]\])** + +: \[DEPRECATED\] Returns a **STRING** consisting of PostScript code to + draw a moon in the upper-left hand corner of the calendar box. + *Phase* specifies the phase of the moon, and is 0 (new moon), 1 + (first quarter), 2 (full moon) or 3 (third quarter). If *size* is + specified, it controls the radius of the moon in PostScript units + (1/72 inch.) If it is not specified or is negative, the size of the + day-number font is used. + +> For example, the following four lines place moon symbols on the +> PostScript calendar: +> +> REM [moondate(0)] PS [psmoon(0)] +> REM [moondate(1)] PS [psmoon(1)] +> REM [moondate(2)] PS [psmoon(2)] +> REM [moondate(3)] PS [psmoon(3)] +> +> If *note* is specified, the text is used to annotate the moon display. +> The font is the same font used for calendar entries. If *notesize* is +> given, it specifies the font size to use for the annotation, in +> PostScript units (1/72 inch.) If *notesize* is not given, it defaults +> to the size used for calendar entries. (If you annotate the display, +> be careful not to overwrite the day number \-- **Remind** does not +> check for this.) For example, if you want the time of each new moon +> displayed, you could use this in your reminder script: +> +> REM [moondate(0)] PS [psmoon(0, -1, moontime(0)+"")] +> +> Note how the time is coerced to a string by concatenating the null +> string. + +**psshade(i_gray)** or **psshade(i_red, i_green, i_blue)** + +: \[DEPRECATED\] Returns a **STRING** that consists of PostScript + commands to shade a calendar box. *Num* can range from 0 (completely + black) to 100 (completely white.) If three arguments are given, they + specify red, green and blue intensity from 0 to 100. Here\'s an + example of how to use this: + + REM Sat Sun PS [psshade(95)] + + The above command emits PostScript code to lightly shade the boxes + for Saturday and Sunday in a PostScript calendar. + + Note that **psmoon** and **psshade** are deprecated; instead you + should use the SPECIAL SHADE and SPECIAL MOON reminders as described + in \"Out-of-Band Reminders.\" + +**realcurrent()** + +: Returns (as a DATETIME) the true date and time of day as provided by + the operating system. This is in contrast to **current()**, which + may return a time supplied on the command line. + +**realnow()** + +: Returns the true time of day as provided by the operating system. + This is in contrast to **now()**, which may return a time supplied + on the command line. + +**realtoday()** + +: Returns the date as provided by the operating system. This is in + contrast to **Remind**\'s concept of \"today\", which may be changed + if it is running in calendar mode, or if a date has been supplied on + the command line. + +**rows()** + +: If standard output is a TTY, returns the height of the terminal in + rows. If standard output is not a TTY, attempts to open \"/dev/tty\" + to obtain the terminal size. If this fails, returns -1. + +**sgn(i_num)** + +: Returns -1 if *num* is negative, 1 if *num* is positive, and 0 if + *num* is zero. + +**shell(s_cmd \[,i_maxlen\])** + +: Executes *cmd* as a system command, and returns the first 511 + characters of output resulting from *cmd*. Any whitespace character + in the output is converted to a space. Note that if **RUN** OFF has + been executed, or the **-r** command-line option has been used, + **shell()** will result in an error, and *cmd* will not be executed. + + When **shell** runs *cmd*, it arranges for *cmd*\'s standard input + file descriptor to be connected to /dev/null. + + If *maxlen* is specified, then **shell()** returns the first + *maxlen* characters of output (rather than the first 511). If + *maxlen* is specified as a negative number, then it defaults to the + value of the system variable **\$MaxStringLen**. + +**shellescape(s_str)** + +: Returns *str* with all shell metacharacters such as \" \", \"\*\", + etc escaped with a backslash. For example: + +```{=html} + +``` + SET a shellescape("a b*? c&d$e") + +> will set **a** to: + + "a\ b\*\?\ c\&d\$e" + +**slide(d_start, i_amt \[, i_step\] \[,s_wkday\...\])** + +: This function is the inverse of **nonomitted**. It adds *amt* (which + can be negative) chunks of *step* days to *start*, *not counting + omitted days*. If *step* is not supplied, then it is assumed to + be 1. Note that only every *step*th day is tested to see if it is + omitted. The optional *wkday* arguments are additional weekday names + to omit. + + Consider this example: + + OMIT 14 May 2009 + SET a slide('2009-05-13', 5, "Sat", "Sun") + + In this case, *a* is set to 2009-05-21. That\'s because we slide + forward by 5 days, not including Thursday, May 14 or Saturday and + Sunday, May 16 and 17. You can go backwards, too, so: + + OMIT 14 May 2009 + SET a slide('2009-05-21', -5, "Sat", "Sun") + + takes *a* back to 2009-05-13. + + Now consider this example: + + OMIT 14 May 2009 + SET a slide('2009-05-07', 2, 7) + + This sets *a* to \'2009-05-28\' because we skip ahead two weeks, not + counting a week where the day we land on happens to be omitted. + Contrast with this: + + OMIT 13 May 2009 + SET a slide('2009-05-07', 2, 7) + + which sets *a* to \'2009-05-21\'. Although 2009-05-13 is omitted, we + don\'t \"land\" on it as we step forward in chunks of 7 days, so we + never see that it is omitted. + +**soleq(i_which \[, dqi_start\])** + +: The **soleq** function computes solstices and equinoxes. The *which* + parameter ranges from 0 to 3, and specifies which event we are + interested in: 0 is the March equinox; 1 is the June solstice; 2 is + the September equinox and 3 is the December solstice. + + The optional *start* parameter can either be an integer specifying + the year of the event we are interested in, or a **DATE** or + **DATETIME** object; if the latter, then **soleq** returns the first + event on or after the date part of the *start* parameter (it ignores + the time component if *start* is a **DATETIME**.) If *start* is not + supplied, then it defaults to **today()**. + + The return value of **soleq()** is a **DATETIME** object specifying + the date and time of the solstice or equinox in the local time zone. + It should be accurate to within 3 minutes or so in the worst case. + + See the included file **\$SysInclude/seasons.rem** for examples of + how to use **soleq()**. + +**stdout()** + +: Returns a string representing where Remind\'s standard output is + going. The return values are one of the following: \"TTY\" if + standard-output is a terminal, \"BLOCKDEV\" if it is a block device + (very unlikely), \"CHARDEV\" if it is a character device (e.g., + /dev/null), \"DIR\" if it is a directory (very unlikely), \"PIPE\" + if it is a pipe or FIFO, \"SYMLINK\" if it is a symlink (very + unlikely), \"SOCKET\" if it is a socket, or \"UNKNOWN\" if it could + not be determined. + + The purpose of **stdout()** is mostly to distinguish between TTY and + non-TTY output; you may wish to change or disable colors if the + output is not going to a TTY. + +**strlen(s_str)** + +: Returns the length of *str* in bytes. If the length of *str* is too + large to represent as an integer, emits a \"Number too high\" error. + Note that **strlen** returns the number of *bytes* in the string, + not the number of *characters*. These numbers are the same for ASCII + strings, but may be different for UTF-8 strings. + +**mbstrlen(str)** + +: Similar to **strlen**, but returns the length of the string in + *characters* rather than *bytes* and is thus safe for use on + multi-byte strings. + +**substr(s_str, i_start \[,i_end\])** + +: Returns a **STRING** consisting of all bytes in *str* from *start* + up to and including *end*. Bytes are numbered from 1. If *end* is + not supplied, then it defaults to the length of *str*. Because + **substr** uses *byte* indexes rather than *character* indexes, it + should not be used on multi-byte strings. + +**mbsubstr(s_str, i_start \[,i_end\])** + +: Similar to **substr** but uses *character* indexes rather than + *byte* indexes, and is thus safe for use on multi-byte strings. + +**sunrise(\[dq_date\])** + +: Returns a **TIME** indicating the time of sunrise on the specified + *date* (default **today()**.) In high latitudes, there may be no + sunrise on a particular day, in which case **sunrise()** returns the + **INT** 0 if the sun never sets, or 1440 if it never rises. + +**sunset(\[dq_date\])** + +: Returns a **TIME** indicating the time of sunset on the specified + *date* (default **today()**.) In high latitudes, there may be no + sunset on a particular day, in which case **sunset()** returns the + **INT** 0 if the sun never rises, or 1440 if it never sets. + + The functions **sunrise()** and **sunset()** are based on an + algorithm in \"Almanac for Computers for the year 1978\" by L. E. + Doggett, Nautical Almanac Office, USNO. They require the latitude + and longitude to be specified by setting the appropriate system + variables. (See \"System Variables\".) The sun functions should be + accurate to within about 4 minutes for latitudes lower than 60 + degrees. The functions are available starting from version 03.00.07 + of **Remind**. + +**time(i_hr, i_min)** + +: Creates a **TIME** with the hour and minute components specified by + *hr* and *min*. + +**timepart(tq_datetime)** + +: Returns a **TIME** object representing the time portion of + *datetime*. + +**timezone(\[dq_datetime\])** + +: Returns a string representing the local time zone name of the given + **DATETIME**. If no argument is supplied, **Remind** uses the value + of **current()**. If a **DATE** rather than **DATETIME** is + supplied, **Remind** uses a time part of 00:00. + +**today()** + +: Returns **Remind**\'s notion of \"today.\" This may be the actual + system date, or a date supplied on the command line, or the date of + the calendar entry currently being computed. + +**trig(s_1 \[,s_2, \...\])** + +: For each string argument s\_*n*, **trig** evaluates s\_*n* as if it + were a REM or IFTRIG trigger specification. If the trigger would + trigger today, then the trigger date is returned and no further + triggers are evaluated. If none of the triggers would trigger today, + then the zero date 1990-01-01 is returned. + + **trig** also has a zero-argument form; this returns the trigger + date of the *most recent* **trig** function that returned a non-zero + trigger date. + + **trig** can be used to make more sophisticated versions of + **IFTRIG**. For example, if you have meetings every Monday in June + and July, and you want warnings 3 days in advance, you could use: + + REM [trig("Mon Jun +3", "Mon July +3")] +3 MSG Meeting %b + + NOTE: We need to repeat the +3 delta outside of the **trig** + function for advance warning to work properly. This is because + **trig** returns a date constant (the trigger date) and the REM + command does not know the details of **trig**\'s arguments. + + Note that because **Remind** has short-circuit logical operators, + something like: + + SET a trig("Mon +7") || trig("Fri +7") + + would set the value of trig() to the date of the following Monday. + Because trig(\"Mon +7\") always returns true, the logical-OR + operator does not bother evaluating trig(\"Fri +7\") which therefore + does not set **trig()**. + + **Important Note**: Because **trig()** always returns an absolute + date, it will **not** work properly with a **SATISFY** clause. + Consider this reminder: + + REM [trig("Mar", "Apr")] SATISFY [$Td == 15] MSG 15 Mar or April + + If we run **Remind** on 5 March 2022, we might expect the trigger + date to be calculated as 15 March 2022\... but that\'s not what + happens. Instead, the **trig** function is evaluated first, and it + returns 2022-03-05. So as far as **Remind** is concerned, the REM + statement becomes: + + REM 2022-03-05 SATISFY [$Td == 15] MSG 15 Mar or April + + and the SATISFY expression is never true. So: *Do not mix* trig() + and SATISFY. + +**trigdate()** + +: Returns the calculated trigger date of the last **REM** or + **IFTRIG** command. If used in the *body* of a **REM** command, + returns that command\'s trigger date. If the most recent **REM** + command did not yield a computable trigger date, returns the integer + 0. + +**trigdatetime()** + +: Similar to trigdate(), but returns a **DATETIME** if the most recent + triggerable **REM** command had an **AT** clause. If there was no + **AT** clause, returns a **DATE**. If no trigger could be computed, + returns the integer 0. See \"MULTI-DAY EVENTS\" for more + information. + +**trigeventstart()** + +: Returns a **DATETIME** representing the start of the most recent + triggerable **REM** command that had an **AT** clause. For events + without a **DURATION** or that do not span multiple days, returns + the same as **trigdatetime()**. If the **REM** command did not have + an **AT** clause, returns the integer -1 (and differs from + **trigdatetime()** in this respect.) See \"MULTI-DAY EVENTS\" for + more information. + +**trigeventstarttz()** + +: Similar to **trigeventstart()** but returns the DATETIME in the time + zone specified by a TZ clause, if one was present. If no TZ clause + was present, returns the same value as **trigeventstart()**. + +**trigeventduration()** + +: Returns a **TIME** representing the duration of the most recent + triggerable **REM** command that had an **AT** and a **DURATION** + clause. If the event does not span multiple days, returns the same + thing as **trigduration()**. If the **REM** command lacked an **AT** + or **DURATION** clause, returns -1. See \"MULTI-DAY EVENTS\" for + more information. + +**trigback()** + +: Returns the \"back\" amount of the last **REM** or **IFTRIG** + command. Returns a positive integer N if the \"back\" is of the form + -N, or a negative integer if it is of the form \--N. If there is no + \"back\", then returns 0. + +**trigbase()** + +: Returns the \"base\" date of the last **REM** or **IFTRIG** command. + If the trigger specification includes *all three* of day, month and + year, then the base date is the date formed from those components. + If the trigger specification lacks one of those components, then 0 + is returned. + + Here is an example of how **trigbase()** might be used: + + REM 2025-05-05 *7 UNTIL 2025-05-26 \ + MSG Meeting: week #[(trigdate() - trigbase())/trigrep()+1] + + On 2025-05-05, it would print: \"Meeting: week #1\". On 2025-05-12, + it would print: \"Meeting: week #2\" and so on. + +**trigdelta()** + +: Returns the \"delta\" amount of the last **REM** or **IFTRIG** + command. Returns a positive integer N if the \"delta\" is of the + form +N, or a negative integer if it is of the form ++N. If there is + no \"delta\", then returns 0. + +**trigtimedelta()** + +: Similar to **trigdelta()**, but returns the delta used in the **AT** + clause of a timed reminder. + +**trigrep()** + +: Returns the \"repeat\" amount of the last **REM** or **IFTRIG** + command. Returns a positive integer N if the \"repeat\" is of the + form \*N. If there is no \"repeat\", then returns 0. + +**trigtimerep()** + +: Similar to **trigrep()**, but returns the repeat used in the **AT** + clause of a timed reminder. + +**trigtz()** + +: If a **TZ** clause was used in the last **REM** or **IFTRIG** + command, returns the time zone name. Otherwise returns the empty + string. + +**trigduration()** + +: Returns (as a TIME type) the **DURATION** parameter of a timed + reminder. If there is no **DURATION** parameter, returns the integer + -1. See \"MULTI-DAY EVENTS\" for more information. + +**trigpriority()** + +: Returns the **PRIORITY** of the last **REM** or **IFTRIG** command. + +**triguntil()** + +: Returns (as a **DATE** type) the **UNTIL** parameter of the last + **REM** or **IFTRIG** command. If there was no **UNTIL** parameter, + returns the integer -1. If there is a **THROUGH** parameter, that + will be returned by **triguntil()** since \"THROUGH yyyy-mm-dd\" is + simply syntactic sugar for \"\*1 UNTIL yyyy-mm-dd\". + +**trigscanfrom()** + +: Returns (as a **DATE** type) the **SCANFROM** parameter of the last + **REM** or **IFTRIG** command. If there was no **SCANFROM** + parameter, returns the integer -1. Note that **FROM** and + **SCANFROM** interact; a reminder that has a \"FROM yyyy-mm-dd\" + parameter will act as if it has a **SCANFROM** parameter whose value + is the maximum of \"yyyy-mm-dd\" and today. + +**trigfrom()** + +: Returns (as a **DATE** type) the **FROM** parameter of the last + **REM** or **IFTRIG** command. If there was no **FROM** parameter, + returns the integer -1. + +**triginfo(s_header)** + +: Returns a **STRING** that is the INFO item associated with the + header *header*. The header should *not* contain a colon. Header + name comparisons are case-insensitive. + + For example, the following will assign \"At home\" to the variable a + and the empty string to variable b: + + REM INFO "Location: At home" MSG test + SET a triginfo("location") + SET b triginfo("no_such_header") + +**trigistodo()** + +: Returns 1 if the last REM command was a **TODO** type or 0 if not. + +**trigcompletethrough()** + +: Returns a **DATE** object that is the COMPLETE-THROUGH date of the + most recent **REM** command. If there was no COMPLETE-THROUGH date, + returns the **INT** 0. + +**trigmaxoverdue()** + +: Returns an **INT** that is the MAX-OVERDUE value of the most recent + **REM** command. If there was no MAX-OVERDUE clause, returns -1. + +**trigger(d_date \[,t_time \[,i_utcflag\]\]) or trigger(q_datetime \[,i_utcflag\])** + +: Returns a string suitable for use in a **REM** command or a + **SCANFROM** or UNTIL clause, allowing you to calculate trigger + dates in advance. Note that in earlier versions of **Remind**, + **trigger** was required to convert a date into something the + **REM** command could consume. However, in this version of + **Remind**, you can omit it. Normally, the *date* and *time* are the + local date and time; however, if *utcflag* is non-zero, the *date* + and *time* are interpreted as UTC times, and are converted to local + time. Examples: + + trigger(\'1993/04/01\') + + returns \"1 April 1993\", + + trigger(\'1994/08/09\', 12:33) + + returns \"9 August 1994 AT 12:33\", as does: + + trigger(\'1994/08/09@12:33\'). + + Finally: + + trigger(\'1994/12/01\', 03:00, 1) + + returns \"30 November 1994 AT 22:00\" for EST, which is 5 hours + behind UTC. The value for your time zone may differ. + + **trigger()** will *never* return a date earlier than \"1 January + 1990\" even if the UTC flag dictates that it should. So do not use + it for reminders before about 2 January 1990 in your local time + zone. I do not anticipate this restriction being a real problem. + +**trigtags()** + +: Returns a comma-separated list of the TAGs associated with the most + recent **REM** command that was triggered. Returns the empty string + if there were no TAGs. If there are multiple tags, they are each + separated by a single comma, not a comma and a space. + +**trigtime()** + +: Returns the time of the last **REM** command with an **AT** clause + *in the system default time zone*. If the last **REM** did not have + an **AT** clause, returns the integer 0. If a **REM** command has an + **AT** clause with a **DURATION**, then you can compute the end time + as **trigtime() + trigduration()**. + +**trigtimetz()** + +: Similar to **trigtime()** but returns the time in the time zone + specified by a TZ clause, if one was present. If no TZ clause was + present, returns the same value as **trigtime()**. + +**trigvalid()** + +: Returns 1 if the value returned by **trigdate()** is valid for the + most recent **REM** command, or 0 otherwise. Sometimes **REM** + commands cannot calculate a trigger date. For example, the following + **REM** command can never be triggered: + +```{=html} + +``` + REM Mon OMIT Mon SKIP MSG Impossible! + +**typeof(x_arg)** + +: Returns \"STRING\", \"INT\", \"DATE\", \"TIME\" or \"DATETIME\", + depending on the type of *arg*. + +**tzconvert(q_datetime, s_srczone \[,s_dstzone\])** + +: Converts **datetime** from the time zone named by **srczone** to the + time zone named by **dstzone**. If **srczone** is the empty string, + then the default system time zone is used as the source zone. If + **dstzone** is omitted or is the empty string, the default system + time zone is used as the destination zone. The return value is a + DATETIME. Time zone names are system-dependent; consult your + operating system for legal values. Here is an example: + + tzconvert('2007-07-08@01:14', "Canada/Eastern", "Canada/Pacific") + + returns + + 2007-07-07@22:14 + + If your system includes the directory /usr/share/zoneinfo, + **Remind** will warn you if you use an invalid time zone name for + **srczone** or **dstzone**. To suppress these warnings, add a \"!\" + to the beginning of the time zone name. + +```{=html} + +``` + +**upper(s_string)** + +: Returns a **STRING** with all lower-case characters in *string* + converted to upper-case. + +**utctolocal(q_datetime)** + +: Given a **DATETIME** object interpreted in UTC, return a + **DATETIME** object that expresses the same time in the local time + zone. + +**value(s_varname \[,x_default\])** + +: Returns the value of the specified variable. For example, + value(\"X\"+\"Y\") returns the value of variable XY, if it is + defined. If XY is not defined, an error results. + + However, if you supply a second argument, it is returned if the + *varname* is not defined. The expression value(\"XY\", 0) will + return 0 if XY is not defined, and the value of XY if it is defined. + Note that **value** evaluates the second argument lazily; it is + *not* evaluated if *varname* is defined. + +**version()** + +: Returns a string specifying the version of **Remind**. For version + 06.02.01, returns \"06.02.01\". It is guaranteed that as new + versions of **Remind** are released, the value returned by + **version()** will strictly increase, according to the rules for + string ordering. + +**weekno(\[dq_date, \[i_wkstart, \[i_daystart\]\]\])** + +: Returns the week number of the year. If no arguments are supplied, + returns the ISO 8601 week number for **today()**. If one argument + *date* is supplied, then returns the ISO 8601 week number for that + date. If two arguments are supplied, then *wkstart* must range from + 0 to 6, and represents the first day of the week (with 0 being + Sunday and 6 being Saturday.). If *wkstart* is not supplied, then it + defaults to 1. If the third argument *daystart* is supplied, then it + specifies when Week 1 starts. If *daystart* is less than or equal to + 7, then Week 1 starts on the first *wkstart* on or after January + *daystart*. Otherwise, Week 1 starts on the first *wkstart* on or + after December *daystart*. If omitted, *daystart* defaults to 29 + (following the ISO 8601 definition.) + +**wkday(dqi_arg)** + +: If *arg* is a **DATE** or **DATETIME**, returns a string + representing the day of the week of the date. If *arg* is an **INT** + from 0 to 6, returns the corresponding weekday (\"Sunday\" to + \"Saturday\"). + +**wkdaynum(dq_date)** or **wkdaynum(s_str)** + +: Returns a number from 0 to 6 representing the day-of-week of the + specified *date*. (0 represents Sunday, and 6 represents Saturday.) + If a **STRING** is supplied, then if *str* is a valid weekday name + (or minimum 3-character abbreviation thereof) then the corresponding + weekday number is returned. If *str* is not a valid weekday name, + then an error occurs. + +**year(dq_date)** + +: Returns a **INT** that is the year component of *date*. + +# MACHINES WITH A 32-BIT TIME_T TYPE + +Internally, the standard C library represents times as a **time_t** +type. This is a signed integer type that counts seconds since midnight, +1 January 1970 UTC. If the **time_t** type is only 32 bits in size, then +the C library cannot represent times after 19 January 2038 at 03:14:07 +UTC. + +To work around this limitation, **Remind** will \"fold\" years greater +than 2037 to a smaller year that begins on the same day and has the same +number of days. This results in a year that\'s very likely to have the +same daylight saving rules as the original year, unless the rules have +changed in the interim (in which case the rule change could not even be +represented on a 32-bit machine, so it\'s moot.) + +**Remind** then does UTC-to-local or local-to-UTC conversions with the +folded year, and then post-conversion corrects it back. This should give +correct results for astronomical functions and conversions between local +and UTC time, providing the daylight saving time rules have not changed +between the folded year and the original year. + +Note that the \$FoldYear system variable is no longer that useful; +**Remind** *always* folds years if it\'s necessary on a 32-bit system. +You can set \$FoldYear to 1 to force **Remind** to fold years even on a +64-bit system, but that\'s useful only for testing and not for +production. + +# MULTI-DAY EVENTS + +If you specify a start time with **AT** and a duration with +**DURATION**, you can create events that span multiple days. Consider +these two REM statements: + + REM 1991-02-13 AT 16:00 DURATION 72:00 MSG 72-hour event + REM 1991-02-13 THROUGH 1991-02-16 AT 16:00 MSG Four events + +The first statement creates a *single* event that starts on 13 February +1991 at 16:00 and runs through 16 February 1991 at 16:00 + +The second statements creates *four separate* events that start at 16:00 +on 13, 14, 15 and 16 February 1991 and have indefinite duration. + +**Remind** handles multi-day events specially. These are the rules: + +On the *first* day of a multi-day event, **trigdatetime()** will return +the starting date and time of the event, and **trigduration()** will +return the original DURATION. + +On each *subsequent* day of a multi-day event, **trigdatetime()** will +return midnight on the day in question, and **trigduration()** will +return the *remaining* duration. Consider this example: + + #!/bin/sh + remind - 12 feb 1991 '*6' <<'EOF' + BANNER % + REM 1991-02-13 AT 16:00 DURATION 72:00 SATISFY 1 + set a trigdatetime() + set b trigduration() + set c trigeventstart() + set d trigeventduration() + MSG now=[today()] dt=[a] dur=[b] estart=[c] edur=[d]% + EOF + +The output is: + + now=1991-02-12 dt=1991-02-13@16:00 dur=72:00 estart=1991-02-13@16:00 edur=72:00 + now=1991-02-13 dt=1991-02-13@16:00 dur=72:00 estart=1991-02-13@16:00 edur=72:00 + now=1991-02-14 dt=1991-02-14@00:00 dur=64:00 estart=1991-02-13@16:00 edur=72:00 + now=1991-02-15 dt=1991-02-15@00:00 dur=40:00 estart=1991-02-13@16:00 edur=72:00 + now=1991-02-16 dt=1991-02-16@00:00 dur=16:00 estart=1991-02-13@16:00 edur=72:00 + now=1991-02-17 dt=1991-02-13@16:00 dur=72:00 estart=-1 edur=-1 + +As you see, the **trigdatetime()** and **trigduration()** functions +return the start time and duration of the *remaining* portion of a +multi-day event, whereas **trigeventstart** and **trigeventduration** +always return the original start and duration of the multi-day event. +Note also that the return value for expired reminders is not reliable; +the fact that **trigeventstart** and **trigeventduration** return -1 in +that case is an implementation artifact. + +**SELF-OVERLAPPING EVENTS** + +A multi-day event has the possibility of \"overlapping itself\". When +this happens, **Remind** prefers the *later* event (only one copy of an +event is ever triggered for a given date.) Consider this example: + + #!/bin/sh + remind - '*5' 10 Feb 1991 <<'EOF' + + BANNER % + REM MON at 0:00 DURATION 192:0 MSG [today()] [trigeventstart()] [trigduration()]% + + EOF + +The output is: + + 1991-02-10 1991-02-04@00:00 48:00 + 1991-02-11 1991-02-11@00:00 192:00 + 1991-02-12 1991-02-11@00:00 168:00 + 1991-02-13 1991-02-11@00:00 144:00 + 1991-02-14 1991-02-11@00:00 120:00 + +Although the event from 1991-02-04 still has 24 hours left on +1991-02-11, the fresh occurrence on 1991-02-11 takes precedences and is +the one that is triggered. + +I do not recommend constructing self-overlapping multi-day events. + +# EXPRESSION PASTING + +An extremely powerful feature of **Remind** is its macro capability, or +\"expression pasting.\" + +In almost any situation where **Remind** is not expecting an expression, +you can \"paste\" an expression in. To do this, surround the expression +with square brackets. For example: + + REM [mydate] MSG foo + +This evaluates the expression \"mydate\", where \"mydate\" is presumably +some pre-computed variable, and then \"pastes\" the result into the +command-line for the parser to process. + +If you want a literal \"\[\" character for some reason, simply use +\"\[\[\". For example: + + REM MSG Here are [[square] brackets! + +A formal description of this is: When **Remind** encounters a +\"pasted-in\" expression, it evaluates the expression, and coerces the +result to a **STRING**. It then substitutes the string for the pasted-in +expression, and continues parsing. Note, however, that expressions are +evaluated only once, not recursively. Thus, writing: + + ["[a+b]"] + +causes **Remind** to read the token \"\[a+b\]\". It does not interpret +this as a pasted-in expression. + +You can use expression pasting almost anywhere. However, there are a few +exceptions: + +o + +: If **Remind** is expecting an expression, as in the **SET** command, + or the **IF** command, you should **not** include square brackets. + For example, use: + +```{=html} + +``` + SET a 4+5 + +and not: + + SET a [4+5] + +o + +: You cannot use expression pasting for the first token on a line. For + example, the following will not work: + +```{=html} + +``` + ["SET"] a 1 + +> This restriction is because **Remind** must be able to unambiguously +> determine the first token of a line for the flow-control commands (to +> be discussed later.) +> +> In fact, if **Remind** cannot determine the first token on a line, it +> assumes that it is a **REM** command. If expression-pasting is used, +> **Remind** assumes it is a **REM** command. Thus, the following three +> commands are equivalent: +> +> REM 12 Nov 1993 AT 13:05 MSG BOO! +> 12 Nov 1993 AT 13:05 MSG BOO! +> [12] ["Nov " + 1993] AT [12:05+60] MSG BOO! + +o + +: You cannot use expression-pasting to determine the type (**MSG**, + **CAL**, etc.) of a **REM** command. You can paste expressions + before and after the **MSG**, etc. keywords, but cannot do something + like this: + + REM ["12 Nov 1993 AT 13:05 " + "MSG" + " BOO!"] + + However, as an escape hatch, the sequence **SPECIAL** *type* means + the same thing as just *type* where *type* is one of MSG, MSF, RUN, + CAL, PS and PSFILE. This lets you do something like this: + + SET type "MSG" + REM 12 Nov 2024 SPECIAL [type] Hello + + You can use this to control the types of your reminders based on + variables you set, how **Remind** is invoked, etc. + +**COMMON PITFALLS WITH EXPRESSION PASTING** + +Remember that extra spaces are not inserted when an expression is +pasted. Thus, something like: + + REM[expr]MSG[expr] + +will probably fail. + +If you use an expression to calculate a *delta* or *back*, ensure that +the result is a positive number. Something like: + + REM +[mydelta] Nov 12 1993 MSG foo + +will fail if *mydelta* happens to be negative. + +# FLOW CONTROL COMMANDS + +**Remind** has commands that control the flow of a reminder script. +Normally, reminder scripts are processed sequentially. However, **IF** +and related commands allow you to process files conditionally, and skip +sections that you don\'t want interpreted. + +**THE IF COMMAND** + +The **IF** command has the following form: + + IF expr + t-command + t-command... + ELSE + f-command + f-command... + ENDIF + +Note that the commands are shown indented for clarity. Also, the +**ELSE** portion can be omitted. **IF** commands can be nested up to a +depth of 64, across *all* levels of INCLUDE. + +If the *expr* evaluates to a non-zero **INT**, a **DATE** that is not +1990-01-01, a **TIME** that is not 00:00, a **DATETIME** that is not +1990-01-01@00:00, or a non-null **STRING**, then the **IF** portion is +considered true, and the *t-commands* are executed. If *expr* evaluates +to zero or null, then the *f-commands* (if the **ELSE** portion is +present) are executed. + +Examples: + + IF defined("want_hols") + INCLUDE /usr/share/remind/holidays + ENDIF + + IF today() > '1992/2/10' + set missed_ap "You missed it!" + ELSE + set missed_ap "Still have time..." + ENDIF + +**THE IFTRIG COMMAND** + +The **IFTRIG** command is similar to an **IF** command, except that it +computes a trigger (as in the **REM** command), and evaluates to true if +a corresponding **REM** command would trigger. Examples: + + IFTRIG 1 Nov + ; Executed on 1 Nov + ELSE + ; Executed except on 1 Nov + ENDIF + + IFTRIG 1 -1 OMIT Sat Sun +4 + ; Executed on last working day of month, + ; and the 4 working days preceding it + ELSE + ; Executed except on above days + ENDIF + +Note that the **IFTRIG** command computes a trigger date, which can be +retrieved with the **trigdate()** function. You can use all of the +normal trigger components, such as **UNTIL**, *delta*, etc. in the +**IFTRIG** command. However, you cannot use a type specifier such as +**CAL**, **MSG** or **SATISFY**; attempting to do so yields a parse +error. + +# USER-DEFINED FUNCTIONS + +In addition to the built-in functions, **Remind** allows you to define +your own functions. The **FSET** command does this for you: + +**FSET** *fname*(*args*) *expr* + +*Fname* is the name of the function, and follows the convention for +naming variables. *Args* is a comma-separated list of arguments, and +*expr* is an expression. *expr* is not evaluated at the time the +function is defined; instead, it is evaluated each time the function is +called. *Args* can be empty, in which case you define a function taking +no parameters. Here are some examples: + + FSET double(x) 2*x + FSET yeardiff(date1, date2) year(date1) - year(date2) + FSET since(x) ord($Ty - x) + +The last function is useful in birthday reminders. For example: + + REM 1 Nov +12 MSG Dean's [since(1984)] birthday is %b. + +Dean was born in 1984. The above example, on 1 November 1992, would +print: + + Dean's 8th birthday is today. + +Similarly, the function is useful in anniversary reminders. For example: + + REM 4 June MSG [since(1989)] anniversary of the Tienanmen Square massacre + +Notes: + +o + +: If you access a variable in *expr* that is not in the list of + arguments, the global value (if any) is used. + +o + +: Function and parameter names are significant to 64 characters. + +o + +: The **value()** function *always* accesses the global value of a + variable, even if it has the same name as an argument. For example: + + fset func(x) value("x") + set x 1 + set y func(5) + + The above sequence sets y to 1, which is the global value of x. + +o + +: User-defined functions may call other functions, including other + user-defined functions. Recursive calls are allowed, but they must + terminate (for example, by using a short-circuit operator or + function that breaks the recursion) or an error will result after a + certain maximum number of recursive calls (by default, 1000.) + +o + +: If a user-defined function has the same name as a built-in function, + it is ignored and the built-in function is used. To prevent + conflicts with future versions of **Remind** (which may define more + built-in functions), you may wish to name all user-defined functions + beginning with an underscore. + +o + +: If a user-defined function is defined in a context where **RUN** is + disabled, then whenever that function is called, **RUN** will be + disabled during its evaluation, *even if* it is called from a + context where **RUN** is enabled. + +To delete a user-defined function, use **FUNSET**. This takes a +space-separated list of user-defined functions to delete. For example, +after the command: + + FUNSET myfunc1 otherfunc thirdfunc + +it is guaranteed that no user-defined functions named myfunc1, otherfunc +or thirdfunc will exist. **Remind** does not issue an error if you try +to **FUNSET** a nonexistent user-defined function; it simply does +nothing in that case. + +You can rename a user-defined function with **FRENAME**. This takes two +names: An old name and a new name. Consider this command: + + FRENAME func_a func_b + +If *func_a* does not exist, the command unsets *func_b* if it is +defined. However, if *func_a* exists, then it is renamed to *func_b*, +and *func_a* is no longer defined. If *func_b* was defined prior to the +**FRENAME** command, then that old definition is lost. + +If either argument to the **FRENAME** command is the name of a built-in +function, the command fails with an error message and does nothing. + +If you define a user-defined function and then later on redefine it, +**Remind** will issue a warning. If you do not want this warning, then +use **FUNSET** to remove the existing definition before you redefine the +function. Alternatively, you can use a \"-\" token before the function +name to suppress \"redefined function\" warnings, as in the following +example: + + FSET - f(x) 2*x + # You must have space before and after the "-" + # This will NOT work: + # FSET -f(x) 2*x + +# SAVING AND RESTORING FUNCTIONS + +Occasionally, it is useful to redefine a function for a specific block +of reminders, but to restore its original definition at the end of that +block. Just as with variables, functions can be pushed onto and popped +off an internal stack. Here is an example: + + PUSH-FUNCS msgprefix + FSET msgprefix(x) "My new prefix: " + INCLUDE block_of_reminders.rem + POP-FUNCS + +The file **block_of_reminders.rem** would be executed with the +**msgprefix** function defined above. After the POP-FUNCS **msgprefix** +would be restored to its previous definition if it had one, or simply +unset if it was not previously defined. + +The command PUSH-FUNCS takes a space-separated list of function names. +All of the named user-defined functions will be saved to an internal +stack. You can even push names that are not defined as any function. + +After a function name has been pushed, **Remind** will not issue a +warning if you redefine it, because presumably the purpose of pushing it +in the first place is to redefine it. + +The command POP-FUNCS restores the definitions of all the user-defined +functions in the corresponding PUSH-FUNCS command. If undefined +functions were pushed onto the stack, then POP-FUNCS makes those +functions undefined again. Here\'s one more example: + + FUNSET a + FSET b(x) 2*x + REM MSG [b(3)] # Outputs 6 + PUSH-FUNCS a b + FSET a(x) 22*x + FSET b(x) 3*x + REM MSG [a(3)] [b(3)] # Outputs 66 9 + POP-FUNCS + REM MSG [a(3)] # Undefined function "a" + REM MSG [b(3)] # Outputs 6 + +# PRECISE SCHEDULING + +The **WARN** keyword allows precise control over advance warning in a +more flexible manner than the *delta* mechanism. It should be followed +by the name of a user-defined function, *warn_function*. + +If a *warn_function* is supplied, then it must take one argument of type +**INT**. **Remind** ignores any delta, and instead calls *warn_function* +successively with the arguments 1, 2, 3, \... + +*Warn_function*\'s return value *n* is interpreted as follows: + +o + +: If *n* is positive, then the reminder is triggered exactly *n* days + before its trigger date. + +o + +: If *n* is negative, then it is triggered *n* days before its trigger + date, *not counting* **OMIT**ted days. + +As an example, suppose you wish to be warned of American Independence +Day 5, 3, and 1 days in advance. You could use this: + + FSET _wfun(x) choose(x, 5, 3, 1, 0) + REM 4 July WARN _wfun MSG American Independence Day is %b. + +**NOTES** + +1 + +: If an error occurs during the evaluation of *warn_function*, then + **Remind** stops calling it and simply issues the reminder on its + trigger date. + +2 + +: If the absolute-values of the return values of *warn_function* are + not monotonically decreasing, **Remind** stops calling it and issues + the reminder on its trigger date. + +3 + +: *Warn_function* should (as a matter of good style) return 0 as the + final value in its sequence of return values. However, a reminder + will *always* be triggered on its trigger date, regardless of what + *warn_function* does. + +Similarly to **WARN**, the **SCHED** keyword allows precise control over +the scheduling of timed reminders. It should be followed by the name of +a user-defined function, *sched_function*. + +If a scheduling function is supplied, then it must take one argument of +type **INT**. Rather than using the **AT** time, time *delta*, and time +*repeat*, **Remind** calls the scheduling function to determine when to +trigger the reminder. The first time the reminder is queued, the +scheduling function is called with an argument of 1. Each time the +reminder is triggered, it is re-scheduled by calling the scheduling +function again. On each call, the argument is incremented by one. + +The return value of the scheduling function must be an **INT** or a +**TIME**. If the return value is a **TIME**, then the reminder is +re-queued to trigger at that time. If it is a positive integer *n*, then +the reminder is re-queued to trigger at the previous trigger time plus +*n* minutes. Finally, if it is a negative integer or zero, then the +reminder is re-queued to trigger *n* minutes before the **AT** time. +Note that there must be an **AT** clause for the **SCHED** clause to do +anything. + +Here\'s an example: + + FSET _sfun(x) choose(x, -60, 30, 15, 10, 3, 1, 1, 1, 1, 0) + REM AT 13:00 SCHED _sfun MSG foo + +The reminder would first be triggered at 13:00-60 minutes, or at 12:00. +It would next be triggered 30 minutes later, at 12:30. Then, it would be +triggered at 12:45, 12:55, 12:58, 12:59, 13:00, 13:01 and 13:02. + +**NOTES** + +1 + +: If an error occurs during the evaluation of *sched_func*, then + **Remind** reverts to using the **AT** time and the *delta* and + *repeat* values, and never calls *sched_func* again. + +2 + +: If processing *sched_func* yields a time earlier than the current + system time, it is repeatedly called with increasing argument until + it yields a value greater than or equal to the current time. + However, if the sequence of values calculated during the repetition + is not strictly increasing, then **Remind** reverts to the default + behaviour and never calls *sched_func* again. + +3 + +: It is quite possible using *sched_func* to keep triggering a + reminder even after the **AT**-time. However, it is not possible to + reschedule a reminder past midnight - no crossing of date boundaries + is allowed. Also, it is quite possible to **not** trigger a reminder + on the **AT** time when you use a scheduling function. However, if + your scheduling function is terminated (for reasons 1 and 2) before + the **AT** time of the reminder, it *will* be triggered at the + **AT** time, because normal processing takes over. + +4 + +: Your scheduling functions should (as a matter of good style) return + 0 when no more scheduling is required. See the example. + +5 + +: All scheduling functions are evaluated *after* the entire Remind + script has been read in. So whatever function definitions are in + effect at the end of the script are used. + +# THE SATISFY CLAUSE + +The form of **REM** that uses **SATISFY** is as follows: + +**REM** *trigger* **SATISFY** *expr* + +The way this works is as follows: **Remind** first calculates a trigger +date, in the normal fashion. Next, it sets **trigdate()** to the +calculated trigger date. It then evaluates *expr*. If the result is not +the null string or zero, processing ends. Otherwise, **Remind** computes +the next trigger date, and re-tests *expr*. This iteration continues +until *expr* evaluates to non-zero or non-null, or until the iteration +limit specified with the **-x** command-line option is reached. + +If *expr* is not satisfied, then **trigvalid()** is set to 0 and the +error message \"Can\'t compute trigger\" is issued. Otherwise, +**trigvalid()** is set to 1. + +This is really useful only if *expr* involves a call to the +**trigdate()** or related functions or system variables; otherwise, +*expr* will not change as **Remind** iterates. In fact, if *expr* is not +a constant and does not call **trigdate()** or related functions or +system variables, then **Remind** will issue a warning. + +An example of the usefulness of **SATISFY**: Suppose you wish to be +warned of every Friday the 13th. Your first attempt may be: + + # WRONG! + REM Fri 13 +2 MSG Friday the 13th is %b. + +But this won\'t work. This reminder triggers on the first Friday on or +after the 13th of each month. The way to do it is with a more +complicated sequence: + + REM 13 SATISFY wkdaynum(trigdate()) == 5 + IF trigvalid() + REM [trigdate()] +2 MSG \ + Friday the 13th is %b. + ENDIF + +You can write the REM statement a little more concisely: + + REM 13 SATISFY $Tw == 5 + +Let\'s see how this works. The **SATISFY** clause iterates through all +the 13ths of successive months, until a trigger date is found whose +day-of-week is Friday (== 5). If a valid date was found, we use the +calculated trigger date to set up the next reminder. + +We could also have written: + + REM Fri SATISFY day(trigdate()) == 13 + +but this would result in more iterations, since \"Fridays\" occur more +often than \"13ths of the month.\" + +Here is another example: Suppose you want to be reminded of something on +the 15th of January, April, July, and October. You could make four +separate reminders, or you could use: + + REM 15 SATISFY [isany($Tm, 1, 4, 7, 10)] MSG 15th Reminder! + +This technique of using one **REM** command to calculate a trigger date +to be used by another command is quite powerful. For example, suppose +you wanted to OMIT Labour day, which is the first Monday in September. +You could use: + + # Note: SATISFY 1 is an idiom for "do nothing" + REM Mon 1 Sept SATISFY 1 + OMIT [trigdate()] + +**CAVEAT:** This *only* omits the *next* Labour Day, not all Labour Days +in the future. This could cause strange results, as the **OMIT** context +can change depending on the current date. For example, if you use the +following command after the above commands: + + REM Mon AFTER msg hello + +the result will not be as you expect. Consider producing a calendar for +September, 1992. Labour Day was on Monday, 7 September, 1992. However, +when **Remind** gets around to calculating the trigger for Tuesday, 8 +September, 1992, the **OMIT** command will now be omitting Labour Day +for 1993, and the \"Mon AFTER\" command will not be triggered. (But see +the description of **SCANFROM** in the section \"DETAILS ABOUT TRIGGER +COMPUTATION.\") + +It is probably best to stay away from computing **OMIT** trigger dates +unless you keep these pitfalls in mind. + +For versions of **Remind** starting from 03.00.07, you can include a +**MSG**, **RUN**, etc. clause in a **SATISFY** clause as follows: + + REM trigger_stuff SATISFY [expr] MSG body + +Note that for this case only, the *expr* after **SATISFY** *must* be +enclosed in square brackets. It must come after all the other components +of the trigger, and immediately before the **MSG**, **RUN**, etc. +keyword. If *expr* cannot be satisfied, then the reminder is not +triggered. + +Thus, the \"Friday the 13th\" example can be expressed more compactly +as: + + REM 13 +2 SATISFY [$Tw == 5] MSG Friday the 13th is %b. + +And you can trigger a reminder on Mondays, Wednesdays and Thursdays +occurring on odd-numbered days of the month with the following: + + REM Mon Wed Thu SATISFY [$Td %2 ] MSG Here it is!!! + +Note that **SATISFY** and **OMITFUNC** can often be used to solve the +same problem, though in different ways. Sometimes a **SATISFY** is +cleaner and sometimes an **OMITFUNC**; experiment and use whichever +seems clearer. + +# POSSIBLY-UNCOMPUTABLE TRIGGERS + +Occasionally, you may wish to suppress the \"Can\'t compute trigger\" +warnings for reminders for which a trigger date cannot be computed. For +example, the following reminder is triggered on a Monday that is not a +holiday if the following Tuesday is a holiday: + + REM Mon SKIP SATISFY [isomitted($T+1)] MSG Work between holidays + +However, if there are no Mondays after today\'s date that satisfy the +condition, **Remind** will print the \"Can\'t compute trigger\" error. +To suppress this, use the **MAYBE-UNCOMPUTABLE** keyword: + + REM MAYBE-UNCOMPUTABLE Mon SKIP SATISFY [isomitted($T+1)] MSG Work between holidays + +It\'s almost never appropriate to use **MAYBE-UNCOMPUTABLE**, but it is +provided for those rare occasions when it makes sense. If you use +**MAYBE-UNCOMPUTABLE** inside the **evaltrig()** function, then +untriggerable triggers return -1. For example: + + SET a evaltrig("MAYBE-UNCOMPUTABLE Mon SKIP OMIT Mon") + +will set a to -1. + +# DEBUGGING REMINDER SCRIPTS + +Although the command-line **-d** option is useful for debugging, it is +often overkill. For example, if you turn on the **-dx** option for a +reminder file with many complex expressions, you\'ll get a huge amount +of output. The **DEBUG** command allows you to control the debugging +flags under program control. The format is: + +**DEBUG** \[+*flagson*\] \[-*flagsoff*\] + +*Flagson* and *flagsoff* consist of strings of the characters +\"shextvlfqnu\" that correspond to the debugging options discussed in +the command-line options section. If preceded with a \"+\", the +corresponding group of debugging options is switched on. Otherwise, they +are switched off. For example, you could use this sequence to debug a +complicated expression: + + DEBUG +x + set a very_complex_expression(many_args) + DEBUG -x + +**THE DUMPVARS COMMAND** + +The command **DUMPVARS** displays the values of variables in memory. Its +format is: + +**DUMPVARS** \[**-c**\] \[*var*\...\] + +If you supply a space-separated list of variable names, the +corresponding variables are displayed. If you do not supply a list of +variables, then all variables in memory are displayed. To dump a system +variable, put its name in the list of variables to dump. If you put a +lone dollar sign in the list of variables to dump, then all system +variables will be dumped. + +If you supply the **-c** flag, then any variable determined to be +constant will have its value followed by \"\\" + +**THE ERRMSG COMMAND** + +The **ERRMSG** command has the following format: + +**ERRMSG** *body* + +The *body* is passed through the substitution filter (with an implicit +trigger date of **today()**) and printed to the error output stream. +Example: + + IF !defined("critical_var") + ERRMSG You must supply a value for "critical_var" + EXIT + ENDIF + +**THE EXIT COMMAND** + +The above example also shows the use of the **EXIT** command. This +causes an unconditional exit from script processing. Any queued timed +reminders are discarded. If you are in calendar mode (described next), +then the calendar processing is aborted. + +If you supply an **INT**-type expression after the **EXIT** command, it +is returned to the calling program as the exit status. Otherwise, an +exit status of 99 is returned. + +**THE FLUSH COMMAND** + +This command simply consists of the word **FLUSH** on a line by itself. +The command flushes the standard output and standard error streams used +by **Remind**. This is not terribly useful to most people, but may be +useful if you run **Remind** as a subprocess of another program, and +want to use pipes for communication. + +# AGENDA MODE JSON OUTPUT + +If you supply the **\--json** argument, then Remind outputs JSON instead +of the normal text output. The JSON output consists of a single JSON +array of zero or more objects. There are three possible types of objects +in the array: + +**banner** + +: The **banner** object, if present, will be the first object in the + array. There will be at most one **banner** object. It contains a + single key, **banner**, whose value is the banner that **Remind** + would normally print in Agenda Mode. + +**noreminders** + +: The **noreminders** object, if present, will be the final object in + the array. There will be at most one **noreminders** object. It + contains a single keym, **noreminders**, whose value is the phrase + \"No reminders.\", possibly localized into a different language. + +**event** + +: All other objects in the array are **event** objects. They are JSON + objects that contain all of the keys described in the **rem2ps**(1) + man page section \"CALENDAR ENTRIES\". However, the + **calendar_body**, **plain_body** and **raw_body** keys will not be + present. + +JSON output can be used by a front-end to display an attractive list of +reminders in Agenda Mode. The \"show today\'s reminders\" feature of +**tkremind** uses the JSON output. + +# CALENDAR MODE + +If you supply the **-c**, **-s** or **-p** command-line option, then +**Remind** runs in \"calendar mode.\" In this mode, **Remind** +interprets the script repeatedly, performing one iteration through the +whole file for each day in the calendar. Reminders that trigger are +saved in internal buffers, and then inserted into the calendar in the +appropriate places. + +If you also supply the **-a** option, then **Remind** will not include +timed reminders in the calendar. + +The **-p** option is used in conjunction with the **Rem2PS** program to +produce a calendar in PostScript format. For example, the following +command will send PostScript code to standard output: + + remind -p .reminders | rem2ps + +You can print a PostScript calendar by piping this to the **lpr** +command. + +If you have a reminder script called \".reminders\", and you execute +this command: + + remind -c .reminders jan 1993 + +then **Remind** executes the script 31 times, once for each day in +January. Each time it executes the script, it increments the value of +**today()**. Any reminders whose trigger date matches **today()** are +entered into the calendar. + +**MSG** and **CAL**-type reminders, by default, have their entire body +inserted into the calendar. **RUN**-type reminders are not normally +inserted into the calendar. However, if you enclose a portion of the +body in the %\"\...%\" sequence, only that portion is inserted. For +example, consider the following: + + REM 6 Jan MSG %"Dianne's birthday%" is %b + +In agenda mode, **Remind** would print \"Dianne\'s birthday is today\" +on 6 January. However, in the calendar mode, only the text \"Dianne\'s +birthday\" is inserted into the box for 6 January. + +If you explicitly use the %\"\...%\" sequence in a **RUN**-type +reminder, then the text between the delimiters is inserted into the +calendar. If you use the sequence %\"%\" in a **MSG** or **CAL**-type +reminder, then no calendar entry is produced for that reminder. + +**PRESERVING VARIABLES** + +Because **Remind** iterates through the script for each day in the +calendar, slow operations may severely reduce the speed of producing a +calendar. + +For example, suppose you set the variables \"me\" and \"hostname\" as +follows: + + SET me shell("whoami") + SET hostname shell("hostname") + +Normally, **Remind** clears all variables between iterations in calendar +mode. However, if certain variables are slow to compute, and will not +change between iterations, you can \"preserve\" their values with the +**PRESERVE** command. Also, since function definitions are preserved +between calendar iterations, there is no need to redefine them on each +iteration. Thus, you could use the following sequence: + + IF ! defined("initialized") + set initialized 1 + set me shell("whoami") + set hostname shell("hostname") + fset func(x) complex_expr + preserve initialized me hostname + ENDIF + +The operation is as follows: On the first iteration through the script, +\"initialized\" is not defined. Thus, the commands between **IF** and +**ENDIF** are executed. The **PRESERVE** command ensures that the values +of initialized, me and hostname are preserved for subsequent iterations. +On the next iteration, the commands are skipped, since initialized has +remained defined. Thus, time-consuming operations that do not depend on +the value of **today()** are done only once. + +Most system variables (those whose names start with \'\$\') are +automatically preserved between calendar iterations. + +Note that for efficiency, **Remind** caches the reminder script (and any +**INCLUDE**d files) in memory when producing a calendar. + +Timed reminders are sorted and placed into the calendar in time order. +These are followed by non-timed reminders. **Remind** automatically +places the time of timed reminders in the calendar according to the +**-b** command-line option. Reminders in calendar mode are sorted as if +the **-g** option had been used; you can change the sort order in +calendar mode by explicitly using the **-g** option to specify a +different order from the default. + +**REPEATED EXECUTION** + +If you supply a *repeat* parameter on the command line, and do not use +the **-c**, **-p**, or **-s** options, **Remind** operates in a similar +manner to calendar mode. It repeatedly executes the reminder script, +incrementing **today()** with each iteration. The same rules about +preserving variables and function definitions apply. Note that using +*repeat* on the command line also enables the **-q** option and disables +any **-z** option. As an example, if you want to see how **Remind** will +behave for the next week, you can type: + + remind .reminders '*7' + +If you want to print the dates of the next 1000 days, use: + + (echo 'banner %'; echo 'msg [today()]%') | remind - '*1000' + +# INITIALIZING VARIABLES ON THE COMMAND LINE + +The **-i** option is used to initialize variables on the **Remind** +command line. The format is **-i**_var=expr_, where *expr* is any +valid expression. Note that you may have to use quotes or escapes to +prevent the shell from interpreting special characters in *expr*. You +can have as many **-i** options as you want on the command line, and +they are processed in order. Thus, if a variable is defined in one +**-i** option, it can be referred to by subsequent **-i** options. + +Note that if you supply a date on the command line, it is not parsed +until all options have been processed. Thus, if you use **today()** in +any of the **-i** expressions, it will return the same value as +**realtoday()** and not the date supplied on the command line. + +Any variables defined on the command line are **preserved** as with the +**PRESERVE** command. + +You should not have any spaces between the **-i** option and the equal +sign; otherwise, strange variable names are created that can only be +accessed with the **value()** or **defined()** functions. + +You can also define a function on the command line by using: + +**-i**_func(args)=definition_ + +Be sure to protect special characters from shell interpretation. + +# MORE ABOUT POSTSCRIPT + +The **PS** and **PSFILE** reminders pass PostScript code directly to the +printer. They differ in that the **PS**-type reminder passes its body +directly to the PostScript output (after processing by the substitution +filter) while the **PSFILE**-type\'s body should simply consist of a +filename. The **Rem2PS** program will open the file named in the +**PSFILE**-type reminder, and include its contents in the PostScript +output. + +The PostScript-type reminders for a particular day are included in the +PostScript output in sorted order of priority. Note that the order of +PostScript commands has a *major* impact on the appearance of the +calendars. For example, PostScript code to shade a calendar box will +obliterate code to draw a moon symbol if the moon symbol code is placed +in the calendar first. For this reason, you should not provide **PS** or +**PSFILE**-type reminders with priorities; instead, you should ensure +that they appear in the reminder script in the correct order. PostScript +code should draw objects working from the background to the foreground, +so that foreground objects properly overlay background ones. If you +prioritize these reminders and run the script using descending sort +order for priorities, the PostScript output will not work. + +All of the PostScript code for a particular date is enclosed in a +**save**-**restore** pair. However, if several PostScript-type reminders +are triggered for a single day, each section of PostScript is not +enclosed in a **save**-**restore** pair - instead, the entire body of +included PostScript is enclosed. + +PostScript-type reminders are executed by the PostScript printer before +any regular calendar entries. Thus, regular calendar entries will +overlay the PostScript-type reminders, allowing you to create shaded or +graphical backgrounds for particular days. + +Before executing your PostScript code, the origin of the PostScript +coordinate system is positioned to the bottom left-hand corner of the +\"box\" in the calendar representing **today()**. This location is +exactly in the middle of the intersection of the bottom and left black +lines delineating the box - you may have to account for the thickness of +these lines when calculating positions. + +Several PostScript variables are available to the PostScript code you +supply. All distance and size variables are in PostScript units (1/72 +inch.) The variables are: + +LineWidth + +: The width of the black grid lines making up the calendar. + +Border + +: The border between the center of the grid lines and the space used + to print calendar entries. This border is normally blank space. + +BoxWidth and BoxHeight + +: The width and height of the calendar box, from center-to-center of + the black gridlines. + +InBoxHeight + +: The height from the center of the bottom black gridline to the top + of the regular calendar entry area. The space from here to the top + of the box is used only to draw the day number. + +/DayFont, /EntryFont, /SmallFont, /TitleFont and /HeadFont + +: The fonts used to draw the day numbers, the calendar entries, the + small calendars, the calendar title (month, year) and the + day-of-the-week headings, respectively. + +DaySize, EntrySize, TitleSize and HeadSize + +: The sizes of the above fonts. (The size of the small calendar font + is *not* defined here.) For example, if you wanted to print the + Hebrew date next to the regular day number in the calendar, use: + +```{=html} + +``` + REM PS Border BoxHeight Border sub DaySize sub moveto \ + /DayFont findfont DaySize scalefont setfont \ + ([hebday(today())] [hebmon(today())]) show + +> Note how /DayFont and DaySize are used. + +Note that if you supply PostScript code, it is possible to produce +invalid PostScript files. Always test your PostScript thoroughly with a +PostScript viewer before sending it to the printer. You should not use +any document structuring comments in your PostScript code. + +# DAEMON MODE + +If you use the **-z** command-line option, **Remind** runs in \"daemon +mode\". In this mode, no \"normal\" reminders are issued. Instead, only +timed reminders are collected and queued, and are then issued whenever +they reach their trigger time. + +In addition, **Remind** wakes up every few minutes to check the +modification date on the reminder script (the filename supplied on the +command line.) If **Remind** detects that the script has changed, it +re-executes itself in daemon mode, and interprets the changed script. If +**Remind** was compiled with support for **inotify**(7), then if the +command-line reminder script is really a directory, **Remind** also +re-executes itself if any of the files in the directory is changed. + +In daemon mode, **Remind** also re-reads the remind script when it +detects that the system date has changed. + +In daemon mode, **Remind** acts as if the **-f** option had been used, +so to run in daemon mode in the background, use: + + remind -z .reminders & + +If you use **sh** or **bash**, you may have to use the \"nohup\" command +to ensure that the daemon is not killed when you log out. + +# PURGE MODE + +If you supply the **-j** command-line option, **Remind** runs in *purge +mode*. In this mode, it tries to purge expired reminders from your +reminder files. + +In purge mode, **Remind** reads your reminder file and creates a new +file by appending \".purged\" to the original file name. Note that +**Remind** *never* edits your original file; it always creates a new +.purged file. + +If you invoke **Remind** against a directory instead of a file, then a +.purged file is created for each \*.rem file in the directory. + +Normally, **Remind** does not create .purged files for INCLUDed files. +However, if you supply a numeric argument after **-j**, then **Remind** +will create .purged files for the specified level of INCLUDE. For +example, if you invoke **Remind** with the argument **-j2**, then +.purged files will be created for the file (or directory) specified on +the command line, any files included by them, and any files included by +those files. However, .purged files will not be created for +third-or-higher level INCLUDE files. + +Determining which reminders have expired is extremely tricky. **Remind** +does its best, but you should always compare the .purged file to the +original file and hand-merge the changes back in. + +**Remind** annotates the .purged file as follows: + +An expired reminder is prefixed with: #!P: Expired: + +In situations where **Remind** cannot reliably determine that something +was expired, you may see the following comments inserted before the +problematic line: + + #!P: Cannot purge SATISFY-type reminders + + #!P: The next IF evaluated false... + #!P: REM statements in IF block not checked for purging. + + #!P: The previous IF evaluated true. + #!P: REM statements in ELSE block not checked for purging + + #!P: The next IFTRIG did not trigger. + #!P: REM statements in IFTRIG block not checked for purging. + + #!P: Next line has expired, but contains expression... please verify + + #!P: Next line may have expired, but contains non-constant expression + #!P: or a relative SCANFROM clause + + #!P! Could not parse next line: Some-Error-Message-Here + + #!P! Problem calculating trigger date + +**Remind** always annotates .purged files with lines beginning with +\"#!P\". If such lines are encountered in the *original* file, they are +not copied to the .purged file. + +If you use the \"Hush\" flag **-h** in conjunction with the \"Purge\" +flag **-j**, then **Remind** does *not* create any of the diagnostic +comments listed above. Instead, the only change it makes to the .purged +file is to mark expired reminders with \"#!P: Expired\". + +# NON-CONSTANT EXPRESSIONS + +In Purge Mode, **Remind** will not mark a REM statement as expired if +its trigger specification contains a non-constant expression. A +non-constant expression is defined as one whose value might differ from +run to run, usually because it depends on the current date, but also if +it depends on something from the environment (such as the output of the +**shell()** function.) + +The use of any of the following in an expression causes **Remind** to +consider it non-constant: + +> **o** +> +> : A global variable that has been assigned the result of a +> non-constant expression, or that has been assigned a value in a +> *non-constant* context (to be described later.) +> +> **o** +> +> : A system variable +> +> **o** +> +> : Certain built-in functions that depend on the current date (for +> example, **today()**) or the environment (for example, +> **shell()**). + +In addition, for the purposes of safely expiring reminders in Purge +Mode, **Remind** considers the following to be non-constant: + +> **o** +> +> : The use of an OMITFUNC +> +> **o** +> +> : The use of a relative SCANFROM + +Whenever a variable is assigned a value, **Remind** tracks whether or +not the expression whose value it was assigned is constant or +non-constant. Additionally, variables that are assigned in a +non-constant context are always assumed to be non-constant. A +non-constant context is the code in the scope of an **IF** statement +where the **IF** expression is non-constant. + +Here are some examples that should make things clear: + + SET d '2025-06-01' # d is constant + REM [d] MSG Hello! # eligible for purging + + SET d today() - 3 # d is non-constant + REM [d] MSG Hello! # not eligible for purging + + IF wkdaynum(today()) == 3 + set d '2025-06-01' # d is non-constant + ELSE + set d '2026-01-01' # d is non-constant + ENDIF + + SET d '2025-06-01' # d is constant + IF today() > today() + 3 # This branch is never taken, but... + SET d '2029-01-01' # d is still marked non-constant + ENDIF + + # Although here d is still '2025-06-01', it is marked + # non-constant because as far as Remind is concerned, + # the IF body *might* have been executed depending on today() + +Note that within the **IF**\...**ENDIF** scope, any assignments are +non-constant because the code flow depends on today\'s date, which could +change in subsequent **Remind** runs. If you want to force a variable to +be treated as constant, no matter what, then use the following just +before you use the variable: + + SET var const(var) + +Variables initialized on the command-line with the **-i** flag are +*always* considered to be non-constant. + +If you have an expired reminder that for some reason you never want +purged, simply use the built-in function **nonconst** somewhere in the +trigger. For example: + + REM 1992-01-01 MSG This will be purged after Jan 1 1992 + REM [nonconst('1992-01-01')] MSG This will never be purged + + REM Wed UNTIL 1993-12-31 MSG This will be purged after 1993 + REM Wed UNTIL [nonconst('1993-12-31')] MSG Never purged + +The **n** debugging flag prints a message to standard error whenever +**Remind** decides that an expression is non-constant. This can produce +a large amount of output, so if you want to find out why **Remind** +considers a specific expression to be non-constant, it\'s best to use +**DEBUG +n** before it and **DEBUG -n** after it to limit the amount of +output. + +# SORTING REMINDERS + +The **-g** option causes **Remind** to sort reminders by trigger date, +time and priority before issuing them. Note that reminders are still +calculated in the order encountered in the script. However, rather than +being issued immediately, they are saved in an internal buffer. When +**Remind** has finished processing the script, it issues the saved +reminders in sorted order. The **-g** option can be followed by up to +four characters that must all be \"a\" or \"d\". The first character +specifies the sort order by trigger date (ascending or descending), the +second specifies the sort order by trigger time and the third specifies +the sort order by priority. If the fourth character is \"d\", the +untimed reminders are sorted before timed reminders. The default is to +sort all fields in ascending order and to sort untimed reminders after +timed reminders. + +In ascending order, reminders are issued with the most imminent first. +Descending order is the reverse. Reminders are always sorted by trigger +date, and reminders with the same trigger date are then sorted by +trigger time. If two reminders have the same date and time, then the +priority is used to break ties. Reminders with the same date, time and +priority are issued in the order they were encountered. + +You can define a user-defined function called SORTBANNER that takes one +**DATE**-type argument. In sort mode, the following sequence happens: + +If **Remind** notices that the next reminder to issue has a different +trigger date from the previous one (or if it is the first one to be +issued), then SORTBANNER is called with the trigger date as its +argument. The result is coerced to a string, and passed through the +substitution filter with the appropriate trigger date. The result is +then displayed. + +Here\'s an example - consider the following fragment: + + # Switch off the normal banner + BANNER % + REM 11 March 1993 ++1 MSG Not so important + REM 17 March 1993 ++7 MSG Way in the future + REM 10 March 1993 MSG Important Reminder + REM 11 March 1993 ++1 MSG Not so important - B + FSET sortbanner(x) iif(x == today(), \ + "***** THINGS TO DO TODAY *****", \ + "----- Things to do %b -----") + +Running this with the **-gaa** option on 10 March 1993 produces the +following output: + + ***** THINGS TO DO TODAY ***** + + Important Reminder + + ----- Things to do tomorrow ----- + + Not so important + + Not so important - B + + ----- Things to do in 7 days' time ----- + + Way in the future + +You can use the **args()** built-in function to determine whether or not +SORTBANNER has been defined. (This could be used, for example, to +provide a default definition for SORTBANNER in a system-wide file +included at the end of the user\'s file.) Here\'s an example: + + # Create a default sortbanner function if it hasn't already + # been defined + if args("sortbanner") != 1 + fset sortbanner(x) "--- Things to do %b ---" + endif + +# MSGPREFIX() AND MSGSUFFIX() + +You can define two functions in your script called **msgprefix()** and +**msgsuffix()**. They should each accept one argument, a number from 0 +to 9999. + +In agenda mode, for **MSG**- and **MSF**-type reminders, the following +sequence occurs when **Remind** triggers a reminder: + +o + +: If **msgprefix()** is defined, it is evaluated with the priority of + the reminder as its argument. The result is printed. It is *not* + passed through the substitution filter. + +o + +: The body of the reminder is printed. + +o + +: If **msgsuffix()** is defined, it is evaluated with the priority of + the reminder as its argument. The result is printed. It is *not* + passed through the substitution filter. + +Here\'s an example: The following definition causes priority-0 reminders +to be preceded by \"URGENT\", and priority-6000 reminders to be preceded +by \"(not important)\". + + fset msgprefix(x) iif(x==0, "URGENT: ", \ + x==6000, "(not important) ", "") + +In Calendar Mode (with the **-c**, **-s** or **-p** options), an +analogous pair of functions named **calprefix()** and **calsuffix()** +can be defined. They work with all reminders that produce an entry in +the calendar (i.e., **CAL**- and possibly **RUN**-type reminders as well +as **MSG**-type reminders.) + +**NOTES** + +Normally, the body of a reminder is followed by a carriage return. Thus, +the results of **msgsuffix()** will appear on the next line. If you +don\'t want this, make sure the output of **msgsuffix** begins with a +backspace. This places the suffix *before* rather than after the +carriage return. (The backspace character itself is stripped out.) Here +is an example: + + FSET msgsuffix(x) char(8) + " - suffix on same line" + +If **Remind** has problems evaluating **msgprefix()**, **msgsuffix()** +or **sortbanner()**, you will see a lot of error messages. For an +example of this, define the following: + + fset msgprefix(x) x/0 + +# COMPILE-TIME SUPPORT FOR OTHER LANGUAGES + +**Remind** used to support compile-time localization to other languages, +but no longer does. All localization is now done at run-time. + +# RUN-TIME SUPPORT FOR OTHER LANGUAGES + +**Remind** has run-time support for other languages, and compile-time +support has been removed in favour of run-time support. + +A number of system variables let you translate various phrases to other +languages. These system variables are: + +**\$Monday, \$Tuesday, \$Wednesday, \$Thursday, \$Friday, \$Saturday, \$Sunday** + +: Set each of these system variables to a string representing the + corresponding day\'s name in your language. Strings must be valid + UTF-8 strings. + +**\$January, \$February, \$March, \$April, \$May, \$June, \$July, \$August, \$September, \$October, \$November, \$December** + +: Set each of these system variables to a string representing the + corresponding month\'s name in your language. Strings must be valid + UTF-8 strings. + +**\$Ago, \$Am, \$And, \$At, \$Hour, \$Is, \$Minute, \$Now, \$On, \$Pm, \$Today, \$Tomorrow, \$Was** + +: Set each of these system variables to the translation of the + corresponding English word into your language. Note that **\$Am** + and **\$Pm** should be the translations of \"AM\" and \"PM\" + (morning and afternoon time indicators) respectively. + +**\$Hplu, \$Mplu** + +: Set these to the suffix to add to the word for \"hour\" and + \"minute\" to make them plural. In English, both would be set to + \"s\". + +**\$Fromnow** + +: Set this to the translation of the English phrase \"from now\" + +Note that if you set any of the language-related system variables, they +should be set in a section of your script that always is evaluated. If +you set them inside an **IF** statement, for example, results are +unpredictable. + +Note also that the **Rem2PS** back-end does not support the full range +of UTF-8 characters. The **TkRemind**, **rem2html** and **rem2pdf** +back-ends all do support the full UTF-8 range. + +# RUN-TIME MODIFICATION OF THE SUBSTITUTION FILTER + +The system variables mentioned in the previous section are not typically +sufficient to properly translate Remind\'s output to another language. +Some languages have complicated rules for AM vs PM times and others have +complex rules for making words plural. **Remind** therefore allows you +to define a number of functions that modify the behavior of the +substitution filter at run-time. The functions are: + +**subst_ampm(h)** + +: This function is passed a single integer, namely an hour from 0 + to 23. It should return a string that indicates \"AM\" or \"PM\" or + even finer gradations in some languages. + +**subst_ordinal(d)** + +: This function is passed a single integer, namely a day of the month + from 1 to 31. It should return a string that is suffixed to the day + number to turn it into an ordinal number. In English, for example, + the function might return \"st\", \"nd\", \"rd\" or \"th\", + depending on *d*. + +**subst\_N(alt, date, time)** + +: This is actually a *family* of functions, where *N* is a letter or + number. This function *completely overrides* the substitution + sequence \"%N\". The three arguments are an integer *alt* which, if + non-zero, indicates that the alternate-mode substitution sequence + \"%\*N\" was encountered; *date* which is the trigger date of the + reminder and *time* which is the trigger time. + +**subst\_Nx(alt, date, time)** + +: Again, this is a *family* of functions. It is similar to the + **subst\_N** family except it is only called if *date* is two or + more days away from *today()*. This is useful if you don\'t want to + override the \"today\" or \"tomorrow\" output for most substitution + sequences. + +Here\'s an example of how you might customize your substitution filter. +Suppose you want to change the \"%b\" sequence to substitute \"the day +after tomorrow\" for an event two days from now. You could do this: + + FSET subst_bx(a,d,t) iif(d==today()+2, "the day after tomorrow", \ + "in " + (d-today()) + " days' time") + REM [today()+3] ++3 MSG Event 1 is %b% + REM [today()+2] ++3 MSG Event 2 is %b% + REM [today()+1] ++3 MSG Event 3 is %b% + REM [today()] ++3 MSG Event 4 is %b% + +The output of this script is: + + Event 1 is in 3 days' time + Event 2 is the day after tomorrow + Event 3 is tomorrow + Event 4 is today + +Note how Event 2\'s wording was changed from the normal \"in 2 days\' +time\", and note also that the \"tomorrow\" and \"today\" events used +the normal substitution\-\--**subst_bx** is not called for trigger days +of today or tomorrow. + +As a special case, if a **subst_Nx or subst_N** function returns the +integer zero, then the normal substitution mechanism is used. Therefore, +the previous example could have been written more simply as: + + FSET subst_bx(a,d,t) iif(d==today()+2, "the day after tomorrow", 0) + +You can override substitution sequences that are not alphanumeric as +follows: + +> Override %: with **subst_colon** +> +> Override %! with **subst_bang** +> +> Override %? with **subst_question** +> +> Override %@ with **subst_at** +> +> Override %# with **subst_hash** + +You can define your own substitution sequences in addition to the +built-in ones as follows: If you define a function named +**subst\_name(alt, date, time)**, then the sequence **%{name}** +calls the function with **alt** set to 0 and **date** and **time** to +the trigger date and time, respectively. The **%{name}** sequence is +replaced with whatever the function returns. The sequence **%\*{name}** +is similar, but calls the function with **alt** set to 1. + +If you use a **%{name}** sequence and the function **subst\_name** is +not defined or returns an error, then **%{name}** is replaced with the +empty string. + +Note that when **Remind** invokes any callback function for a +substitution sequence, **RUN** will be disabled. + +# THE TRANSLATION TABLE + +To assist with localizing reminder files, **Remind** maintains a table +of translations. This is simple a lookup table that maps one string (the +original string) to a new string (the translated string.) When +**Remind** starts executing, the translation table is empty. + +To add a message to the translation table, use the **TRANSLATE** command +(which may be abbreviated to **TRANS**.) The **TRANSLATE** command must +be followed by two quoted strings, separated from each other and from +the command by whitespace. For example, a Dutch language file might +contain something like this: + + TRANSLATE "New Moon" "Nieuwe maan" + TRANSLATE "First Quarter" "Eerste kwartier" + TRANSLATE "Full Moon" "Volle maan" + TRANSLATE "Last Quarter" "Laatste kwartier" + +To actually use the translation table, make use of the **\_** built-in +function, as follows: + + REM NOQUEUE [moondatetime(0)] MSG [_("New Moon")] (%2) + REM NOQUEUE [moondatetime(1)] MSG [_("First Quarter")] (%2) + REM NOQUEUE [moondatetime(2)] MSG [_("Full Moon")] (%2) + REM NOQUEUE [moondatetime(3)] MSG [_("Last Quarter")] (%2) + +By using **TRANSLATE** and **\_** judiciously, you can make your +reminder files easy to translate. + +**TRANSLATE** has four additional forms: If it is followed by *one* +quoted string instead of two, then **Remind** deletes the translation +table entry for that string. If it is followed by the keyword **DUMP**, +then **Remind** dumps all translation table entries to standard output. +And if it is followed by **CLEAR**, then **Remind** deletes all of the +translation table entries. + +The fourth form, **TRANSLATE GENERATE**, dumps all of the strings that +can be localized (as a series of TRANSLATE commands) to standard output. +Strings that are already localized are output with their localization; +strings that are not localized are output as: + + TRANSLATE "untranslated" "" + + If you want to add a new language, you can obtain a skeleton translation + file by running: + + echo "TRANSLATE GENERATE" | remind -h - > /tmp/skeleton.rem + +If you have an existing language file that is missing some translations, +you can update it by running: + + (echo INCLUDE mylang.rem; echo TRANSLATE GENERATE) | \ + remind -h - > /tmp/mylang-update.rem + +and then editing **mylang-update.rem** to add in the missing +translations. + +If you have some reminder scripts that use the **\_()** built-in +function or **%(\...)** substitution sequence, you can generate a list +of needed TRANSLATE commands by running: + + remind -q -n -dq myscript.rem 2>&1 | grep ^TRANSLATE | sort | uniq + +Note that if you **SET** various translation-related system variables +such as **\$Monday**, **\$December**, **\$Ago**, etc, then **Remind** +*also* makes a corresponding translation table entry automatically. This +is done for all of the translation-related system variables *except for* +**\$Hplu** and **\$Mplu**. + +The converse applies too; creating a translation table for \"December\" +automatically sets **\$December**. And if you invoke **TRANSLATE +CLEAR**, then all translation-related system variables are set to their +default values as well. + +The translation table always contains a special entry **LANGID** whose +default value is **en**. Translators are encouraged to add a **LANGID** +entry in their language files; the value should be the two-characters +ISO 639 language code. + +For example, if you write a translation file for the Dutch language, add +this line: + + TRANSLATE "LANGID" "nl" + +Scripts can use **\_(\"LANGID\")** to query the translation language +that is in effect. + +The **\_()** function uses the following procedure to obtain the +translation for a string: + +> 1 +> +> : Look for an exact match. If found, return. +> +> 2 +> +> : If the original string had an upper-case letter, search for the +> all-lower-case equivalent. If found, make the first letter of the +> result upper-case and return. +> +> 3 +> +> : If the original string started with a lower-case letter, search +> for an equivalent whose first letter is upper-case and the rest +> lower-case. If found, make the first letter of the result +> lower-case and return. +> +> 4 +> +> : No translation was found. Return the original string. + +# LANGUAGE PACKS + +**Remind** ships with a number of language packs, which are simply +reminder scripts located in **\[\$SysInclude\]/lang**. The +currently-shipping language packs are: + +da.rem (Danish), de.rem (German), es.rem (Spanish), fr.rem (French), +is.rem (Icelandic), it.rem (Italian), nl.rem (Dutch), no.rem +(Norwegian), pl.rem (Polish), pt.rem (Portuguese) and ro.rem (Romanian). + +To use a language pack (in this example, de.rem), simply place this at +the top of your reminders file: + + SYSINCLUDE lang/de.rem + +If you want **Remind** to try to find the language pack appropriate for +your locale settings, use: + + SYSINCLUDE lang/auto.rem + +You are encouraged to study the language packs to see how to translate +**Remind** into additional languages. + +# THE HEBREW CALENDAR + +**Remind** has support for the Hebrew calendar, which is a luni-solar +calendar. This allows you to create reminders for Jewish holidays, +jahrzeits (anniversaries of deaths) and smachot (joyous occasions.) + +**THE HEBREW YEAR** + +The Hebrew year has 12 months, alternately 30 and 29 days long. The +months are: Tishrey, Heshvan, Kislev, Tevet, Shvat, Adar, Nisan, Iyar, +Sivan, Tamuz, Av and Elul. In Biblical times, the year started in Nisan, +but Rosh Hashana (Jewish New Year) is now celebrated on the 1st and 2nd +of Tishrey. + +In a cycle of 19 years, there are 7 leap years, being years 3, 6, 8, 11, +14, 17 and 19 of the cycle. In a leap year, an extra month of 30 days is +added before Adar. The two Adars are called Adar A and Adar B. + +For certain religious reasons, the year cannot start on a Sunday, +Wednesday or Friday. To adjust for this, a day is taken off Kislev or +added to Heshvan. Thus, a regular year can have from 353 to 355 days, +and a leap year from 383 to 385. + +When Kislev or Heshvan is short, it is called *chaser*, or lacking. When +it is long, it is called *shalem*, or full. + +The Jewish date changes at sunset. However, **Remind** will change the +date at midnight, not sunset. So in the period between sunset and +midnight, **Remind** will be a day earlier than the true Jewish date. +This should not be much of a problem in practice. + +The computations for the Jewish calendar were based on the program +\"hdate\" written by Amos Shapir of the Hebrew University of Jerusalem, +Israel. He also supplied the preceding explanation of the calendar. + +**HEBREW DATE FUNCTIONS** + +**hebday(d_date)** + +: Returns the day of the Hebrew month corresponding to the *date* + parameter. For example, 12 April 1993 corresponds to 21 Nisan 5753. + Thus, hebday(\'1993/04/12\') returns 21. + +**hebmon(d_date)** + +: Returns the name of the Hebrew month corresponding to *date*. For + example, hebmon(\'1993/04/12\') returns \"Nisan\". + +**hebyear(d_date)** + +: Returns the Hebrew year corresponding to *date*. For example, + hebyear(\'1993/04/12\') returns 5753. + +**hebdate(i_day, s_hebmon \[,id_yrstart \[,i_jahr \[,i_aflag\]\]\])** + +: The **hebdate()** function is the most complex of the Hebrew support + functions. It can take from 2 to 5 arguments. It returns a **DATE** + corresponding to the Hebrew date. + +> The *day* parameter can range from 1 to 30, and specifies the day of +> the Hebrew month. The *hebmon* parameter is a string that must name +> one of the Hebrew months specified above. Note that the month must be +> spelled out in full, and use the English transliteration shown +> previously. You can also specify \"Adar A\" and \"Adar B.\" Month +> names are not case-sensitive. +> +> The *yrstart* parameter can either be a **DATE** or an **INT**. If it +> is a **DATE**, then the **hebdate()** scans for the first Hebrew date +> on or after that date. For example: +> +> hebdate(15, "Nisan", '1990/01/01') +> +> returns 1990/03/30, because that is the first occurrence of 15 Nisan +> on or after 1 January 1990. +> +> If *yrstart* is an **INT**, it is interpreted as a Hebrew year. Thus: +> +> hebdate(22, "Kislev", 5756) +> +> returns 1995/12/15, because that date corresponds to 22 Kislev, 5756. +> Note that none of the Hebrew date functions will work with dates +> outside **Remind\'s** normal range for dates. +> +> If *yrstart* is not supplied, it defaults to **today()**. +> +> The *jahr* modifies the behaviour of **hebdate()** as follows: +> +> If *jahr* is 0 (the default), then **hebdate()** keeps scanning until +> it finds a date that exactly satisfies the other parameters. For +> example: +> +> hebdate(30, "Adar A", 1993/01/01) +> +> returns 1995/03/02, corresponding to 30 Adar A, 5755, because that is +> the next occurrence of 30 Adar A after 1 January, 1993. This behaviour +> is appropriate for Purim Katan, which only appears in leap years. +> +> If *jahr* is 1, then the date is modified as follows: +> +> o +> +> : 30 Heshvan is converted to 1 Kislev in years when Heshvan is +> *chaser* +> +> o +> +> : 30 Kislev is converted to 1 Tevet in years when Kislev is *chaser* +> +> o +> +> : 30 Adar A is converted to 1 Nisan in non-leapyears +> +> o +> +> : Other dates in Adar A are moved to the corresponding day in Adar +> in non-leapyears +> +> This behaviour is appropriate for smachot (joyous occasions) and for +> some jahrzeits - see \"JAHRZEITS.\" +> +> if *jahr* is 2, then the date is modified as follows: +> +> o +> +> : 30 Kislev and 30 Heshvan are converted to 29 Kislev and 29 +> Heshvan, respectively, if the month is *chaser* +> +> o +> +> : 30 Adar A is converted to 30 Shvat in non-leapyears +> +> o +> +> : Other dates in Adar A are moved to the corresponding day in Adar +> in non-leapyears +> +> if *jahr* is not 0, 1, or 2, it is interpreted as a Hebrew year, and +> the behaviour is calculated as described in the next section, +> \"JAHRZEITS.\" +> +> The *aflag* parameter modifies the behaviour of the function for dates +> in Adar during leap years. The *aflag* is *only* used if *yrstart* is +> a **DATE** type. +> +> The *aflag* only affects date calculations if *hebmon* is specified as +> \"Adar\". In leap years, the following algorithm is followed: +> +> o +> +> : If *aflag* is 0, then the date is triggered in Adar B. This is the +> default. +> +> o +> +> : If *aflag* is 1, then the date is triggered in Adar A. This may be +> appropriate for jahrzeits in the Ashkenazi tradition; consult a +> rabbi. +> +> o +> +> : If *aflag* is 2, then the date is triggered in both Adar A and +> Adar B of a leap year. Some Ashkenazim perform jahrzeit in both +> Adar A and Adar B. + +**JAHRZEITS** + +A jahrzeit is a yearly commemoration of someone\'s death. It normally +takes place on the anniversary of the death, but may be delayed if +burial is delayed - consult a rabbi for more information. + +In addition, because some months change length, it is not obvious which +day the anniversary of a death is. The following rules are used: + +o + +: If the death occurred on 30 Heshvan, and Heshvan in the year after + the death is *chaser*, then the jahrzeit is observed on 29 Heshvan + in years when Heshvan is *chaser*. Otherwise, the jahrzeit is + observed on 1 Kislev when Heshvan is *chaser*. + +o + +: If the death occurred on 30 Kislev, and Kislev in the year after the + death is *chaser*, then the jahrzeit is observed on 29 Kislev in + years when Kislev is *chaser*. Otherwise, the jahrzeit is observed + on 1 Tevet when Kislev is *chaser*. + +o + +: If the death occurred on 1-29 Adar A, it is observed on 1-29 Adar in + non-leapyears. + +o + +: If the death occurred on 30 Adar A, it is observed on 30 Shvat in a + non-leapyear. + +Specifying a Hebrew year for the *jahr* parameter causes the correct +behaviour to be selected for a death in that year. You may also have to +specify *aflag*, depending on your tradition. + +The jahrzeit information was supplied by Frank Yellin, who quoted \"The +Comprehensive Hebrew Calendar\" by Arthur Spier, and \"Calendrical +Calculations\" by E. M. Reingold and Nachum Dershowitz. + +# OUT-OF-BAND REMINDERS + +The **SPECIAL** keyword is used to transmit \"out-of-band\" information +to **Remind** backends, such as **tkremind** or **Rem2PS**. They are +used only when piping data from a **remind -p** line. (Note that the +COLOR special is an exception; it works similarly to MSG when the **-p** +option is not supplied.) + +The various **SPECIAL**s recognized are particular for each backend; +however, there are four **SPECIAL**s that all backends should attempt to +support. They are currently supported by **Rem2PS**, **tkremind** and +**rem2html**. + +The **SHADE** special replaces the **psshade()** function. Use it like +this: + + REM Sat Sun SPECIAL SHADE 128 + REM Mon SPECIAL SHADE 255 0 0 + +The **SHADE** keyword is followed by either one or three numbers, from 0 +to 255. If one number is supplied, it is interpreted as a grey-scale +value from black (0) to white (255). If three numbers are supplied, they +are interpreted as RGB components from minimum (0) to maximum (255). The +example above shades weekends a fairly dark grey and makes Mondays a +fully-saturated red. (These shadings appear in calendars produced by +**Rem2PS**, **tkremind** and **rem2html**.) + +The **MOON** special replaces the **psmoon()** function. Use it like +this: + + REM [moondate(0)] SPECIAL MOON 0 + REM [moondate(1)] SPECIAL MOON 1 + REM [moondate(2)] SPECIAL MOON 2 + REM [moondate(3)] SPECIAL MOON 3 + +These draw little moons on the various calendars. The complete syntax of +the **MOON** special is as follows: + + ... SPECIAL MOON phase moonsize fontsize msg + +*Phase* is a number from 0 to 3, with 0 representing a new moon, 1 the +first quarter, 2 a full moon and 3 the last quarter. + +*moonsize* is the diameter in PostScript units of the moon to draw. If +omitted or supplied as -1, the backend chooses an appropriate size. + +*fontsize* is the font size in PostScript units of the *msg* + +*Msg* is additional text that is placed near the moon glyph. + +Note that only the **Rem2PS** backend supports *moonsize* and +*fontsize*; the other backends use fixed sizes. + +The **COLOR** special lets you place colored reminders in the calendar. +Use it like this: + + REM ... SPECIAL COLOR 255 0 0 This is a bright red reminder + REM ... SPECIAL COLOR 0 128 0 This is a dark green reminder + +You can spell COLOR either the American way (\"COLOR\") or the British +way (\"COLOUR\"). This manual will use the American way. + +Immediately following COLOR should be three decimal numbers ranging from +0 to 255 specifying red, green and blue intensities, respectively. The +rest of the line is the text to put in the calendar. + +The COLOR special is \"doubly special\", because in agenda mode, +**remind** treats a COLOR special just like a MSG-type reminder. Also, +if you invoke **Remind** with **-@**\[*n*\], then it approximates +SPECIAL COLOR reminders on your terminal. + +See also the documentation of the **\$DefaultColor** system variable in +the section \"SYSTEM VARIABLES\". + +The **WEEK** special lets you place annotations such as the week number +in the calendar. For example, this would number each Monday with the ISO +8601 week number. The week number is shown like this: \"(W*n*)\" in this +example, but you can put whatever text you like after the WEEK keyword. + + REM Monday SPECIAL WEEK (W[weekno()]) + +# MISCELLANEOUS + +**COMMAND AND KEYWORD ABBREVIATIONS** + +The following tokens can be abbreviated: + +o + +: **CLEAR-OMIT-CONTEXT** \--\> **CLEAR** + +o + +: **PUSH-OMIT-CONTEXT** \--\> **PUSH** + +o + +: **POP-OMIT-CONTEXT** \--\> **POP** + +o + +: **DUMPVARS** \--\> **DUMP** + +o + +: **BANNER** \--\> **BAN** + +o + +: **INCLUDE** \--\> **INC** + +o + +: **MAYBE-UNCOMPUTABLE** \--\> **MAYBE** + +o + +: **SCANFROM** \--\> **SCAN** + +**NIFTY EXAMPLES** + +This section is a sampling of what you can do with **Remind**. + + REM 5 Feb 1991 AT 14:00 +45 *30 \ + RUN mail -s "Meeting at %2" $LOGNAME wrote **Remind**. The moon phase code +was copied largely unmodified from \"moontool\" by John Walker. The +moonrise/moonset code comes from https://github.com/signetica/MoonRise +by Stephen R. Schmitt and Cyrus Rahman. The sunrise and sunset functions +use ideas from programs by Michael Schwartz and Marc T. Kaufman. The +Hebrew calendar support was taken from \"hdate\" by Amos Shapir. The +supported languages and their translators are listed below. Languages +marked \"complete\" support error messages in that language; all others +only support the substitution filter mechanism and month/day names. + +**German** \-- Wolfgang Thronicke + +**Dutch** \-- Willem Kasdorp and Erik-Jan Vens + +**Finnish** \-- Mikko Silvonen (complete) + +**French** \-- Laurent Duperval (complete) + +**Norwegian** \-- Trygve Randen + +**Danish** \-- Mogens Lynnerup + +**Polish** \-- Jerzy Sobczyk (complete) + +**Brazilian Portuguese** \-- Marco Paganini (complete) + +**Italian** \-- Valerio Aimale + +**Romanian** \-- Liviu Daia + +**Spanish** \-- Rafa Couto + +**Icelandic** \-- Björn Davíðsson + +# BUGS + +If you find a bug in Remind, please report it to: dianne@skoll.ca + +There\'s no good reason why read-only system variables are not +implemented as functions, or why functions like **version()**, etc. are +not implemented as read-only system variables. + +Hebrew dates in **Remind** change at midnight instead of sunset. + +**Remind** has some built-in limits (for example, number of global +**OMIT**s.) + +# BIBLIOGRAPHY + +Nachum Dershowitz and Edward M. Reingold, \"Calendrical Calculations\", +*Software-Practice and Experience*, Vol. 20(9), Sept. 1990, pp 899-928. + +L. E. Doggett, *Almanac for computers for the year 1978*, Nautical +Almanac Office, USNO. + +Richard Siegel and Michael and Sharon Strassfeld, *The First Jewish* +Catalog, Jewish Publication Society of America. + +Jean Meeus, *Astronomical Algorithms, Second Edition*, Willmann-Bell, +Inc. + +# HOME PAGE + +https://dianne.skoll.ca/projects/remind/ + +# MAILING LIST + +https://dianne.skoll.ca/mailman/listinfo/remind-fans + +# SEE ALSO + +**rem**(1), **rem2ps**(1), **rem2pdf**(1), **tkremind**(1), +**rem2html**(1)