Compare commits

...

36 Commits

Author SHA1 Message Date
Dianne Skoll
85f96e2e01 Update man page. 2020-03-14 11:37:11 -04:00
Dianne Skoll
47331cd919 Make ampm() function accept a DATETIME and obey $TimeSep, $DateSep and $DateTimeSep 2020-03-14 11:32:35 -04:00
Dianne Skoll
d9f18ed6a7 Don't use many-json2dict 2020-03-03 11:43:54 -05:00
Dianne Skoll
efa4816371 Properly-form JSON output. 2020-03-03 10:36:01 -05:00
Dianne Skoll
2dc8c63adb Proper prefix for beta versions. 2020-02-29 09:38:51 -05:00
Dianne Skoll
c3c1781021 Update COPYRIGHT date. 2020-02-28 08:56:49 -05:00
Dianne Skoll
cd39480a98 Remove extraneous spaces. 2020-02-27 16:50:44 -05:00
Dianne Skoll
281a1a206e Implement JSONQUEUE daemon command. 2020-02-27 15:18:23 -05:00
Dianne Skoll
cbff2a7bf2 Document ampm() 2020-02-27 08:39:21 -05:00
Dianne Skoll
2a08be8fd0 Fix up unit tests. 2020-02-26 17:43:47 -05:00
Dianne Skoll
0826678209 Make coerce from string to time and datetime accept ampm 2020-02-26 17:41:51 -05:00
Dianne Skoll
f2e421bfa5 Add acceptance tests for ampm() function. 2020-02-26 17:25:09 -05:00
Dianne Skoll
ce53a9b91a Add "ampm" built-in function. 2020-02-26 17:21:34 -05:00
Dianne Skoll
b166b1cf82 Fix up test file. 2020-02-24 15:17:07 -05:00
Dianne Skoll
d09fbb03b2 Bump version to 3.3.1. 2020-02-24 15:16:20 -05:00
Dianne Skoll
7a835f3b10 Update docs. 2020-02-23 16:02:59 -05:00
Dianne Skoll
6b991cdf9c Refactor saving of trigger info. 2020-02-23 11:38:17 -05:00
Dianne Skoll
018e9d0323 JSON can handle newlines (the literal newlines will be escaped to "\n" by the JSON writer.) 2020-02-23 11:17:59 -05:00
Dianne Skoll
f499ae096f Don't include filename or line number in synthesized tag. 2020-02-23 11:15:38 -05:00
Dianne Skoll
725e046a15 Fix bug in recording trigdate() for SATISFY-type reminders. :( 2020-02-22 19:15:24 -05:00
Dianne Skoll
275b1f62b6 For overlapping reminders, prefer the *later* version. 2020-02-22 16:50:42 -05:00
Dianne Skoll
6e58dea198 Handle overlapping events better. 2020-02-22 16:30:08 -05:00
Dianne Skoll
ad4e62c8c3 Test specifying DURATION as an integer. 2020-02-22 12:48:07 -05:00
Dianne Skoll
a5768d55d8 Update man page to document integer form of DURATION. 2020-02-22 12:46:57 -05:00
Dianne Skoll
7db49971c8 Update man page. 2020-02-22 12:40:32 -05:00
Dianne Skoll
45ca6631ac Make sure AM/PM is case-insensitive. 2020-02-22 12:32:52 -05:00
Dianne Skoll
d51944f36c Allow duration to be specified as a single number, meaning minutes.
Don't convert 90-99 to 1990-1999 when parsing numbers.
2020-02-22 12:31:17 -05:00
Dianne Skoll
037c79fb0f Allow times to have am/pm specifications. 2020-02-22 12:24:37 -05:00
Dianne Skoll
993373414f Parse times and datetimes with trailing am/pm 2020-02-22 12:14:18 -05:00
Dianne Skoll
38a0a9992a Leave a space after the asterisk row. 2020-02-12 11:19:30 -05:00
Dianne Skoll
a82bbe3776 Highlight today in month mode also. 2020-02-12 11:17:21 -05:00
Dianne Skoll
a32ba217ba Update man page. 2020-02-08 15:11:15 -05:00
Dianne Skoll
d322cf54ac Allow $FormWidth to be as large as 500. 2020-02-08 15:10:48 -05:00
Dianne Skoll
b1d07a231d Document $FormWidth. 2020-02-08 15:06:09 -05:00
Dianne Skoll
97851a08e6 Clamp min cal width at 72 2020-02-08 15:04:59 -05:00
Dianne Skoll
8547b30a8f Set $FormWidth to terminal width - 8 on startup. 2020-02-08 15:04:48 -05:00
21 changed files with 1043 additions and 133 deletions

View File

@@ -3,7 +3,7 @@ THE REMIND COPYRIGHT
1. REMIND refers to the entire set of files and documentation in the
REMIND package.
2. REMIND is Copyright 1992-2018 Dianne Skoll, except where noted in
2. REMIND is Copyright 1992-2020 Dianne Skoll, except where noted in
individual files.
3. DISTRIBUTION AND USE

2
configure vendored
View File

@@ -3991,7 +3991,7 @@ _ACEOF
fi
done
VERSION=03.03.00
VERSION=03.03.01
ac_config_files="$ac_config_files src/Makefile www/Makefile src/version.h"

View File

@@ -75,6 +75,6 @@ if test "$GCC" = yes; then
fi
AC_CHECK_FUNCS(setenv unsetenv glob mbstowcs setlocale)
VERSION=03.03.00
VERSION=03.03.01
AC_SUBST(VERSION)
AC_OUTPUT(src/Makefile www/Makefile src/version.h)

View File

@@ -1,5 +1,34 @@
CHANGES TO REMIND
* VERSION 3.3 Patch 1 - 2020-XX-XX
- CHANGE: For overlapping multi-day events, issue a reminder for the
most *recent* event rather than the earliest event. NOTE INCOMPATIBILITY:
This is a behavior change!
- CHANGE: Do not convert 90-99 to 1990-1999 when parsing numbers to recognize
years. NOTE INCOMPATIBILITY: This is a behavior change!
- CHANGE: Revert change to -y option that included filename and line number
in the hash.
- CHANGE: Retain newlines (produced by %_) in JSON output.
- FIX: Document $FormWidth system variable
- FIX: Highlight today's date in "remind -c" output
- IMPROVEMENT: Allow times to be specified either in 24-hour mode
(HH:MM or HH.MM) or AM/PM mode (HH:MMam; HH:MMpm, etc.)
- IMPROVEMENT: Allow DURATION to be specified as a time (1:30) or a
number of minutes (90).
- IMPROVEMENT: If terminal size can be determined, set $FormWidth to
terminal width - 8; if not, set $FormWidth to 72.
- MINOR IMPROVEMENT: Add the "ampm()" built-in function.
* Version 3.3 Patch 0 - 2020-01-31
- FIX: rem2ps: Add a %%PageBoundingBox: document structuring convention

View File

@@ -334,14 +334,15 @@ If you supply a \fIdate\fR on the command line, it must consist of
of the month, and \fIyear\fR is a year (all 4 digits) from 1990 to
about 2075. You can leave out the \fIday\fR, which then defaults to 1.
.PP
If you do supply a \fIdate\fR on the command line, then \fBRemind\fR 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 \fItime\fR (in 24-hour format -- for example, 17:15) to
set \fBRemind\fR's notion of "now" to a particular time. Supplying
a \fItime\fR on the command line also implicitly enables the \fB\-q\fR
option and disables the \fB\-z\fR option.
If you do supply a \fIdate\fR on the command line, then \fBRemind\fR
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 \fItime\fR to set \fBRemind\fR's notion of "now" to a
particular time. Supplying a \fItime\fR on the command line also
implicitly enables the \fB\-q\fR option and disables the \fB\-z\fR
option. The \fItime\fR may be specified in 24-hour format (eg, 13:20)
or common "AM/PM" format (1:20pm).
.PP
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
@@ -517,7 +518,7 @@ characters must be used. The following are examples of the various parts of a
JANUARY, feb, March, ApR, may, Aug
.TP
.I year:
1990, 1993, 2030, 95 (interpreted as 1995). The year can range
1990, 1993, 2030. The year can range
from 1990 to 2075.
.TP
.I weekday:
@@ -967,8 +968,13 @@ it terminates the search after the \fBSATISFY\fR iteration limit
.PP
Timed reminders are those that have an \fBAT\fR keyword followed
by a \fItime\fR and optional \fItdelta\fR and \fItrepeat\fR. The \fItime\fR
must be specified in 24-hour format, with 0:00 representing midnight,
mau 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.
.PP
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.
.PP
@@ -995,7 +1001,7 @@ The following reminder will be triggered on Thursdays and Fridays,
but will only be queued on Fridays:
.PP
.nf
REM Fri ++1 AT 13:00 MSG Lunch at 1pm Friday.
REM Fri ++1 AT 1:00PM MSG Lunch at 1pm Friday.
.fi
.PP
The \fItdelta\fR and \fItrepeat\fR have the same form as a \fIrepeat\fR
@@ -1077,15 +1083,21 @@ to each distinct REM command.
.PP
The \fBDURATION\fR 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:
90-minute meeting starting at 1:00pm, you could use any of the following:
.PP
.nf
REM 5 March 1999 AT 13:00 DURATION 1:30 MSG Meeting
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
.fi
.PP
Note that \fIduration\fR is specified in hours and minutes. If you
specify a duration of 00:00, then \fBRemind\fR behaves exactly as if
no \fBDURATION\fR at all had been present.
Note that \fIduration\fR is specified either in hours and minutes as a
\fItime\fR, or in minutes as an \fIinteger\fR. If you specify a
duration of 00:00 or 0, then \fBRemind\fR behaves exactly as if no
\fBDURATION\fR at all had been present.
.PP
.SH THE SUBSTITUTION FILTER
.PP
@@ -1665,13 +1677,15 @@ in C.
.RE
.TP
.B TIME constants
12:33, 0:01, 14:15, 16:42, 12.16, 13.00, 1.11
12:33, 0:01, 14:15, 16:42, 12.16, 13.00, 1.11, 4:30PM, 12:20am
.PP
.RS
Note that \fBTIME\fR constants are written in 24-hour format. Either the
period or colon can be used to separate the minutes from the hours.
However, Remind will consistently output times using only one separator
character. (The output separator character is chosen at compile-time.)
Note that \fBTIME\fR 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.)
.RE
.TP
.B DATE constants
@@ -1694,11 +1708,12 @@ versions prior to 03.00.02 did not support the '-' date separator.
.RE
.TP
.B DATETIME constants
\fBDATETIME\fR constants are expressed similarly to \fBDATE\fR constants
with the addition of an "@HH:MM" part. For example:
\fBDATETIME\fR constants are expressed similarly to \fBDATE\fR
constants with the addition of an "@HH:MM" part, optionally followed
by "am" or "pm". For example:
.PP
.RS
\'2008-04-05@23:11', '1999/02/03@14:06', '2001-04-07@08:30'
\'2008-04-05@23:11', '1999/02/03@14:06', '2001-04-07@08:30', '2020-01-01@3:20pm'
.PP
\fBDATETIME\fR values are printed without the quotes. Notes about date
and time separator characters for \fBDATE\fR and \fBTIME\fR constants apply
@@ -2011,9 +2026,11 @@ for years greater than 2037.
.TP
.B $FormWidth
The maximum width of each line of text for formatting \fBMSF\fR-type
reminders. The default is 72. If an \fBMSF\fR-type reminder contains
a word too long to fit in this width, it will not be truncated - the
width limit will be ignored.
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 \fBMSF\fR-type
reminder contains a word too long to fit in this width, it will not be
truncated - the width limit will be ignored.
.TP
.B $HushMode (read-only)
If non-zero, then the \fB\-h\fR option was supplied on the command line.
@@ -2206,6 +2223,23 @@ is supplied, only the date component is used.
Returns the time of "astronomical twilight" on the specified \fIdate\fR. If
\fIdate\fR is omitted, defaults to \fBtoday()\fR.
.TP
.B ampm(tq_time [,s_am [,s_pm]])
Returns a \fBSTRING\fR that is the result of converting \fItime\fR
(which is either a \fBTIME\R or a \fBDATETIME\fR object) to "AM/PM"
format. The optional arguments \fIam\fR and \fIpm\fR are the strings
to append in the AM and PM case, respectively; they default to "AM"
and "PM". The function obeys the system variables $DateSep,
$TimeSep and $DateTimeSep when formatting its output. For example:
.RS
.PP
.nf
ampm(0:22) returns "12:22AM"
ampm(17:45, "am", "pm") returns "5:45pm"
ampm('2020-03-14@21:34') returns "2020-03-14@9:34PM"
.fi
.PP
.RE
.TP
.B args(s_fname)
Returns the number of arguments expected by the user-defined function
\fIfname\fR, or \-1 if no such user-defined function exists. Note that
@@ -3115,12 +3149,12 @@ is an implementation artifact.
.B SELF-OVERLAPPING EVENTS
.PP
A multi-day event has the possibility of "overlapping itself". When this
happens, \fBRemind\fR prefers the \fIearlier\fR event (only one copy of
happens, \fBRemind\fR prefers the \fIlater\fR event (only one copy of
an event is ever triggered for a given date.) Consider this example:
.PP
.nf
#!/bin/sh
remind - '*4' 11 Feb 1991 <<'EOF'
remind - '*5' 10 Feb 1991 <<'EOF'
BANNER %
REM MON at 0:00 DURATION 192:0 MSG [today()] [trigeventstart()] [trigduration()]%
@@ -3131,17 +3165,16 @@ an event is ever triggered for a given date.) Consider this example:
The output is:
.PP
.nf
1991-02-11 1991-02-04@00:00 24:00
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
.fi
.PP
Although 1991-02-11 is a Monday (which should cause the event to be
triggered, the 8-day-long event that started on 1991-02-04 \fIhas not
finished yet\fR, so that is the one that is triggered. The next day,
the event starting on 1991-02-04 \fIhas\fR finished, so the 1991-02-11
event triggers, with a remaining duration of 168:00, or 7 days.
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.
.PP
I do not recommend constructing self-overlapping multi-day events.
.PP

View File

@@ -582,7 +582,7 @@ proc ConfigureCalFrame { w firstDay numDays } {
proc DoQueue {} {
global DaemonFile
puts $DaemonFile "QUEUE"
puts $DaemonFile "JSONQUEUE"
flush $DaemonFile
}
@@ -2418,18 +2418,19 @@ proc ShowQueue { file } {
grid columnconfigure $w 1 -weight 0
grid rowconfigure $w 0 -weight 1
grid rowconfigure $w 1 -weight 0
set did 0
CenterWindow $w .
while (1) {
# We should only get one line
gets $file line
if {$line == "NOTE endqueue"} {
if {$line == "NOTE ENDJSONQUEUE"} {
break
}
set did 1
$w.t insert end "$line\n"
}
if {!$did} {
$w.t insert end "*** Queue is empty ***\n"
if {[catch {set obj [::json::json2dict $line]}]} {
continue;
}
foreach q $obj {
$w.t insert end "$q\n"
}
}
$w.t configure -state disabled
}
@@ -2456,7 +2457,7 @@ proc DaemonReadable { file } {
scan $line "NOTE reminder %s %s %s" time now tag
IssueBackgroundReminder $file $time $now $tag
}
"NOTE queue" {
"NOTE JSONQUEUE" {
ShowQueue $file
}
"NOTE newdate" {

View File

@@ -86,7 +86,7 @@ distro:
gpg --detach-sign -u dianne@skoll.ca remind-$(VERSION).tar.gz
beta-tgz:
cd .. && git archive --worktree-attributes --format=tar --prefix=remind-$(VERSION)/ HEAD > src/remind-$(VERSION)-BETA-$(BETA).tar
cd .. && git archive --worktree-attributes --format=tar --prefix=remind-$(VERSION)-BETA-$(BETA)/ HEAD > src/remind-$(VERSION)-BETA-$(BETA).tar
gzip -f -v -9 remind-$(VERSION)-BETA-$(BETA).tar
gpg --detach-sign -u dianne@skoll.ca remind-$(VERSION)-BETA-$(BETA).tar.gz

View File

@@ -245,7 +245,7 @@ static void WriteBottomCalLine (void);
static void WriteIntermediateCalLine (void);
static void WriteCalDays (void);
static void PrintJSONString(char const *s)
void PrintJSONString(char const *s)
{
while (*s) {
switch(*s) {
@@ -262,14 +262,14 @@ static void PrintJSONString(char const *s)
}
}
static void PrintJSONKeyPairInt(char const *name, int val)
void PrintJSONKeyPairInt(char const *name, int val)
{
printf("\"");
PrintJSONString(name);
printf("\":%d, ", val);
}
static void PrintJSONKeyPairString(char const *name, char const *val)
void PrintJSONKeyPairString(char const *name, char const *val)
{
/* If value is blank, skip it! */
if (!val || !*val) {
@@ -283,7 +283,7 @@ static void PrintJSONKeyPairString(char const *name, char const *val)
printf("\", ");
}
static void PrintJSONKeyPairDate(char const *name, int jul)
void PrintJSONKeyPairDate(char const *name, int jul)
{
int y, m, d;
if (jul == NO_DATE) {
@@ -297,7 +297,7 @@ static void PrintJSONKeyPairDate(char const *name, int jul)
}
static void PrintJSONKeyPairDateTime(char const *name, int dt)
void PrintJSONKeyPairDateTime(char const *name, int dt)
{
int y, m, d, h, i, k;
if (dt == NO_TIME) {
@@ -315,6 +315,21 @@ static void PrintJSONKeyPairDateTime(char const *name, int dt)
}
void PrintJSONKeyPairTime(char const *name, int t)
{
int h, i;
if (t == NO_TIME) {
/* Skip it! */
return;
}
h = t / 60;
i = t % 60;
printf("\"");
PrintJSONString(name);
printf("\":\"%02d:%02d\", ", h, i);
}
#ifdef REM_USE_WCHAR
static void PutWideChar(wchar_t const wc)
{
@@ -495,7 +510,7 @@ ComputeCalWidth(int x)
return 80;
}
if (w.ws_col < 71) {
return 80;
return 71;
}
return w.ws_col;
}
@@ -778,8 +793,13 @@ static int WriteCalendarRow(void)
if (i < wd || d+i-wd>DaysInMonth(m, y))
PrintLeft("", ColSpaces, ' ');
else {
sprintf(buf, "%d", d+i-wd);
PrintLeft(buf, ColSpaces, ' ');
sprintf(buf, "%d ", d+i-wd);
if (OrigJul+i == RealToday) {
PrintLeft(buf, ColSpaces-1, '*');
PutChar(' ');
} else {
PrintLeft(buf, ColSpaces, ' ');
}
}
gon();
DRAW(tb);
@@ -2081,8 +2101,6 @@ char const *SynthesizeTag(void)
unsigned char buf[16];
static char out[128];
MD5Init(&ctx);
MD5Update(&ctx, (unsigned char *) FileName, strlen(FileName));
MD5Update(&ctx, (unsigned char *) &LineNo, sizeof(LineNo));
MD5Update(&ctx, (unsigned char *) CurLine, strlen(CurLine));
MD5Final(buf, &ctx);
sprintf(out, "__syn__%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x",

View File

@@ -417,6 +417,9 @@ int ParseRem(ParsePtr s, Trigger *trig, TimeTrig *tim, int save_in_globals)
switch(tok.type) {
case T_Time:
case T_LongTime:
case T_Year:
case T_Day:
case T_Number:
if (tok.val != 0) {
tim->duration = tok.val;
} else {
@@ -1099,10 +1102,24 @@ int DoSatRemind(Trigger *trig, TimeTrig *tt, ParsePtr p)
iter = 0;
start = trig->scanfrom;
while (iter++ < MaxSatIter) {
jul = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1);
jul = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, 0);
if (r) {
if (r == E_CANT_TRIG) return OK; else return r;
}
if (jul != start && trig->duration_days) {
jul = ComputeTriggerNoAdjustDuration(start, trig, tt, &r, 1, trig->duration_days);
if (r) {
if (r == E_CANT_TRIG) return OK; else return r;
}
} else if (jul == start) {
if (tt->ttime != NO_TIME) {
trig->eventstart = MINUTES_PER_DAY * r + tt->ttime;
if (tt->duration != NO_TIME) {
trig->eventduration = tt->duration;
}
}
SaveAllTriggerInfo(trig, tt, jul, tt->ttime, 1);
}
if (jul == -1) {
return E_EXPIRED;
}

View File

@@ -644,10 +644,11 @@ int DoSubst(ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int jul,
break;
case '_':
if (mode != CAL_MODE && mode != ADVANCE_MODE && !MsgCommand)
if (PsCal == PSCAL_LEVEL2 || PsCal == PSCAL_LEVEL3 || (mode != CAL_MODE && mode != ADVANCE_MODE && !MsgCommand)) {
sprintf(s, "%s", NL);
else
} else {
sprintf(s, " ");
}
SHIP_OUT(s);
break;

View File

@@ -492,6 +492,7 @@ static int MakeValue(char const *s, Value *v, Var *locals, ParsePtr p)
{
int len;
int h, m, r;
int ampm = 0;
if (*s == '\"') { /* It's a literal string "*/
len = strlen(s)-1;
@@ -532,7 +533,27 @@ static int MakeValue(char const *s, Value *v, Var *locals, ParsePtr p)
m += *s - '0';
s++;
}
/* Check for p[m] or a[m] */
if (*s == 'A' || *s == 'a' || *s == 'P' || *s == 'p') {
ampm = tolower(*s);
s++;
if (*s == 'm' || *s == 'M') {
s++;
}
}
if (*s || h>23 || m>59) return E_BAD_TIME;
if (ampm) {
if (h < 1 || h > 12) return E_BAD_TIME;
if (ampm == 'a') {
if (h == 12) {
h = 0;
}
} else if (ampm == 'p') {
if (h < 12) {
h += 12;
}
}
}
v->type = TIME_TYPE;
v->v.val = h*60 + m;
return OK;
@@ -701,26 +722,12 @@ int DoCoerce(char type, Value *v)
return OK;
case STR_TYPE:
h = 0;
m = 0;
s = v->v.str;
if (!isdigit(*s)) return E_CANT_COERCE;
while (isdigit(*s)) {
h *= 10;
h += *s++ - '0';
}
if (*s != ':' && *s != '.' && *s != TimeSep)
return E_CANT_COERCE;
s++;
if (!isdigit(*s)) return E_CANT_COERCE;
while (isdigit(*s)) {
m *= 10;
m += *s++ - '0';
}
if (*s || h>23 || m>59) return E_CANT_COERCE;
if (ParseLiteralTime(&s, &i)) return E_CANT_COERCE;
if (*s) return E_CANT_COERCE;
v->type = TIME_TYPE;
free(v->v.str);
v->v.val = h*60+m;
v->v.val = i;
return OK;
default: return E_CANT_COERCE;
@@ -1222,6 +1229,48 @@ int CopyValue(Value *dest, const Value *src)
return OK;
}
int ParseLiteralTime(char const **s, int *tim)
{
int h=0;
int m=0;
int ampm=0;
if (!isdigit(**s)) return E_BAD_TIME;
while(isdigit(**s)) {
h *= 10;
h += *(*s)++ - '0';
}
if (**s != ':' && **s != '.' && **s != TimeSep) return E_BAD_TIME;
(*s)++;
if (!isdigit(**s)) return E_BAD_TIME;
while(isdigit(**s)) {
m *= 10;
m += *(*s)++ - '0';
}
/* Check for p[m] or a[m] */
if (**s == 'A' || **s == 'a' || **s == 'P' || **s == 'p') {
ampm = tolower(**s);
(*s)++;
if (**s == 'm' || **s == 'M') {
(*s)++;
}
}
if (h>23 || m>59) return E_BAD_TIME;
if (ampm) {
if (h < 1 || h > 12) return E_BAD_TIME;
if (ampm == 'a') {
if (h == 12) {
h = 0;
}
} else if (ampm == 'p') {
if (h < 12) {
h += 12;
}
}
}
*tim = h * 60 + m;
return OK;
}
/***************************************************************/
/* */
/* ParseLiteralDate */
@@ -1233,10 +1282,9 @@ int CopyValue(Value *dest, const Value *src)
int ParseLiteralDate(char const **s, int *jul, int *tim)
{
int y, m, d;
int hour, min;
int r;
y=0; m=0; d=0;
hour=0; min=0;
*tim = NO_TIME;
if (!isdigit(**s)) return E_BAD_DATE;
@@ -1266,20 +1314,9 @@ int ParseLiteralDate(char const **s, int *jul, int *tim)
/* Do we have a time part as well? */
if (**s == ' ' || **s == '@' || **s == 'T' || **s == 't') {
(*s)++;
while(isdigit(**s)) {
hour *= 10;
hour += *(*s)++ - '0';
}
if (**s != ':' && **s != '.' && **s != TimeSep) return E_BAD_TIME;
(*s)++;
while(isdigit(**s)) {
min *= 10;
min += *(*s)++ - '0';
}
if (hour > 23 || min > 59) return E_BAD_TIME;
*tim = hour * 60 + min;
r = ParseLiteralTime(s, tim);
if (r != OK) return r;
}
return OK;
}

View File

@@ -57,6 +57,7 @@ static int FADawn (func_info *);
static int FADusk (func_info *);
static int FAbs (func_info *);
static int FAccess (func_info *);
static int FAmpm (func_info *);
static int FArgs (func_info *);
static int FAsc (func_info *);
static int FBaseyr (func_info *);
@@ -206,6 +207,7 @@ BuiltinFunc Func[] = {
{ "access", 2, 2, 0, FAccess },
{ "adawn", 0, 1, 0, FADawn},
{ "adusk", 0, 1, 0, FADusk},
{ "ampm", 1, 3, 1, FAmpm },
{ "args", 1, 1, 0, FArgs },
{ "asc", 1, 1, 1, FAsc },
{ "baseyr", 0, 0, 1, FBaseyr },
@@ -886,6 +888,76 @@ static int FSgn(func_info *info)
return OK;
}
/***************************************************************/
/* */
/* FAmpm - return a time as a string with "AM" or "PM" suffix */
/* */
/***************************************************************/
static int FAmpm(func_info *info)
{
int h, m;
int yr=0, mo=0, da=0;
char const *am = "AM";
char const *pm = "PM";
char const *ampm = NULL;
char outbuf[128];
if (ARG(0).type != DATETIME_TYPE && ARG(0).type != TIME_TYPE) {
return E_BAD_TYPE;
}
if (HASDATE(ARG(0))) {
FromJulian(DATEPART(ARG(0)), &yr, &mo, &da);
}
if (Nargs >= 2) {
ASSERT_TYPE(1, STR_TYPE);
am = ARGSTR(1);
if (Nargs >= 3) {
ASSERT_TYPE(2, STR_TYPE);
pm = ARGSTR(2);
}
}
h = TIMEPART(ARG(0)) / 60;
m = TIMEPART(ARG(0)) % 60;
if (h <= 11) {
/* AM */
if (h == 0) {
if (ARG(0).type == DATETIME_TYPE) {
snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c12%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, TimeSep, m);
} else {
snprintf(outbuf, sizeof(outbuf), "12%c%02d", TimeSep, m);
}
} else {
if (ARG(0).type == DATETIME_TYPE) {
snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m);
} else {
snprintf(outbuf, sizeof(outbuf), "%d%c%02d", h, TimeSep, m);
}
}
ampm = am;
} else {
if (h > 12) {
h -= 12;
}
if (ARG(0).type == DATETIME_TYPE) {
snprintf(outbuf, sizeof(outbuf), "%04d%c%02d%c%02d%c%d%c%02d", yr, DateSep, mo+1, DateSep, da, DateTimeSep, h, TimeSep, m);
} else {
snprintf(outbuf, sizeof(outbuf), "%d%c%02d", h, TimeSep, m);
}
ampm = pm;
}
RetVal.type = STR_TYPE;
RetVal.v.str = malloc(strlen(outbuf) + strlen(ampm) + 1);
if (!RetVal.v.str) {
RetVal.type = ERR_TYPE;
return E_NO_MEM;
}
strcpy(RetVal.v.str, outbuf);
strcat(RetVal.v.str, ampm);
return OK;
}
/***************************************************************/
/* */
/* FOrd - returns a string containing ordinal number. */

View File

@@ -23,6 +23,7 @@
#include <sys/types.h>
#include <pwd.h>
#include <unistd.h>
#include <sys/ioctl.h>
#include "types.h"
#include "protos.h"
@@ -147,6 +148,17 @@ void InitRemind(int argc, char const *argv[])
jul = NO_DATE;
/* If stdout is a terminal, initialize $FormWidth to terminal width-8,
but clamp to [20, 500] */
if (isatty(STDOUT_FILENO)) {
struct winsize w;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &w) == 0) {
FormWidth = w.ws_col - 8;
if (FormWidth < 20) FormWidth = 20;
if (FormWidth > 500) FormWidth = 500;
}
}
/* Initialize global dynamic buffers */
DBufInit(&Banner);
DBufInit(&LineBuffer);

View File

@@ -1370,6 +1370,16 @@ ClearLastTriggers(void)
LastTimeTrig.duration = NO_TIME;
}
void
SaveAllTriggerInfo(Trigger const *t, TimeTrig const *tt, int trigdate, int trigtime, int valid)
{
SaveLastTrigger(t);
SaveLastTimeTrig(tt);
LastTriggerDate = trigdate;
LastTriggerTime = trigtime;
LastTrigValid = valid;
}
void
SaveLastTrigger(Trigger const *t)
{

View File

@@ -37,6 +37,7 @@ int ShouldTriggerReminder (Trigger *t, TimeTrig *tim, int jul, int *err);
int DoSubst (ParsePtr p, DynamicBuffer *dbuf, Trigger *t, TimeTrig *tt, int jul, int mode);
int DoSubstFromString (char const *source, DynamicBuffer *dbuf, int jul, int tim);
int ParseLiteralDate (char const **s, int *jul, int *tim);
int ParseLiteralTime (char const **s, int *tim);
int EvalExpr (char const **e, Value *v, ParsePtr p);
int DoCoerce (char type, Value *v);
void PrintValue (Value *v, FILE *fp);
@@ -89,7 +90,7 @@ char const *FindInitialToken (Token *tok, char const *s);
void FindToken (char const *s, Token *tok);
void FindNumericToken (char const *s, Token *t);
int ComputeTrigger (int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals);
int ComputeTriggerNoAdjustDuration (int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals);
int ComputeTriggerNoAdjustDuration (int today, Trigger *trig, TimeTrig *tim, int *err, int save_in_globals, int duration_days);
int AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int save_in_globals);
int ComputeScanStart(int today, Trigger *trig, TimeTrig *tt);
char *StrnCpy (char *dest, char const *source, int n);
@@ -148,6 +149,14 @@ char const *SynthesizeTag(void);
void ClearLastTriggers(void);
void SaveLastTrigger(Trigger const *t);
void SaveLastTimeTrig(TimeTrig const *t);
void SaveAllTriggerInfo(Trigger const *t, TimeTrig const *tt, int trigdate, int trigtime, int valid);
void PerIterationInit(void);
char const *Decolorize(int r, int g, int b);
char const *Colorize(int r, int g, int b);
void PrintJSONString(char const *s);
void PrintJSONKeyPairInt(char const *name, int val);
void PrintJSONKeyPairString(char const *name, char const *val);
void PrintJSONKeyPairDate(char const *name, int jul);
void PrintJSONKeyPairDateTime(char const *name, int dt);
void PrintJSONKeyPairTime(char const *name, int t);

View File

@@ -246,11 +246,7 @@ void HandleQueuedReminders(void)
/* Set up global variables so some functions like trigdate()
and trigtime() work correctly */
LastTriggerDate = JulianToday;
LastTriggerTime = q->tt.ttime;
LastTrigValid = 1;
SaveLastTrigger(&(q->t));
SaveLastTimeTrig(&(q->tt));
SaveAllTriggerInfo(&(q->t), &(q->tt), JulianToday, q->tt.ttime, 1);
(void) TriggerReminder(&p, &trig, &q->tt, JulianToday);
if (Daemon < 0) {
printf("NOTE endreminder\n");
@@ -452,6 +448,70 @@ static int CalculateNextTimeUsingSched(QueuedRem *q)
}
}
/* Dump the queue in JSON format */
static void
json_queue(QueuedRem const *q)
{
int done = 0;
printf("[");
while(q) {
if (q->tt.nexttime == NO_TIME) {
q = q->next;
continue;
}
if (done) {
printf(",");
}
done = 1;
printf("{");
switch(q->typ) {
case NO_TYPE: PrintJSONKeyPairString("type", "NO_TYPE"); break;
case MSG_TYPE: PrintJSONKeyPairString("type", "MSG_TYPE"); break;
case RUN_TYPE: PrintJSONKeyPairString("type", "RUN_TYPE"); break;
case CAL_TYPE: PrintJSONKeyPairString("type", "CAL_TYPE"); break;
case SAT_TYPE: PrintJSONKeyPairString("type", "SAT_TYPE"); break;
case PS_TYPE: PrintJSONKeyPairString("type", "PS_TYPE"); break;
case PSF_TYPE: PrintJSONKeyPairString("type", "PSF_TYPE"); break;
case MSF_TYPE: PrintJSONKeyPairString("type", "MSF_TYPE"); break;
case PASSTHRU_TYPE: PrintJSONKeyPairString("type", "PASSTHRU_TYPE"); break;
default: PrintJSONKeyPairString("type", "?"); break;
}
PrintJSONKeyPairInt("rundisabled", q->RunDisabled);
PrintJSONKeyPairInt("ntrig", q->ntrig);
PrintJSONKeyPairTime("ttime", q->tt.ttime);
PrintJSONKeyPairTime("nextttime", q->tt.nexttime);
PrintJSONKeyPairInt("delta", q->tt.delta);
if (q->tt.rep != NO_TIME) {
PrintJSONKeyPairInt("rep", q->tt.rep);
}
if (q->tt.duration != NO_TIME) {
PrintJSONKeyPairInt("duration", q->tt.duration);
}
if (q->passthru[0]) {
PrintJSONKeyPairString("passthru", q->passthru);
}
if (q->sched[0]) {
PrintJSONKeyPairString("sched", q->sched);
}
if (DBufLen(&(q->tags))) {
PrintJSONKeyPairString("tags", DBufValue(&(q->tags)));
}
/* Last one is a special case - no trailing comma */
printf("\"");
PrintJSONString("body");
printf("\":\"");
if (q->text) {
PrintJSONString(q->text);
} else {
PrintJSONString("");
}
printf("\"}");
q = q->next;
}
printf("]\n");
}
/***************************************************************/
/* */
/* DaemonWait */
@@ -536,6 +596,11 @@ static void DaemonWait(unsigned int sleeptime)
}
printf("NOTE endqueue\n");
fflush(stdout);
} else if (!strcmp(cmdLine, "JSONQUEUE\n")) {
printf("NOTE JSONQUEUE\n");
json_queue(QueueHead);
printf("NOTE ENDJSONQUEUE\n");
fflush(stdout);
} else if (!strcmp(cmdLine, "REREAD\n")) {
printf("NOTE reread\n");
fflush(stdout);

View File

@@ -257,6 +257,7 @@ void FindNumericToken(char const *s, Token *t)
{
int mult = 1, hour, min;
char const *s_orig = s;
int ampm = 0;
t->type = T_Illegal;
t->val = 0;
@@ -284,10 +285,6 @@ void FindNumericToken(char const *s, Token *t)
like Jan 6, 1998 */
if (*s == ',') {
s++;
/* Special hack - convert years between 90 and
99 to 1990 and 1999 */
if (t->val >= 90 && t->val <= 99) t->val += 1900;
/* Classify the number we've got */
if (t->val >= BASE && t->val <= BASE+YR_RANGE) t->type = T_Year;
else if (t->val >= 1 && t->val <= 31) t->type = T_Day;
@@ -299,7 +296,29 @@ void FindNumericToken(char const *s, Token *t)
s++;
hour = t->val;
PARSENUM(min, s);
if (*s || min > 59) return; /* Illegal time */
if (min > 59) return; /* Illegal time */
/* Check for p[m] or a[m] */
if (*s == 'A' || *s == 'a' || *s == 'P' || *s == 'p') {
ampm = tolower(*s);
s++;
if (*s == 'm' || *s == 'M') {
s++;
}
}
if (*s) return; /* Illegal time */
if (ampm) {
if (hour < 1 || hour > 12) return;
if (ampm == 'a') {
if (hour == 12) {
hour = 0;
}
} else if (ampm == 'p') {
if (hour < 12) {
hour += 12;
}
}
}
t->val = hour*60 + min; /* Convert to minutes past midnight */
if (hour <= 23) {
t->type = T_Time;

View File

@@ -454,11 +454,7 @@ AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int sav
}
if (save_in_globals) {
LastTriggerTime = tim->ttime;
SaveLastTimeTrig(tim);
SaveLastTrigger(trig);
LastTriggerDate = r;
LastTrigValid = 1;
SaveAllTriggerInfo(trig, tim, r, tim->ttime, 1);
}
return r;
}
@@ -474,10 +470,29 @@ AdjustTriggerForDuration(int today, int r, Trigger *trig, TimeTrig *tim, int sav
int ComputeTrigger(int today, Trigger *trig, TimeTrig *tim,
int *err, int save_in_globals)
{
int r = ComputeTriggerNoAdjustDuration(today, trig, tim, err, save_in_globals);
int r = ComputeTriggerNoAdjustDuration(today, trig, tim, err, save_in_globals, 0);
if (*err != OK) {
return r;
}
if (r == today) {
if (tim->ttime != NO_TIME) {
trig->eventstart = MINUTES_PER_DAY * r + tim->ttime;
if (tim->duration != NO_TIME) {
trig->eventduration = tim->duration;
}
}
if (save_in_globals) {
SaveAllTriggerInfo(trig, tim, r, tim->ttime, 1);
}
return r;
}
if (trig->duration_days) {
r = ComputeTriggerNoAdjustDuration(today, trig, tim, err, save_in_globals, trig->duration_days);
if (*err != OK) {
return r;
}
}
r = AdjustTriggerForDuration(today, r, trig, tim, save_in_globals);
return r;
}
@@ -491,10 +506,10 @@ int ComputeTrigger(int today, Trigger *trig, TimeTrig *tim,
/* */
/***************************************************************/
int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
int *err, int save_in_globals)
int *err, int save_in_globals, int duration_days)
{
int nattempts = 0,
start = today - trig->duration_days,
start = today - duration_days,
nextstart = 0,
y, m, d, omit,
result;
@@ -561,7 +576,7 @@ int ComputeTriggerNoAdjustDuration(int today, Trigger *trig, TimeTrig *tim,
}
/** FIXME: Fix bad interaction with SATISFY... need to rethink!!! */
if (result+trig->duration_days >= today &&
if (result+duration_days >= today &&
(trig->skip != SKIP_SKIP || !omit)) {
if (save_in_globals) {
LastTriggerDate = result; /* Save in global var */

View File

@@ -657,7 +657,7 @@ static SysVar SysVarArr[] = {
{"EndSentIg", 1, STR_TYPE, &EndSentIg, 0, 0 },
{"FirstIndent", 1, INT_TYPE, &FirstIndent, 0, 132 },
{"FoldYear", 1, INT_TYPE, &FoldYear, 0, 1 },
{"FormWidth", 1, INT_TYPE, &FormWidth, 20, 132 },
{"FormWidth", 1, INT_TYPE, &FormWidth, 20, 500 },
{"HushMode", 0, INT_TYPE, &Hush, 0, 0 },
{"IgnoreOnce", 0, INT_TYPE, &IgnoreOnce, 0, 0 },
{"InfDelta", 0, INT_TYPE, &InfiniteDelta, 0, 0 },

View File

@@ -857,7 +857,7 @@ set a057 value("a05"+"6")
"a05" + "6" => "a056"
value("a056") => "SDFJHSDF KSJDFH KJSDFH KSJDFH"
set a058 version()
version() => "03.03.00"
version() => "03.03.01"
set a059 wkday(today())
today() => 1991-02-16
wkday(1991-02-16) => "Saturday"
@@ -1098,7 +1098,7 @@ trigtimerep() => 0
set a105 trigduration()
trigduration() => -1
REM 2010-09-03 +3 -4 UNTIL 2012-01-01 PRIORITY 7 *14 AT 14:41 +15 *2 DURATION 3:33 MSG foo
REM 2010-09-03 +3 -4 UNTIL 2012-01-01 PRIORITY 7 *14 AT 14:41 +15 *2 DURATION 213 MSG foo
../tests/test.rem(340): Trig = Monday, 30 August, 2010 AT 14:41 DURATION 03:33
set a106 trigback()
trigback() => 4
@@ -1156,6 +1156,7 @@ set a129 23:30 + '2019-02-02@16:44'
# Multi-day reminder
REM 13 AT 16:00 DURATION 72:00 MSG 72-hour event
../tests/test.rem(371): Trig = Wednesday, 13 March, 1991 AT 16:00 DURATION 72:00
../tests/test.rem(371): Trig = Wednesday, 13 February, 1991 AT 16:00 DURATION 72:00
../tests/test.rem(371): Trig(adj) = Saturday, 16 February, 1991 AT 00:00 DURATION 16:00
72-hour event
@@ -1215,7 +1216,7 @@ dump
a109 2012-01-01
a128 2018-02-03@16:45
a039 "February"
a058 "03.03.00"
a058 "03.03.01"
a077 "1992 92
"
a096 -4
@@ -1351,49 +1352,489 @@ trigger(1991-02-28) => "28 February 1991"
Leaving UserFN _i() => "28 February 1991"
../tests/test.rem(387): Trig = Thursday, 28 February, 1991
# Regression test for bug found by Larry Hynes
REM SATISFY [day(trigdate()-25) == 14] MSG Foo
../tests/test.rem(390): Trig = Saturday, 16 February, 1991
trigdate() => 1991-02-16
1991-02-16 - 25 => 1991-01-22
day(1991-01-22) => 22
22 == 14 => 0
../tests/test.rem(390): Trig = Sunday, 17 February, 1991
trigdate() => 1991-02-17
1991-02-17 - 25 => 1991-01-23
day(1991-01-23) => 23
23 == 14 => 0
../tests/test.rem(390): Trig = Monday, 18 February, 1991
trigdate() => 1991-02-18
1991-02-18 - 25 => 1991-01-24
day(1991-01-24) => 24
24 == 14 => 0
../tests/test.rem(390): Trig = Tuesday, 19 February, 1991
trigdate() => 1991-02-19
1991-02-19 - 25 => 1991-01-25
day(1991-01-25) => 25
25 == 14 => 0
../tests/test.rem(390): Trig = Wednesday, 20 February, 1991
trigdate() => 1991-02-20
1991-02-20 - 25 => 1991-01-26
day(1991-01-26) => 26
26 == 14 => 0
../tests/test.rem(390): Trig = Thursday, 21 February, 1991
trigdate() => 1991-02-21
1991-02-21 - 25 => 1991-01-27
day(1991-01-27) => 27
27 == 14 => 0
../tests/test.rem(390): Trig = Friday, 22 February, 1991
trigdate() => 1991-02-22
1991-02-22 - 25 => 1991-01-28
day(1991-01-28) => 28
28 == 14 => 0
../tests/test.rem(390): Trig = Saturday, 23 February, 1991
trigdate() => 1991-02-23
1991-02-23 - 25 => 1991-01-29
day(1991-01-29) => 29
29 == 14 => 0
../tests/test.rem(390): Trig = Sunday, 24 February, 1991
trigdate() => 1991-02-24
1991-02-24 - 25 => 1991-01-30
day(1991-01-30) => 30
30 == 14 => 0
../tests/test.rem(390): Trig = Monday, 25 February, 1991
trigdate() => 1991-02-25
1991-02-25 - 25 => 1991-01-31
day(1991-01-31) => 31
31 == 14 => 0
../tests/test.rem(390): Trig = Tuesday, 26 February, 1991
trigdate() => 1991-02-26
1991-02-26 - 25 => 1991-02-01
day(1991-02-01) => 1
1 == 14 => 0
../tests/test.rem(390): Trig = Wednesday, 27 February, 1991
trigdate() => 1991-02-27
1991-02-27 - 25 => 1991-02-02
day(1991-02-02) => 2
2 == 14 => 0
../tests/test.rem(390): Trig = Thursday, 28 February, 1991
trigdate() => 1991-02-28
1991-02-28 - 25 => 1991-02-03
day(1991-02-03) => 3
3 == 14 => 0
../tests/test.rem(390): Trig = Friday, 1 March, 1991
trigdate() => 1991-03-01
1991-03-01 - 25 => 1991-02-04
day(1991-02-04) => 4
4 == 14 => 0
../tests/test.rem(390): Trig = Saturday, 2 March, 1991
trigdate() => 1991-03-02
1991-03-02 - 25 => 1991-02-05
day(1991-02-05) => 5
5 == 14 => 0
../tests/test.rem(390): Trig = Sunday, 3 March, 1991
trigdate() => 1991-03-03
1991-03-03 - 25 => 1991-02-06
day(1991-02-06) => 6
6 == 14 => 0
../tests/test.rem(390): Trig = Monday, 4 March, 1991
trigdate() => 1991-03-04
1991-03-04 - 25 => 1991-02-07
day(1991-02-07) => 7
7 == 14 => 0
../tests/test.rem(390): Trig = Tuesday, 5 March, 1991
trigdate() => 1991-03-05
1991-03-05 - 25 => 1991-02-08
day(1991-02-08) => 8
8 == 14 => 0
../tests/test.rem(390): Trig = Wednesday, 6 March, 1991
trigdate() => 1991-03-06
1991-03-06 - 25 => 1991-02-09
day(1991-02-09) => 9
9 == 14 => 0
../tests/test.rem(390): Trig = Thursday, 7 March, 1991
trigdate() => 1991-03-07
1991-03-07 - 25 => 1991-02-10
day(1991-02-10) => 10
10 == 14 => 0
../tests/test.rem(390): Trig = Friday, 8 March, 1991
trigdate() => 1991-03-08
1991-03-08 - 25 => 1991-02-11
day(1991-02-11) => 11
11 == 14 => 0
../tests/test.rem(390): Trig = Saturday, 9 March, 1991
trigdate() => 1991-03-09
1991-03-09 - 25 => 1991-02-12
day(1991-02-12) => 12
12 == 14 => 0
../tests/test.rem(390): Trig = Sunday, 10 March, 1991
trigdate() => 1991-03-10
1991-03-10 - 25 => 1991-02-13
day(1991-02-13) => 13
13 == 14 => 0
../tests/test.rem(390): Trig = Monday, 11 March, 1991
trigdate() => 1991-03-11
1991-03-11 - 25 => 1991-02-14
day(1991-02-14) => 14
14 == 14 => 1
../tests/test.rem(390): Trig(satisfied) = Monday, 11 March, 1991
# Check combo of SATISFY and long-duration events
REM 14 SATISFY [$Tw == 4] MSG Thursday, the 14th
../tests/test.rem(390): Trig = Thursday, 14 March, 1991
../tests/test.rem(393): Trig = Thursday, 14 March, 1991
$Tw => 4
4 == 4 => 1
../tests/test.rem(390): Trig(satisfied) = Thursday, 14 March, 1991
../tests/test.rem(393): Trig(satisfied) = Thursday, 14 March, 1991
REM 14 AT 16:00 DURATION 8:00 SATISFY [$Tw == 4] MSG Thursday, the 14th
../tests/test.rem(391): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:00
../tests/test.rem(394): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:00
$Tw => 4
4 == 4 => 1
../tests/test.rem(391): Trig(satisfied) = Thursday, 14 March, 1991 AT 16:00 DURATION 08:00
../tests/test.rem(394): Trig(satisfied) = Thursday, 14 March, 1991 AT 16:00 DURATION 08:00
REM 14 AT 16:00 DURATION 8:01 SATISFY [$Tw == 4] MSG Thursday, the 14th
../tests/test.rem(392): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:01
../tests/test.rem(395): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:01
../tests/test.rem(395): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 08:01
$Tw => 4
4 == 4 => 1
../tests/test.rem(392): Trig(satisfied) = Thursday, 14 March, 1991 AT 16:00 DURATION 08:01
../tests/test.rem(395): Trig(satisfied) = Thursday, 14 March, 1991 AT 16:00 DURATION 08:01
REM 14 AT 16:00 DURATION 32:00 SATISFY [$Tw == 4] MSG Thursday, the 14th
../tests/test.rem(393): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 32:00
../tests/test.rem(396): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 32:00
../tests/test.rem(396): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 32:00
$Tw => 4
4 == 4 => 1
../tests/test.rem(393): Trig(satisfied) = Thursday, 14 March, 1991 AT 16:00 DURATION 32:00
../tests/test.rem(396): Trig(satisfied) = Thursday, 14 March, 1991 AT 16:00 DURATION 32:00
REM 14 AT 16:00 DURATION 32:01 SATISFY [$Tw == 4] MSG Thursday, the 14th
../tests/test.rem(394): Trig = Thursday, 14 February, 1991 AT 16:00 DURATION 32:01
../tests/test.rem(397): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 32:01
../tests/test.rem(397): Trig = Thursday, 14 February, 1991 AT 16:00 DURATION 32:01
$Tw => 4
4 == 4 => 1
../tests/test.rem(394): Trig(adj) = Saturday, 16 February, 1991 AT 00:00 DURATION 00:01
../tests/test.rem(394): Trig(satisfied) = Saturday, 16 February, 1991 AT 00:00 DURATION 00:01
../tests/test.rem(397): Trig(adj) = Saturday, 16 February, 1991 AT 00:00 DURATION 00:01
../tests/test.rem(397): Trig(satisfied) = Saturday, 16 February, 1991 AT 00:00 DURATION 00:01
Thursday, the 14th
REM 14 AT 16:00 DURATION 40:00 SATISFY [$Tw == 4] MSG Thursday, the 14th
../tests/test.rem(395): Trig = Thursday, 14 February, 1991 AT 16:00 DURATION 40:00
../tests/test.rem(398): Trig = Thursday, 14 March, 1991 AT 16:00 DURATION 40:00
../tests/test.rem(398): Trig = Thursday, 14 February, 1991 AT 16:00 DURATION 40:00
$Tw => 4
4 == 4 => 1
../tests/test.rem(395): Trig(adj) = Saturday, 16 February, 1991 AT 00:00 DURATION 08:00
../tests/test.rem(395): Trig(satisfied) = Saturday, 16 February, 1991 AT 00:00 DURATION 08:00
../tests/test.rem(398): Trig(adj) = Saturday, 16 February, 1991 AT 00:00 DURATION 08:00
../tests/test.rem(398): Trig(satisfied) = Saturday, 16 February, 1991 AT 00:00 DURATION 08:00
Thursday, the 14th
# This is now an error
REM DURATION 15:00 MSG Should fail... need AT if you have DURATION.
../tests/test.rem(398): Cannot specify DURATION without specifying AT
../tests/test.rem(401): Cannot specify DURATION without specifying AT
# Parsing of AM/PM times
REM AT 0:00am MSG foo 0a
../tests/test.rem(404): Expecting time after AT
REM AT 1:00AM MSG foo 1a
../tests/test.rem(405): Trig = Saturday, 16 February, 1991 AT 01:00
foo 1a
REM AT 2:00am MSG foo 2a
../tests/test.rem(406): Trig = Saturday, 16 February, 1991 AT 02:00
foo 2a
REM AT 3:00AM MSG foo 3a
../tests/test.rem(407): Trig = Saturday, 16 February, 1991 AT 03:00
foo 3a
REM AT 4:00am MSG foo 4a
../tests/test.rem(408): Trig = Saturday, 16 February, 1991 AT 04:00
foo 4a
REM AT 5:00AM MSG foo 5a
../tests/test.rem(409): Trig = Saturday, 16 February, 1991 AT 05:00
foo 5a
REM AT 6:00am MSG foo 6a
../tests/test.rem(410): Trig = Saturday, 16 February, 1991 AT 06:00
foo 6a
REM AT 7:00AM MSG foo 7a
../tests/test.rem(411): Trig = Saturday, 16 February, 1991 AT 07:00
foo 7a
REM AT 8:00am MSG foo 8a
../tests/test.rem(412): Trig = Saturday, 16 February, 1991 AT 08:00
foo 8a
REM AT 9:00AM MSG foo 9a
../tests/test.rem(413): Trig = Saturday, 16 February, 1991 AT 09:00
foo 9a
REM AT 10:00am MSG foo 10a
../tests/test.rem(414): Trig = Saturday, 16 February, 1991 AT 10:00
foo 10a
REM AT 11:00AM MSG foo 11a
../tests/test.rem(415): Trig = Saturday, 16 February, 1991 AT 11:00
foo 11a
REM AT 12:00am MSG foo 12a
../tests/test.rem(416): Trig = Saturday, 16 February, 1991 AT 00:00
foo 12a
REM AT 13:00AM MSG foo 13a
../tests/test.rem(417): Expecting time after AT
REM AT 0:00pm MSG foo 0p
../tests/test.rem(418): Expecting time after AT
REM AT 1:00PM MSG foo 1p
../tests/test.rem(419): Trig = Saturday, 16 February, 1991 AT 13:00
foo 1p
REM AT 2:00pm MSG foo 2p
../tests/test.rem(420): Trig = Saturday, 16 February, 1991 AT 14:00
foo 2p
REM AT 3:00PM MSG foo 3p
../tests/test.rem(421): Trig = Saturday, 16 February, 1991 AT 15:00
foo 3p
REM AT 4:00pm MSG foo 4p
../tests/test.rem(422): Trig = Saturday, 16 February, 1991 AT 16:00
foo 4p
REM AT 5:00PM MSG foo 5p
../tests/test.rem(423): Trig = Saturday, 16 February, 1991 AT 17:00
foo 5p
REM AT 6:00pm MSG foo 6p
../tests/test.rem(424): Trig = Saturday, 16 February, 1991 AT 18:00
foo 6p
REM AT 7:00PM MSG foo 7p
../tests/test.rem(425): Trig = Saturday, 16 February, 1991 AT 19:00
foo 7p
REM AT 8:00pm MSG foo 8p
../tests/test.rem(426): Trig = Saturday, 16 February, 1991 AT 20:00
foo 8p
REM AT 9:00PM MSG foo 9p
../tests/test.rem(427): Trig = Saturday, 16 February, 1991 AT 21:00
foo 9p
REM AT 10:00pm MSG foo 10p
../tests/test.rem(428): Trig = Saturday, 16 February, 1991 AT 22:00
foo 10p
REM AT 11:00PM MSG foo 11p
../tests/test.rem(429): Trig = Saturday, 16 February, 1991 AT 23:00
foo 11p
REM AT 12:00pm MSG foo 12p
../tests/test.rem(430): Trig = Saturday, 16 February, 1991 AT 12:00
foo 12p
REM AT 13:00PM MSG foo 13p
../tests/test.rem(431): Expecting time after AT
DEBUG +x
SET x 0:00am + 0
../tests/test.rem(434): Ill-formed time
SET x 1:00AM + 0
01:00 + 0 => 01:00
SET x 2:00am + 0
02:00 + 0 => 02:00
SET x 3:00AM + 0
03:00 + 0 => 03:00
SET x 4:00am + 0
04:00 + 0 => 04:00
SET x 5:00AM + 0
05:00 + 0 => 05:00
SET x 6:00am + 0
06:00 + 0 => 06:00
SET x 7:00AM + 0
07:00 + 0 => 07:00
SET x 8:00am + 0
08:00 + 0 => 08:00
SET x 9:00AM + 0
09:00 + 0 => 09:00
SET x 10:00am + 0
10:00 + 0 => 10:00
SET x 11:00AM + 0
11:00 + 0 => 11:00
SET x 12:00am + 0
00:00 + 0 => 00:00
SET x 13:00AM + 0
../tests/test.rem(447): Ill-formed time
SET x 0:00pm + 0
../tests/test.rem(449): Ill-formed time
SET x 1:00PM + 0
13:00 + 0 => 13:00
SET x 2:00pm + 0
14:00 + 0 => 14:00
SET x 3:00PM + 0
15:00 + 0 => 15:00
SET x 4:00pm + 0
16:00 + 0 => 16:00
SET x 5:00PM + 0
17:00 + 0 => 17:00
SET x 6:00pm + 0
18:00 + 0 => 18:00
SET x 7:00PM + 0
19:00 + 0 => 19:00
SET x 8:00pm + 0
20:00 + 0 => 20:00
SET x 9:00PM + 0
21:00 + 0 => 21:00
SET x 10:00pm + 0
22:00 + 0 => 22:00
SET x 11:00PM + 0
23:00 + 0 => 23:00
SET x 12:00pm + 0
12:00 + 0 => 12:00
SET x 13:00PM + 0
../tests/test.rem(462): Ill-formed time
SET x '2015-02-03@0:00am' + 0
../tests/test.rem(464): Ill-formed time
SET x '2015-02-03@1:00AM' + 0
2015-02-03@01:00 + 0 => 2015-02-03@01:00
SET x '2015-02-03@2:00am' + 0
2015-02-03@02:00 + 0 => 2015-02-03@02:00
SET x '2015-02-03@3:00AM' + 0
2015-02-03@03:00 + 0 => 2015-02-03@03:00
SET x '2015-02-03@4:00am' + 0
2015-02-03@04:00 + 0 => 2015-02-03@04:00
SET x '2015-02-03@5:00AM' + 0
2015-02-03@05:00 + 0 => 2015-02-03@05:00
SET x '2015-02-03@6:00am' + 0
2015-02-03@06:00 + 0 => 2015-02-03@06:00
SET x '2015-02-03@7:00AM' + 0
2015-02-03@07:00 + 0 => 2015-02-03@07:00
SET x '2015-02-03@8:00am' + 0
2015-02-03@08:00 + 0 => 2015-02-03@08:00
SET x '2015-02-03@9:00AM' + 0
2015-02-03@09:00 + 0 => 2015-02-03@09:00
SET x '2015-02-03@10:00am' + 0
2015-02-03@10:00 + 0 => 2015-02-03@10:00
SET x '2015-02-03@11:00AM' + 0
2015-02-03@11:00 + 0 => 2015-02-03@11:00
SET x '2015-02-03@12:00am' + 0
2015-02-03@00:00 + 0 => 2015-02-03@00:00
SET x '2015-02-03@13:00AM' + 0
../tests/test.rem(477): Ill-formed time
SET x '2015-02-03@0:00pm' + 0
../tests/test.rem(479): Ill-formed time
SET x '2015-02-03@1:00PM' + 0
2015-02-03@13:00 + 0 => 2015-02-03@13:00
SET x '2015-02-03@2:00pm' + 0
2015-02-03@14:00 + 0 => 2015-02-03@14:00
SET x '2015-02-03@3:00PM' + 0
2015-02-03@15:00 + 0 => 2015-02-03@15:00
SET x '2015-02-03@4:00pm' + 0
2015-02-03@16:00 + 0 => 2015-02-03@16:00
SET x '2015-02-03@5:00PM' + 0
2015-02-03@17:00 + 0 => 2015-02-03@17:00
SET x '2015-02-03@6:00pm' + 0
2015-02-03@18:00 + 0 => 2015-02-03@18:00
SET x '2015-02-03@7:00PM' + 0
2015-02-03@19:00 + 0 => 2015-02-03@19:00
SET x '2015-02-03@8:00pm' + 0
2015-02-03@20:00 + 0 => 2015-02-03@20:00
SET x '2015-02-03@9:00PM' + 0
2015-02-03@21:00 + 0 => 2015-02-03@21:00
SET x '2015-02-03@10:00pm' + 0
2015-02-03@22:00 + 0 => 2015-02-03@22:00
SET x '2015-02-03@11:00PM' + 0
2015-02-03@23:00 + 0 => 2015-02-03@23:00
SET x '2015-02-03@12:00pm' + 0
2015-02-03@12:00 + 0 => 2015-02-03@12:00
SET x '2015-02-03@13:00PM' + 0
../tests/test.rem(492): Ill-formed time
# Test the ampm function
set x ampm(0:12) + ""
ampm(00:12) => "12:12AM"
"12:12AM" + "" => "12:12AM"
set x ampm(1:12) + ""
ampm(01:12) => "1:12AM"
"1:12AM" + "" => "1:12AM"
set x ampm(2:12) + ""
ampm(02:12) => "2:12AM"
"2:12AM" + "" => "2:12AM"
set x ampm(3:12) + ""
ampm(03:12) => "3:12AM"
"3:12AM" + "" => "3:12AM"
set x ampm(4:12) + ""
ampm(04:12) => "4:12AM"
"4:12AM" + "" => "4:12AM"
set x ampm(5:12) + ""
ampm(05:12) => "5:12AM"
"5:12AM" + "" => "5:12AM"
set x ampm(6:12) + ""
ampm(06:12) => "6:12AM"
"6:12AM" + "" => "6:12AM"
set x ampm(7:12) + ""
ampm(07:12) => "7:12AM"
"7:12AM" + "" => "7:12AM"
set x ampm(8:12) + ""
ampm(08:12) => "8:12AM"
"8:12AM" + "" => "8:12AM"
set x ampm(9:12) + ""
ampm(09:12) => "9:12AM"
"9:12AM" + "" => "9:12AM"
set x ampm(10:12) + ""
ampm(10:12) => "10:12AM"
"10:12AM" + "" => "10:12AM"
set x ampm(11:12) + ""
ampm(11:12) => "11:12AM"
"11:12AM" + "" => "11:12AM"
set x ampm(12:12) + ""
ampm(12:12) => "12:12PM"
"12:12PM" + "" => "12:12PM"
set x ampm(13:12) + ""
ampm(13:12) => "1:12PM"
"1:12PM" + "" => "1:12PM"
set x ampm(14:12) + ""
ampm(14:12) => "2:12PM"
"2:12PM" + "" => "2:12PM"
set x ampm(15:12) + ""
ampm(15:12) => "3:12PM"
"3:12PM" + "" => "3:12PM"
set x ampm(16:12) + ""
ampm(16:12) => "4:12PM"
"4:12PM" + "" => "4:12PM"
set x ampm(17:12) + ""
ampm(17:12) => "5:12PM"
"5:12PM" + "" => "5:12PM"
set x ampm(18:12) + ""
ampm(18:12) => "6:12PM"
"6:12PM" + "" => "6:12PM"
set x ampm(19:12) + ""
ampm(19:12) => "7:12PM"
"7:12PM" + "" => "7:12PM"
set x ampm(20:12) + ""
ampm(20:12) => "8:12PM"
"8:12PM" + "" => "8:12PM"
set x ampm(21:12) + ""
ampm(21:12) => "9:12PM"
"9:12PM" + "" => "9:12PM"
set x ampm(22:12) + ""
ampm(22:12) => "10:12PM"
"10:12PM" + "" => "10:12PM"
set x ampm(23:12) + ""
ampm(23:12) => "11:12PM"
"11:12PM" + "" => "11:12PM"
# Coerce with am/pm
set x coerce("TIME", "12:45am")
coerce("TIME", "12:45am") => 00:45
set x coerce("TIME", "12:45")
coerce("TIME", "12:45") => 12:45
set x coerce("TIME", "1:45pm")
coerce("TIME", "1:45pm") => 13:45
set x coerce("DATETIME", "2020-05-05@12:45am")
coerce("DATETIME", "2020-05-05@12:45am") => 2020-05-05@00:45
set x coerce("DATETIME", "2020-05-05@12:45")
coerce("DATETIME", "2020-05-05@12:45") => 2020-05-05@12:45
set x coerce("DATETIME", "2020-05-05@1:45pm")
coerce("DATETIME", "2020-05-05@1:45pm") => 2020-05-05@13:45
# Don't want Remind to queue reminders
EXIT
Test 2

View File

@@ -337,7 +337,7 @@ set a103 trigtimedelta()
set a104 trigtimerep()
set a105 trigduration()
REM 2010-09-03 +3 -4 UNTIL 2012-01-01 PRIORITY 7 *14 AT 14:41 +15 *2 DURATION 3:33 MSG foo
REM 2010-09-03 +3 -4 UNTIL 2012-01-01 PRIORITY 7 *14 AT 14:41 +15 *2 DURATION 213 MSG foo
set a106 trigback()
set a107 trigdelta()
set a108 trigrep()
@@ -386,6 +386,9 @@ OMIT DUMP
# Regression test for bugfix in Hebrew calendar Adar jahrzeit
[_i(14, "Adar", today(), 5761)] MSG Purim
# Regression test for bug found by Larry Hynes
REM SATISFY [day(trigdate()-25) == 14] MSG Foo
# Check combo of SATISFY and long-duration events
REM 14 SATISFY [$Tw == 4] MSG Thursday, the 14th
REM 14 AT 16:00 DURATION 8:00 SATISFY [$Tw == 4] MSG Thursday, the 14th
@@ -397,6 +400,134 @@ REM 14 AT 16:00 DURATION 40:00 SATISFY [$Tw == 4] MSG Thursday, the 14th
# This is now an error
REM DURATION 15:00 MSG Should fail... need AT if you have DURATION.
# Parsing of AM/PM times
REM AT 0:00am MSG foo 0a
REM AT 1:00AM MSG foo 1a
REM AT 2:00am MSG foo 2a
REM AT 3:00AM MSG foo 3a
REM AT 4:00am MSG foo 4a
REM AT 5:00AM MSG foo 5a
REM AT 6:00am MSG foo 6a
REM AT 7:00AM MSG foo 7a
REM AT 8:00am MSG foo 8a
REM AT 9:00AM MSG foo 9a
REM AT 10:00am MSG foo 10a
REM AT 11:00AM MSG foo 11a
REM AT 12:00am MSG foo 12a
REM AT 13:00AM MSG foo 13a
REM AT 0:00pm MSG foo 0p
REM AT 1:00PM MSG foo 1p
REM AT 2:00pm MSG foo 2p
REM AT 3:00PM MSG foo 3p
REM AT 4:00pm MSG foo 4p
REM AT 5:00PM MSG foo 5p
REM AT 6:00pm MSG foo 6p
REM AT 7:00PM MSG foo 7p
REM AT 8:00pm MSG foo 8p
REM AT 9:00PM MSG foo 9p
REM AT 10:00pm MSG foo 10p
REM AT 11:00PM MSG foo 11p
REM AT 12:00pm MSG foo 12p
REM AT 13:00PM MSG foo 13p
DEBUG +x
SET x 0:00am + 0
SET x 1:00AM + 0
SET x 2:00am + 0
SET x 3:00AM + 0
SET x 4:00am + 0
SET x 5:00AM + 0
SET x 6:00am + 0
SET x 7:00AM + 0
SET x 8:00am + 0
SET x 9:00AM + 0
SET x 10:00am + 0
SET x 11:00AM + 0
SET x 12:00am + 0
SET x 13:00AM + 0
SET x 0:00pm + 0
SET x 1:00PM + 0
SET x 2:00pm + 0
SET x 3:00PM + 0
SET x 4:00pm + 0
SET x 5:00PM + 0
SET x 6:00pm + 0
SET x 7:00PM + 0
SET x 8:00pm + 0
SET x 9:00PM + 0
SET x 10:00pm + 0
SET x 11:00PM + 0
SET x 12:00pm + 0
SET x 13:00PM + 0
SET x '2015-02-03@0:00am' + 0
SET x '2015-02-03@1:00AM' + 0
SET x '2015-02-03@2:00am' + 0
SET x '2015-02-03@3:00AM' + 0
SET x '2015-02-03@4:00am' + 0
SET x '2015-02-03@5:00AM' + 0
SET x '2015-02-03@6:00am' + 0
SET x '2015-02-03@7:00AM' + 0
SET x '2015-02-03@8:00am' + 0
SET x '2015-02-03@9:00AM' + 0
SET x '2015-02-03@10:00am' + 0
SET x '2015-02-03@11:00AM' + 0
SET x '2015-02-03@12:00am' + 0
SET x '2015-02-03@13:00AM' + 0
SET x '2015-02-03@0:00pm' + 0
SET x '2015-02-03@1:00PM' + 0
SET x '2015-02-03@2:00pm' + 0
SET x '2015-02-03@3:00PM' + 0
SET x '2015-02-03@4:00pm' + 0
SET x '2015-02-03@5:00PM' + 0
SET x '2015-02-03@6:00pm' + 0
SET x '2015-02-03@7:00PM' + 0
SET x '2015-02-03@8:00pm' + 0
SET x '2015-02-03@9:00PM' + 0
SET x '2015-02-03@10:00pm' + 0
SET x '2015-02-03@11:00PM' + 0
SET x '2015-02-03@12:00pm' + 0
SET x '2015-02-03@13:00PM' + 0
# Test the ampm function
set x ampm(0:12) + ""
set x ampm(1:12) + ""
set x ampm(2:12) + ""
set x ampm(3:12) + ""
set x ampm(4:12) + ""
set x ampm(5:12) + ""
set x ampm(6:12) + ""
set x ampm(7:12) + ""
set x ampm(8:12) + ""
set x ampm(9:12) + ""
set x ampm(10:12) + ""
set x ampm(11:12) + ""
set x ampm(12:12) + ""
set x ampm(13:12) + ""
set x ampm(14:12) + ""
set x ampm(15:12) + ""
set x ampm(16:12) + ""
set x ampm(17:12) + ""
set x ampm(18:12) + ""
set x ampm(19:12) + ""
set x ampm(20:12) + ""
set x ampm(21:12) + ""
set x ampm(22:12) + ""
set x ampm(23:12) + ""
# Coerce with am/pm
set x coerce("TIME", "12:45am")
set x coerce("TIME", "12:45")
set x coerce("TIME", "1:45pm")
set x coerce("DATETIME", "2020-05-05@12:45am")
set x coerce("DATETIME", "2020-05-05@12:45")
set x coerce("DATETIME", "2020-05-05@1:45pm")
# Don't want Remind to queue reminders
EXIT
__EOF__
REM This line should not even be seen
And you can put whatever you like here.