Compare commits

...

8 Commits

Author SHA1 Message Date
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
11 changed files with 346 additions and 68 deletions

View File

@@ -3,7 +3,7 @@ THE REMIND COPYRIGHT
1. REMIND refers to the entire set of files and documentation in the 1. REMIND refers to the entire set of files and documentation in the
REMIND package. 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. individual files.
3. DISTRIBUTION AND USE 3. DISTRIBUTION AND USE

View File

@@ -27,6 +27,8 @@ CHANGES TO REMIND
- IMPROVEMENT: If terminal size can be determined, set $FormWidth to - IMPROVEMENT: If terminal size can be determined, set $FormWidth to
terminal width - 8; if not, set $FormWidth to 72. 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 * Version 3.3 Patch 0 - 2020-01-31
- FIX: rem2ps: Add a %%PageBoundingBox: document structuring convention - FIX: rem2ps: Add a %%PageBoundingBox: document structuring convention

View File

@@ -2223,6 +2223,20 @@ is supplied, only the date component is used.
Returns the time of "astronomical twilight" on the specified \fIdate\fR. If Returns the time of "astronomical twilight" on the specified \fIdate\fR. If
\fIdate\fR is omitted, defaults to \fBtoday()\fR. \fIdate\fR is omitted, defaults to \fBtoday()\fR.
.TP .TP
.B ampm(t_time [,s_am [,s_pm]])
Returns a \fBSTRING\fR that is the result of converting \fItime\fR
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". For example:
.RS
.PP
.nf
ampm(0:22) returns "12:22AM"
ampm(17:45, "am", "pm") returns "5:45pm"
.fi
.PP
.RE
.TP
.B args(s_fname) .B args(s_fname)
Returns the number of arguments expected by the user-defined function Returns the number of arguments expected by the user-defined function
\fIfname\fR, or \-1 if no such user-defined function exists. Note that \fIfname\fR, or \-1 if no such user-defined function exists. Note that

View File

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

View File

