summaryrefslogtreecommitdiff
path: root/Functions/Calendar
diff options
context:
space:
mode:
Diffstat (limited to 'Functions/Calendar')
-rw-r--r--Functions/Calendar/calendar6
-rw-r--r--Functions/Calendar/calendar_add178
-rw-r--r--Functions/Calendar/calendar_parse155
-rw-r--r--Functions/Calendar/calendar_scandate124
4 files changed, 340 insertions, 123 deletions
diff --git a/Functions/Calendar/calendar b/Functions/Calendar/calendar
index e4cdff8e4..48876aa51 100644
--- a/Functions/Calendar/calendar
+++ b/Functions/Calendar/calendar
@@ -296,7 +296,9 @@ chmod 600 $mycmds
fi
# Look for a repeat time.
if [[ -n ${reply[rpttime]} ]]; then
- (( repeattime = ${reply[rpttime]}, repeating = 1 ))
+ # The actual time of the next event, which appears as text
+ (( repeattime = ${reply[rpttime]} ))
+ (( repeating = 1 ))
else
(( repeating = 0 ))
fi
@@ -320,7 +322,7 @@ chmod 600 $mycmds
match=()
# Strip continuation lines starting " #".
while [[ $showline = (#b)(*$'\n')[[:space:]]##\#[^$'\n']##(|$'\n'(*)) ]]; do
- showline="$match[1]$match[3]"
+ showline="$match[1]$match[3]"
done
# Strip trailing empty lines
showline=${showline%%[[:space:]]#}
diff --git a/Functions/Calendar/calendar_add b/Functions/Calendar/calendar_add
index eded25b2a..c06deda3a 100644
--- a/Functions/Calendar/calendar_add
+++ b/Functions/Calendar/calendar_add
@@ -7,14 +7,19 @@
# entry before the first existing entry with a later date and time.
emulate -L zsh
-setopt extendedglob
+setopt extendedglob # xtrace
local context=":datetime:calendar_add:"
+local vdatefmt="%Y%m%dT%H%M%S"
+local d='[[:digit:]]'
-local calendar newfile REPLY lastline opt
-local -a calendar_entries lockfiles reply
-integer my_date done rstat nolock nobackup new_recurring old_recurring
-local -A reply parse_new parse_old recurring_uids
+local calendar newfile REPLY lastline opt text occur
+local -a calendar_entries lockfiles reply occurrences
+integer my_date done rstat nolock nobackup new_recurring
+integer keep_my_uid
+local -A reply parse_new parse_old
+local -a match mbegin mend
+local my_uid their_uid
autoload -U calendar_{parse,read,lockfiles}
@@ -47,7 +52,6 @@ if ! calendar_parse $addline; then
fi
parse_new=("${(@kv)reply}")
(( my_date = $parse_new[time] ))
-[[ -n $parse_new[rpttime] ]] && (( new_recurring = 1 ))
if zstyle -t $context reformat-date; then
local datefmt
zstyle -s $context date-format datefmt ||
@@ -55,12 +59,24 @@ if zstyle -t $context reformat-date; then
strftime -s REPLY $datefmt $parse_new[time]
addline="$REPLY $parse_new[text1]"
fi
+if [[ -n $parse_new[rptstr] ]]; then
+ (( new_recurring = 1 ))
+ if [[ $parse_new[rptstr] = CANCELLED ]]; then
+ (( done = 1 ))
+ elif [[ $addline = (#b)(*[[:space:]\#]RECURRENCE[[:space:]]##)([^[:space:]]##)([[:space:]]*|) ]]; then
+ # Use the updated recurrence time
+ strftime -s REPLY $vdatefmt ${parse_new[schedrpttime]}
+ addline="${match[1]}$REPLY${match[3]}"
+ else
+ # Add a recurrence time
+ [[ $addline = *$'\n' ]] || addline+=$'\n'
+ strftime -s REPLY $vdatefmt ${parse_new[schedrpttime]}
+ addline+=" # RECURRENCE $REPLY"
+ fi
+fi
# $calendar doesn't necessarily exist yet.
-local -a match mbegin mend
-local my_uid their_uid
-
# Match a UID, a unique identifier for the entry inherited from
# text/calendar format.
local uidpat='(|*[[:space:]])UID[[:space:]]##(#b)([[:xdigit:]]##)(|[[:space:]]*)'
@@ -87,14 +103,112 @@ fi
calendar_read $calendar
if [[ -n $my_uid ]]; then
- # Pre-scan to find recurring events with a UID
+ # Pre-scan to events with the same UID
for line in $calendar_entries; do
calendar_parse $line || continue
+ parse_old=("${(@kv)reply}")
# Recurring with a UID?
- if [[ -n $reply[rpttime] && $line = ${~uidpat} ]]; then
- # Yes, so record this as a recurring event.
+ if [[ $line = ${~uidpat} ]]; then
their_uid=${(U)match[1]}
- recurring_uids[$their_uid]=$reply[time]
+ if [[ $their_uid = $my_uid ]]; then
+ # Deal with recurrences and also some add some
+ # extra magic for cancellation.
+
+ # See if we have found a recurrent version
+ if [[ -z $parse_old[rpttime] ]]; then
+ # No, so assume this is a straightforward replacement
+ # of a non-recurring event.
+
+ # Is this a cancellation of a non-recurring event?
+ # Look for an OCCURRENCE in the form
+ # OCCURRENCE 20100513T110000 CANCELLED
+ # although we don't bother looking at the date/time---
+ # it's one-off, so this should already be unique.
+ if [[ $new_recurring -eq 0 && \
+ $parse_new[text1] = (|*[[:space:]\#])"OCCURRENCE"[[:space:]]##([^[:space:]]##[[:space:]]##CANCELLED)(|[[:space:]]*) ]]; then
+ # Yes, so skip both the old and new events.
+ (( done = 1 ))
+ fi
+ # We'll skip this UID when we encounter it again.
+ continue
+ fi
+ if (( new_recurring )); then
+ # Replacing a recurrence; there can be only one.
+ # TBD: do we replace existing occurrences of the
+ # replaced recurrent event? I'm guessing not, but
+ # if we keep the UID then maybe we should.
+ #
+ # TBD: ick, suppose we're cancelling an even that
+ # we added to a recurring sequence but didn't replace
+ # the recurrence. We might get RPT CANCELLED for this.
+ # That would be bad. Should invent better way of
+ # cancelling non-recurring event.
+ continue
+ else
+ # The recorded event is recurring, but the new one is a
+ # one-off event. If there are embedded OCCURRENCE declarations,
+ # use those.
+ #
+ # TBD: We could be clever about text associated
+ # with the occurrence. Copying the entire text
+ # of the meeting seems like overkill but people often
+ # add specific remarks about why this occurrence was
+ # moved/cancelled.
+ #
+ # TBD: one case we don't yet handle is cancelling
+ # something that isn't replacing a recurrence, i.e.
+ # something we added as OCCURRENCE XXXXXXXXTXXXXXX <when>.
+ # If we're adding a CANCELLED occurrence we should
+ # look to see if it matches <when> and if so simply
+ # delete that occurrence.
+ #
+ # TBD: one nasty case is if the new occurrence
+ # occurs before the current scheduled time. As we
+ # never look backwards we'll miss it.
+ text=$addline
+ occurrences=()
+ while [[ $text = (#b)(|*[[:space:]\#])"OCCURRENCE"[[:space:]]##([^[:space:]]##[[:space:]]##[^[:space:]]##)(|[[:space:]]*) ]]; do
+ occurrences+=($match[2])
+ text="$match[1] $match[3]"
+ done
+ if (( ! ${#occurrences} )); then
+ # No embedded occurrences. We'll manufacture one
+ # that doesn't refer to an original recurrence.
+ strftime -s REPLY $vdatefmt $my_date
+ occurrences=("XXXXXXXXTXXXXXX $REPLY")
+ fi
+ # Add these occurrences, checking if they replace
+ # an existing one.
+ for occur in ${(o)occurrences}; do
+ REPLY=${occur%%[[:space:]]*}
+ # Only update occurrences that refer to genuine
+ # recurrences.
+ if [[ $REPLY = [[:digit:]](#c8)T[[:digit:]](#c6) && $line = (#b)(|*[[:space:]\#])(OCCURRENCE[[:space:]]##)${REPLY}[[:space:]]##[^[:space:]]##(|[[:space:]]*) ]]; then
+ # Yes, update in situ
+ line="${match[1]}${match[2]}$occur${match[3]}"
+ else
+ # No, append.
+ [[ $line = *$'\n' ]] || line+=$'\n'
+ line+=" # OCCURRENCE $occur"
+ fi
+ done
+ # The line we're adding now corresponds to the
+ # original event. We'll skip the matching UID
+ # in the file below, however.
+ addline=$line
+ # We need to work out which event is next, so
+ # reparse.
+ if calendar_parse $addline; then
+ parse_new=("${(@kv)reply}")
+ (( my_date = ${parse_new[time]} ))
+ if zstyle -t $context reformat-date; then
+ zstyle -s $context date-format datefmt
+ strftime -s REPLY $datefmt $parse_new[time]
+ addline="$REPLY $parse_new[text1]"
+ fi
+ fi
+ fi
+ fi
fi
done
fi
@@ -107,39 +221,11 @@ fi
print -r -- $addline
(( done = 1 ))
fi
- if [[ -n $parse_old[rpttime] ]]; then
- (( old_recurring = 1 ))
- else
- (( old_recurring = 0 ))
- fi
- if [[ -n $my_uid && $line = ${~uidpat} ]]; then
+ # We've already merged any information on the same UID
+ # with our new text, probably.
+ if [[ $keep_my_uid -eq 0 && -n $my_uid && $line = ${~uidpat} ]]; then
their_uid=${(U)match[1]}
- if [[ $my_uid = $their_uid ]]; then
- # Deal with recurrences, being careful in case there
- # are one-off variants that don't replace recurrences.
- #
- # Bug 1: "calendar" still doesn't know about one-off variants.
- # Bug 2: neither do I; how do we know which occurrence
- # it replaces?
- # Bug 3: the code for calculating recurrences is awful anyway.
-
- if (( old_recurring && new_recurring )); then
- # Replacing a recurrence; there can be only one.
- continue
- elif (( ! new_recurring )); then
- # Not recurring. See if we have previously found
- # a recurrent version
- [[ -n $recurring_uids[$their_uid] ]] && (( old_recurring = 1 ))
- # No, so assume this is a straightforward replacement
- # of a non-recurring event.
- (( ! old_recurring )) && continue
- # It's recurring, but if this is a one-off at the
- # same time as the previous one, replace anyway.
- [[ -z $parse_old[$rpttime] ]] &&
- (( ${parse_new[time]} == ${parse_old[time]} )) &&
- continue
- fi
- fi
+ [[ $my_uid = $their_uid ]] && continue
fi
if [[ $parse_old[time] -eq $my_date && $line = $addline ]]; then
(( done )) && continue # paranoia: shouldn't happen
@@ -157,7 +243,7 @@ New calendar left in $newfile." >&2
fi
fi
else
- print -r -- $line >$newfile
+ (( done )) || print -r -- $addline >$newfile
fi
if (( !rstat )) && ! mv $newfile $calendar; then
diff --git a/Functions/Calendar/calendar_parse b/Functions/Calendar/calendar_parse
index e53e97516..b08622a9d 100644
--- a/Functions/Calendar/calendar_parse
+++ b/Functions/Calendar/calendar_parse
@@ -1,6 +1,6 @@
# Parse the line passed down in the first argument as a calendar entry.
# Sets the values parsed into the associative array reply, consisting of:
-# time The time as an integer (as per EPOCHSECONDS)
+# time The time as an integer (as per EPOCHSECONDS) of the (next) event.
# text1 The text from the the line not including the date/time, but
# including any WARN or RPT text. This is useful for rescheduling
# events, since the keywords need to be retained in this case.
@@ -10,11 +10,16 @@
# difference).
# warnstr Any warning time as the original string (e.g. "5 mins"), not
# including the WARN keyword.
-# rpttime Any repeat/recurrence time (RPT keyword) as an integer, else empty.
-# This is the time of the recurrence itself in EPOCHSECONDS units
-# (as with a warning---not the difference between the events).
+# schedrpttime The next scheduled recurrence (which may be cancelled
+# or rescheduled).
+# rpttime The actual occurrence time: the event may have been rescheduled,
+# in which case this is the time of the actual event (for use in
+# programming warnings etc.) rather than that of the normal
+# recurrence (which is recorded by calendar_add as RECURRENCE).
+#
# rptstr Any repeat/recurrence time as the original string.
-# text2 The text from the line with the date and keywords and values removed.
+# text2 The text from the line with the date and other keywords and
+# values removed.
#
# Note that here an "integer" is a string of digits, not an internally
# formatted integer.
@@ -26,9 +31,14 @@
emulate -L zsh
setopt extendedglob
-local REPLY REPLY2
+local vdatefmt="%Y%m%dT%H%M%S"
+
+local REPLY REPLY2 timefmt occurrence skip try_to_recover before after
local -a match mbegin mend
-integer now
+integer now then replaced firstsched schedrpt
+# Any text matching "OCCURRENCE <timestamp> <disposition>"
+# may occur multiple times. We set occurrences[<timestamp>]=disposition.
+local -A occurrences
autoload -U calendar_scandate
@@ -45,51 +55,122 @@ fi
# REPLY2 to the line with the date and time removed.
calendar_scandate -as $1 || return 1
reply[time]=$(( REPLY ))
+schedrpt=${reply[time]}
reply[text1]=${REPLY2##[[:space:]]#}
+reply[text2]=${reply[text1]}
-reply[text2]=$reply[text1]
-
-integer changed=1
+while true; do
-while (( changed )); do
+ case ${reply[text2]} in
+ # First check for a scheduled repeat time. If we don't find one
+ # we'll use the normal time.
+ ((#b)(*[[:space:]\#])RECURRENCE[[:space:]]##([^[:space:]]##)([[:space:]]*|))
+ strftime -rs then $vdatefmt ${match[2]} ||
+ print "format: $vdatefmt, string ${match[2]}" >&2
+ schedrpt=$then
+ reply[text2]="${match[1]}${match[3]##[ ]#}"
+ ;;
- (( changed = 0 ))
-
- # Look for specific warn time.
- if [[ $reply[text2] = (#b)(|*[[:space:],])WARN[[:space:]](*) ]]; then
+ # Look for specific warn time.
+ ((#b)(|*[[:space:],])WARN[[:space:]](*))
if calendar_scandate -asm -R $reply[time] $match[2]; then
reply[warntime]=$REPLY
reply[warnstr]=${match[2]%%"$REPLY2"}
- reply[text2]="${match[1]}${REPLY2##[[:space:]]#}"
+ # Remove spaces and tabs but not newlines from trailing text,
+ # else the formatting looks funny.
+ reply[text2]="${match[1]}${REPLY2##[ ]#}"
else
# Just remove the keyword for further parsing
- reply[text2]="${match[1]}${match[2]##[[:space:]]#}"
+ reply[text2]="${match[1]}${match[2]##[ ]#}"
fi
- (( changed = 1 ))
- elif [[ $reply[text2] = (#b)(|*[[:space:],])RPT[[:space:]](*) ]]; then
- if calendar_scandate -a -R $reply[time] $match[2]; then
- reply[rpttime]=$REPLY
- reply[rptstr]=${match[2]%%"$REPLY2"}
- reply[text2]="${match[1]}${REPLY2##[[:space:]]#}"
- (( now = EPOCHSECONDS ))
- while (( ${reply[rpttime]} < now )); do
- # let's hope the original appointment wasn't in 44 B.C.
- if calendar_scandate -a -R ${reply[rpttime]} ${reply[rptstr]}; then
- if (( REPLY <= ${reply[rpttime]} )); then
- # pathological case
- break;
- fi
- reply[rpttime]=$REPLY
- fi
- done
+ ;;
+
+ ((#b)(|*[[:space:],])RPT[[:space:]](*))
+ before=${match[1]}
+ after=${match[2]}
+ if [[ $after = CANCELLED(|[[:space:]]*) ]]; then
+ reply[text2]="$before${match[2]##[ ]#}"
+ reply[rptstr]=CANCELLED
+ reply[rpttime]=CANCELLED
+ reply[schedrpttime]=CANCELLED
+ elif calendar_scandate -a -R $schedrpt $after; then
+ # It's possible to calculate a recurrence, however we don't
+ # do that yet. For now just keep the current time as
+ # the recurrence. Hence we ignore REPLY.
+ reply[text2]="$before${REPLY2##[ ]#}"
+ reply[rptstr]=${after%%"$REPLY2"}
+ # Until we find an individual occurrence, the actual time
+ # of the event is the regular one.
+ reply[rpttime]=$schedrpt
else
# Just remove the keyword for further parsing
- reply[text2]="${match[1]}${match[2]##[[:space:]]#}"
+ reply[text2]="$before${after##[[:space:]]#}"
fi
- (( changed = 1 ))
- fi
+ ;;
+
+ ((#b)(|*[[:space:]\#])OCCURRENCE[[:space:]]##([^[:space:]]##)[[:space:]]##([^[:space:]]##)(*))
+ occurrences[${match[2]}]="${match[3]}"
+ # as above
+ reply[text2]="${match[1]}${match[4]##[ ]#}"
+ ;;
+
+ (*)
+ break
+ ;;
+ esac
done
+if [[ -n ${reply[rpttime]} && ${reply[rptstr]} != CANCELLED ]]; then
+ # Recurring event. We need to find out when it recurs.
+ (( now = EPOCHSECONDS ))
+
+ # First find the next recurrence.
+ replaced=0
+ reply[schedrpttime]=$schedrpt
+ if (( schedrpt >= now )); then
+ firstsched=$schedrpt
+ fi
+ while (( ${reply[schedrpttime]} < now || replaced )); do
+ if ! calendar_scandate -a -R ${reply[schedrpttime]} ${reply[rptstr]}; then
+ break
+ fi
+ if (( REPLY <= ${reply[schedrpttime]} )); then
+ # going backwards --- pathological case
+ break;
+ fi
+ reply[schedrpttime]=$REPLY
+ reply[rpttime]=$REPLY
+ if (( ${reply[schedrpttime]} > now && firstsched == 0 )); then
+ firstsched=$REPLY
+ fi
+ replaced=0
+ # do we have an occurrence to compare against?
+ if (( ${#occurrences} )); then
+ strftime -s timefmt $vdatefmt ${reply[schedrpttime]}
+ occurrence=$occurrences[$timefmt]
+ if [[ -n $occurrence ]]; then
+ # Yes, this replaces the scheduled one.
+ replaced=1
+ fi
+ fi
+ done
+ # Now look through occurrences (values only) and see which are (i) still
+ # to happen (ii) early than the current rpttime.
+ for occurrence in $occurrences; do
+ if [[ $occurrence != CANCELLED ]]; then
+ strftime -rs then $vdatefmt $occurrence ||
+ print "format: $vdatefmt, string $occurrence" >&2
+ if (( then > now && then < ${reply[rpttime]} )); then
+ reply[rpttime]=$then
+ fi
+ fi
+ done
+ # Finally, update the scheduled repeat time to the earliest
+ # possible value. This is so that if an occurrence replacement is
+ # cancelled we pick up the regular one. Can this happen? Dunno.
+ reply[schedrpttime]=$firstsched
+fi
+
reply[text2]="${reply[text2]##[[:space:],]#}"
return 0
diff --git a/Functions/Calendar/calendar_scandate b/Functions/Calendar/calendar_scandate
index 4ae2ae606..b3a583705 100644
--- a/Functions/Calendar/calendar_scandate
+++ b/Functions/Calendar/calendar_scandate
@@ -23,6 +23,19 @@
# from 1900 to 2099 inclusive are matched.
# - Although timezones are parsed (complicated formats may not be recognized),
# they are then ignored; no time adjustment is made.
+# - Embedding of times within dates (e.g. "Wed Jun 16 09:30:00 BST 2010")
+# causes horrific problems because of the combination of the many
+# possible date and time formats to match. The approach taken
+# here is to match the time, remove it, and see if the nearby text
+# looks like a date. The problem is that the time matched may not
+# be that associated with the date, in which case the time will be
+# ignored. To minimise this, when the argument "-a" is given to
+# anchor the date/time to the start of the line, we never look
+# beyond a newline. So if any date/time strings in the text
+# are on separate lines the problem is avoided.
+# - If you feel sophisticated enough and wish to avoid any ambiguity,
+# you can use RFC 2445 date/time strings, for example 20100601T170000.
+# These are parsed in one go.
#
# The following give some obvious examples; users finding here
# a format they like and not subject to vagaries of style may skip
@@ -136,7 +149,7 @@
# In this case absolute dates are ignored.
emulate -L zsh
-setopt extendedglob
+setopt extendedglob # xtrace
zmodload -i zsh/datetime || return 1
@@ -145,7 +158,7 @@ zmodload -i zsh/datetime || return 1
# relatively logical dates like 2006/09/19:14:27
# don't allow / before time ! the above
# is not 19 hours 14 mins and 27 seconds after anything.
-local tschars="[-,:[:space:]]"
+local tschars="[-,:[:blank:]]"
# start pattern for time when anchored
local tspat_anchor="(${tschars}#)"
# ... when not anchored
@@ -175,9 +188,10 @@ local repat="(|s)(|${schars}*)"
# We may need some completely different heuristic.
local monthpat="(jan|feb|mar|apr|may|jun|jul|aug|sep|oct|nov|dec)[a-z]#"
integer daysecs=$(( 24 * 60 * 60 ))
+local d="[[:digit:]]"
integer year year2 month month2 day day2 hour minute second then nth wday wday2
-local opt line orig_line mname MATCH MBEGIN MEND tz test
+local opt line orig_line mname MATCH MBEGIN MEND tz test rest_line
local -a match mbegin mend
# Flags that we found a date or a time (maybe a relative time)
integer date_found time_found
@@ -237,7 +251,7 @@ while getopts "aAdmrR:st" opt; do
done
shift $(( OPTIND - 1 ))
-line=$1 orig_line=$1
+line=$1
local dspat dspat_noday tspat
if (( anchor )); then
@@ -250,11 +264,20 @@ if (( anchor )); then
# We'll test later if the time is associated with the date.
tspat=$tspat_noanchor
fi
+ # We can save a huge amount of grief (I've discovered) if when
+ # we're anchored to the start we ignore anything after a newline.
+ # However, don't do this if we're anchored to the end. The
+ # match should fail if there are extra lines in that case.
+ if [[ anchor_end -eq 0 && $line = (#b)([^$'\n']##)($'\n'*) ]]; then
+ line=$match[1]
+ rest_line=$match[2]
+ fi
else
dspat=$dspat_noanchor
dspat_noday=$dspat_noanchor
tspat=$tspat_noanchor
fi
+orig_line=$line
# Look for a time separately; we need colons for this.
# We want to look for the first time to ensure it's associated
@@ -268,6 +291,7 @@ fi
# To use a case statement we'd need to be able to request non-greedy
# matching for a pattern.
local rest
+# HH:MM:SECONDS am/pm with optional decimal seconds
rest=${line#(#ibm)${~tspat}(<0-12>):(<0-59>)[.:]((<0-59>)(.<->|))[[:space:]]#([ap])(|.)[[:space:]]#m(.|[[:space:]]|(#e))}
if [[ $rest != $line ]]; then
hour=$match[2]
@@ -275,7 +299,8 @@ if [[ $rest != $line ]]; then
second=$match[5]
[[ $match[7] = (#i)p ]] && (( hour <= 12 )) && (( hour += 12 ))
time_found=1
-else
+fi
+if (( time_found == 0 )); then
# no seconds, am/pm
rest=${line#(#ibm)${~tspat}(<0-12>):(<0-59>)[[:space:]]#([ap])(|.)[[:space:]]#m(.|[[:space:]]|(#e))}
if [[ $rest != $line ]]; then
@@ -283,37 +308,60 @@ else
minute=$match[3]
[[ $match[4] = (#i)p ]] && (( hour <= 12 )) && (( hour += 12 ))
time_found=1
- else
- # no colon, even, but a.m./p.m. indicator
- rest=${line#(#ibm)${~tspat}(<0-12>)[[:space:]]#([ap])(|.)[[:space:]]#m(.|[[:space:]]|(#e))}
- if [[ $rest != $line ]]; then
- hour=$match[2]
- minute=0
- [[ $match[3] = (#i)p ]] && (( hour <= 12 )) && (( hour += 12 ))
- time_found=1
- else
- # 24 hour clock, with seconds
- rest=${line#(#ibm)${~tspat}(<0-24>):(<0-59>)[.:]((<0-59>)(.<->|))(.|[[:space:]]|(#e))}
- if [[ $rest != $line ]]; then
- hour=$match[2]
- minute=$match[3]
- second=$match[5]
- time_found=1
- else
- rest=${line#(#ibm)${~tspat}(<0-24>):(<0-59>)(.|[[:space:]]|(#e))}
- if [[ $rest != $line ]]; then
- hour=$match[2]
- minute=$match[3]
- time_found=1
- fi
- fi
- fi
+ fi
+fi
+if (( time_found == 0 )); then
+ # no colon, even, but a.m./p.m. indicator
+ rest=${line#(#ibm)${~tspat}(<0-12>)[[:space:]]#([ap])(|.)[[:space:]]#m(.|[[:space:]]|(#e))}
+ if [[ $rest != $line ]]; then
+ hour=$match[2]
+ minute=0
+ [[ $match[3] = (#i)p ]] && (( hour <= 12 )) && (( hour += 12 ))
+ time_found=1
+ fi
+fi
+if (( time_found == 0 )); then
+ # 24 hour clock, with seconds
+ rest=${line#(#ibm)${~tspat}(<0-24>):(<0-59>)[.:]((<0-59>)(.<->|))(.|[[:space:]]|(#e))}
+ if [[ $rest != $line ]]; then
+ hour=$match[2]
+ minute=$match[3]
+ second=$match[5]
+ time_found=1
+ fi
+fi
+if (( time_found == 0 )); then
+ rest=${line#(#ibm)${~tspat}(<0-24>):(<0-59>)(.|[[:space:]]|(#e))}
+ if [[ $rest != $line ]]; then
+ hour=$match[2]
+ minute=$match[3]
+ time_found=1
+ fi
+fi
+if (( time_found == 0 )); then
+ # Combined date and time formats: here we can use an anchor because
+ # we know the complete format.
+ (( anchor )) && tspat=$tspat_anchor
+ # RFC 2445
+ rest=${line#(#ibm)${~tspat}(|\"[^\"]##\":)($~d$~d$~d$~d)($~d$~d)($~d$~d)T($~d$~d)($~d$~d)($~d$~d)([[:space:]]#|(#e))}
+ if [[ $rest != $line ]]; then
+ year=$match[3]
+ month=$match[4]
+ day=$match[5]
+ hour=$match[6]
+ minute=$match[7]
+ second=$match[8]
+ # signal don't need to take account of time in date...
+ time_found=2
+ date_found=1
+ date_start=$mbegin[3]
+ date_end=$mend[-1]
fi
fi
(( hour == 24 )) && hour=0
-if (( time_found )); then
- # time was found
+if (( time_found && ! date_found )); then
+ # time was found; if data also found already, process below.
time_start=$mbegin[2]
time_end=$mend[-1]
# Remove the timespec because it may be in the middle of
@@ -331,7 +379,7 @@ if (( time_found )); then
(( debug )) && print "line after time: $line"
fi
-if (( relative == 0 )); then
+if (( relative == 0 && date_found == 0 )); then
# Date.
case $line in
# Look for YEAR[-/.]MONTH[-/.]DAY
@@ -468,7 +516,7 @@ if (( date_found || (time_ok && time_found) )); then
fi
line=${line[1,$date_start-1]}${line[$date_end+1,-1]}
fi
- if (( time_found )); then
+ if (( time_found == 1 )); then
if (( date_found )); then
# If we found a time, it must be associated with the date,
# or we can't use it. Since we removed the time from the
@@ -540,7 +588,7 @@ if (( date_found || (time_ok && time_found) )); then
"'$orig_line[time_start,time_end]'"
(( date_ok )) && print "Date string: $date_start,$date_end:" \
"'$orig_line[date_start,date_end]'"
- print "Remaining line: '$line'"
+ print "Remaining line: '$line$rest_line'"
fi
fi
fi
@@ -722,11 +770,11 @@ if (( relative )); then
(( reladd += (hour * 60 + minute) * 60 + second ))
typeset -g REPLY
(( REPLY = relative_start + reladd ))
- [[ -n $setvar ]] && typeset -g REPLY2="$line"
+ [[ -n $setvar ]] && typeset -g REPLY2="$line$rest_line"
return 0
fi
return 1
-elif (( ! date_found )); then
+elif (( date_found == 0 )); then
return 1
fi
@@ -748,6 +796,6 @@ fi
strftime -s REPLY -r $fmt $nums
-[[ -n $setvar ]] && typeset -g REPLY2="$line"
+[[ -n $setvar ]] && typeset -g REPLY2="$line$rest_line"
return 0