diff options
author | Peter Stephenson <pws@users.sourceforge.net> | 2010-06-14 13:01:41 +0000 |
---|---|---|
committer | Peter Stephenson <pws@users.sourceforge.net> | 2010-06-14 13:01:41 +0000 |
commit | 14dde084755a8b15004d59bb6be5cc7a3726a8bf (patch) | |
tree | 067f4ebff5e399fb560c710b798a4e3421f771ea /Functions | |
parent | 4c1a3a89f0ade5be2330ce688cd3c3c649667f9a (diff) | |
download | zsh-14dde084755a8b15004d59bb6be5cc7a3726a8bf.tar.gz zsh-14dde084755a8b15004d59bb6be5cc7a3726a8bf.tar.xz zsh-14dde084755a8b15004d59bb6be5cc7a3726a8bf.zip |
28038: improved handling of recurring events in calendar system
Diffstat (limited to 'Functions')
-rw-r--r-- | Functions/Calendar/calendar | 6 | ||||
-rw-r--r-- | Functions/Calendar/calendar_add | 178 | ||||
-rw-r--r-- | Functions/Calendar/calendar_parse | 155 | ||||
-rw-r--r-- | Functions/Calendar/calendar_scandate | 124 |
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 |