@@ -245,7 +245,7 @@ static void WriteBottomCalLine (void);
static void WriteIntermediateCalLine (void); static void WriteIntermediateCalLine (void);
static void WriteCalDays (void); static void WriteCalDays (void);
static void PrintJSONString(char const *s) void PrintJSONString(char const *s)
{ {
while (*s) { while (*s) {
switch(*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("\""); printf("\"");
PrintJSONString(name); PrintJSONString(name);
printf("\":%d, ", val); 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 value is blank, skip it! */
if (!val || !*val) { if (!val || !*val) {
@@ -283,7 +283,7 @@ static void PrintJSONKeyPairString(char const *name, char const *val)
printf("\", "); printf("\", ");
} }
static void PrintJSONKeyPairDate(char const *name, int jul) void PrintJSONKeyPairDate(char const *name, int jul)
{ {
int y, m, d; int y, m, d;
if (jul == NO_DATE) { 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; int y, m, d, h, i, k;
if (dt == NO_TIME) { 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 #ifdef REM_USE_WCHAR
static void PutWideChar(wchar_t const wc) static void PutWideChar(wchar_t const wc)
{ {

View File

@@ -722,26 +722,12 @@ int DoCoerce(char type, Value *v)
return OK; return OK;
case STR_TYPE: case STR_TYPE:
h = 0;
m = 0;
s = v->v.str; s = v->v.str;
if (!isdigit(*s)) return E_CANT_COERCE; if (ParseLiteralTime(&s, &i)) return E_CANT_COERCE;
while (isdigit(*s)) { if (*s) return E_CANT_COERCE;
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;
v->type = TIME_TYPE; v->type = TIME_TYPE;
free(v->v.str); free(v->v.str);
v->v.val = h*60+m; v->v.val = i;
return OK; return OK;
default: return E_CANT_COERCE; default: return E_CANT_COERCE;
@@ -1243,6 +1229,48 @@ int CopyValue(Value *dest, const Value *src)
return OK; 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 */ /* ParseLiteralDate */
@@ -1254,11 +1282,9 @@ int CopyValue(Value *dest, const Value *src)
int ParseLiteralDate(char const **s, int *jul, int *tim) int ParseLiteralDate(char const **s, int *jul, int *tim)
{ {
int y, m, d; int y, m, d;
int hour, min; int r;
int ampm = 0;
y=0; m=0; d=0; y=0; m=0; d=0;
hour=0; min=0;
*tim = NO_TIME; *tim = NO_TIME;
if (!isdigit(**s)) return E_BAD_DATE; if (!isdigit(**s)) return E_BAD_DATE;
@@ -1288,40 +1314,9 @@ int ParseLiteralDate(char const **s, int *jul, int *tim)
/* Do we have a time part as well? */ /* Do we have a time part as well? */
if (**s == ' ' || **s == '@' || **s == 'T' || **s == 't') { if (**s == ' ' || **s == '@' || **s == 'T' || **s == 't') {
(*s)++; (*s)++;
while(isdigit(**s)) { r = ParseLiteralTime(s, tim);
hour *= 10; if (r != OK) return r;
hour += *(*s)++ - '0';
}
if (**s != ':' && **s != '.' && **s != TimeSep) return E_BAD_TIME;
(*s)++;
while(isdigit(**s)) {
min *= 10;
min += *(*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 (hour>23 || min>59) return E_BAD_TIME;
if (ampm) {
if (hour < 1 || hour > 12) return E_BAD_TIME;
if (ampm == 'a') {
if (hour == 12) {
hour = 0;
}
} else if (ampm == 'p') {
if (hour < 12) {
hour += 12;
}
}
}
*tim = hour * 60 + min;
} }
return OK; return OK;
} }

View File

@@ -57,6 +57,7 @@ static int FADawn (func_info *);
static int FADusk (func_info *); static int FADusk (func_info *);
static int FAbs (func_info *); static int FAbs (func_info *);
static int FAccess (func_info *); static int FAccess (func_info *);
static int FAmpm (func_info *);
static int FArgs (func_info *); static int FArgs (func_info *);
static int FAsc (func_info *); static int FAsc (func_info *);
static int FBaseyr (func_info *); static int FBaseyr (func_info *);
@@ -206,6 +207,7 @@ BuiltinFunc Func[] = {
{ "access", 2, 2, 0, FAccess }, { "access", 2, 2, 0, FAccess },
{ "adawn", 0, 1, 0, FADawn}, { "adawn", 0, 1, 0, FADawn},
{ "adusk", 0, 1, 0, FADusk}, { "adusk", 0, 1, 0, FADusk},
{ "ampm", 1, 3, 1, FAmpm },
{ "args", 1, 1, 0, FArgs }, { "args", 1, 1, 0, FArgs },
{ "asc", 1, 1, 1, FAsc }, { "asc", 1, 1, 1, FAsc },
{ "baseyr", 0, 0, 1, FBaseyr }, { "baseyr", 0, 0, 1, FBaseyr },
@@ -886,6 +888,57 @@ static int FSgn(func_info *info)
return OK; return OK;
} }
/***************************************************************/
/* */
/* FAmpm - return a time as a string with "AM" or "PM" suffix */
/* */
/***************************************************************/
static int FAmpm(func_info *info)
{
int h, m;
char const *am = "AM";
char const *pm = "PM";
char const *ampm = NULL;
char outbuf[64];
ASSERT_TYPE(0, TIME_TYPE);
if (Nargs >= 2) {
ASSERT_TYPE(1, STR_TYPE);
am = ARGSTR(1);
if (Nargs >= 3) {
ASSERT_TYPE(2, STR_TYPE);
pm = ARGSTR(2);
}
}
h = ARGV(0) / 60;
m = ARGV(0) % 60;
if (h <= 11) {
/* AM */
if (h == 0) {
snprintf(outbuf, sizeof(outbuf), "12:%02d", m);
} else {
snprintf(outbuf, sizeof(outbuf), "%d:%02d", h, m);
}
ampm = am;
} else {
if (h > 12) {
h -= 12;
}
snprintf(outbuf, sizeof(outbuf), "%d:%02d", h, 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. */ /* FOrd - returns a string containing ordinal number. */

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 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 DoSubstFromString (char const *source, DynamicBuffer *dbuf, int jul, int tim);
int ParseLiteralDate (char const **s, 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 EvalExpr (char const **e, Value *v, ParsePtr p);
int DoCoerce (char type, Value *v); int DoCoerce (char type, Value *v);
void PrintValue (Value *v, FILE *fp); void PrintValue (Value *v, FILE *fp);
@@ -153,3 +154,9 @@ void SaveAllTriggerInfo(Trigger const *t, TimeTrig const *tt, int trigdate, int
void PerIterationInit(void); void PerIterationInit(void);
char const *Decolorize(int r, int g, int b); char const *Decolorize(int r, int g, int b);
char const *Colorize(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

@@ -448,6 +448,66 @@ static int CalculateNextTimeUsingSched(QueuedRem *q)
} }
} }
/* Dump the queue in JSON format */
static void
json_queue(QueuedRem const *q)
{
printf("[");
while(q) {
if (q->tt.nexttime == NO_TIME) {
q = q->next;
continue;
}
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 */ /* DaemonWait */
@@ -532,6 +592,11 @@ static void DaemonWait(unsigned int sleeptime)
} }
printf("NOTE endqueue\n"); printf("NOTE endqueue\n");
fflush(stdout); 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")) { } else if (!strcmp(cmdLine, "REREAD\n")) {
printf("NOTE reread\n"); printf("NOTE reread\n");
fflush(stdout); fflush(stdout);

View File

@@ -1745,7 +1745,96 @@ SET x '2015-02-03@12:00pm' + 0
SET x '2015-02-03@13:00PM' + 0 SET x '2015-02-03@13:00PM' + 0
../tests/test.rem(492): Ill-formed time ../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 Test 2

View File

@@ -491,6 +491,43 @@ SET x '2015-02-03@11:00PM' + 0
SET x '2015-02-03@12:00pm' + 0 SET x '2015-02-03@12:00pm' + 0
SET x '2015-02-03@13: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__ __EOF__
REM This line should not even be seen REM This line should not even be seen
And you can put whatever you like here. And you can put whatever you like here.