From e42cfddef3e599add41ee2183dc97fd4ae880a4a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szabolcs=20Szil=C3=A1gyi?= Date: Fri, 15 Sep 2023 17:57:02 +0200 Subject: [PATCH 1/2] Add support for weekly repeating events This adds support for events that repeats on chosen days of the week. E.g. repeating every Tuesday and Monday. The solution isn't the most elegant, as we parse the date again from a previously parsed, then formatted source (the `date` variable), but for a more elegant solution we would need to refactor other methods as well. For the repeating the following format is used (note the dates beneth): ``` * Pretend to do some sport :PROPERTIES: :ID: a3f62a3f-5d6f-4956-9adc-a94b73438b34 :LOCATION: Local Gym :STATUS: CONFIRMED :ATTENDING: ATTENDING :ATTENDEES: :END: :LOGBOOK: CLOCK: [2023-07-15 Sat 10:15]--[2023-07-15 Sat 10:30] => 0:00 :END <2023-07-17 Mon 10:15-10:30 +1w> <2023-07-18 Tue 10:15-10:30 +1w> <2023-07-19 Wed 10:15-10:30 +1w> <2023-07-20 Thu 10:15-10:30 +1w> ``` Other solution could be to use a lisp expression to setup, but for me that is too obsucre, hard to read at glance. E.g.: ``` ** Daily meeting <%%(memq (calendar-day-of-week date) '(1 2 3 4 5))> ``` source: https://orgmode.org/worg/org-faq.html#orga9d21b4 --- ical2org.awk | 68 +++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 3 deletions(-) diff --git a/ical2org.awk b/ical2org.awk index 234ccdc..21fea50 100755 --- a/ical2org.awk +++ b/ical2org.awk @@ -116,6 +116,16 @@ BEGIN { tz_offsets["America/Los_Angeles"] = 0 tz_offsets["America/Chicago"] = 2 + # BYDAY - day definitions + by_day_hash["MO"] = 1 + by_day_hash["TU"] = 2 + by_day_hash["WE"] = 3 + by_day_hash["TH"] = 4 + by_day_hash["FR"] = 5 + by_day_hash["SA"] = 6 + by_day_hash["SU"] = 0 + + ### end config section # use a colon to separate the type of data line from the actual contents @@ -179,6 +189,9 @@ BEGIN { attending = attending_types["UNSET"]; # http://unix.stackexchange.com/a/147958/129055 intfreq = "" # the interval and frequency for repeating org timestamps + # BYDAY is used for events weekly on certain days. E.g. every Monday and + # Tuesday + by_day = "" lasttimestamp = -1; location = "" rrend = "" @@ -312,8 +325,9 @@ BEGIN { # repetition rule /^RRULE:FREQ=(DAILY|WEEKLY|MONTHLY|YEARLY)/ { - # TODO: handle BYDAY values for events that repeat weekly for multiple days - # (e.g. a "Gym" event) + # handle BYDAY values for events that repeat weekly for multiple days + # (e.g. a "Gym" event - BYDAY=MO,WE,TH,SA) + by_day = $2 ~ /BYDAY=/ ? gensub(/.*BYDAY=(.*);.*/, "\\1", 1, $2) : "" # get the d, w, m or y value freq = tolower(gensub(/.*FREQ=(.).*/, "\\1", 1, $0)) @@ -442,7 +456,7 @@ BEGIN { print " :END" } if (!condense) - print "<" date ">" + print_active_dates() print "" if(length(entry)>1) @@ -457,6 +471,36 @@ BEGIN { } +# funtion will print the active dates of the event, if its repeating on +# weekdays, then each weekday occurance gets its own line +function print_active_dates(_dates, _sorted_dates) +{ + if (by_day == "") { + _dates[1] = date + } else { + n = split(by_day, repeat_days, ",") + starting_date_day = by_day_hash[toupper(gensub(".*([A-z]{2})[A-z].*", "\\1", 1, date))] + for (i = 1; i <= n; i++) { + current_repeate_value = repeat_days[i] + repeat_day = by_day_hash[current_repeate_value] + days_to_add = repeat_day - starting_date_day + if (repeat_day < starting_date_day) { + # this is needed if for example the first event is on Tuesday, + # but it should repeat on Monday as well. Then we would like to + # start the Monday repeat from the next closes Monday. + days_to_add += 7 + } + new_date = shift_date_with_days(sysdate_from_org_date(date), days_to_add) + tail_end = substr(date, length(new_date) + 1) + _dates[i] = new_date tail_end + } + } + asort(_dates, _sorted_dates) + for (x in _sorted_dates) { + print "<" _sorted_dates[x] ">" + } +} + # Join keys in an array, return a string function join_keys(input) { @@ -596,6 +640,24 @@ function datestring(input, isenddate) return strftime("%Y-%m-%d %a", stamp); } +# function will convert the given org-mode formatted date string to unix time +# @param date_str org date format e.g.: 2023-08-15 Tue 09:45 +function sysdate_from_org_date(date_str) +{ + formatted_date_str = gensub("^([0-9]{4})-0?([0-9]{1,2})-0?([0-9]{1,2}) [A-z]{3} 0?([0-9]{1,2}):([0-9]{2}).*", "\\1 \\2 \\3 \\4 \\5 0", 1, date_str) + return mktime(formatted_date_str) +} + +# function will return an org-mode formatted string with the given amount of +# days shifted +# @param date_to_shift unix time +# @param days amount of days we should shift +function shift_date_with_days(date_to_shift, days) +{ + return strftime("%Y-%m-%d %a %H:%M", date_to_shift + days * 24 * 60 * 60) +} + + # Add the current attendee's response to a set, so we can list who's going # and who's declined function add_attendee(attendee) From 949e360c778e0af1073901f78401a8eac806bf8a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Szabolcs=20Szil=C3=A1gyi?= Date: Sat, 16 Sep 2023 14:21:10 +0200 Subject: [PATCH 2/2] Correct weekday repeat regexp The previous solution wouldn't reconigize, if the event is repeating on a single day. With the new pattern the grouping goes like follows: - two uppercase letter - followed by an optional coma - one or more repeats of the above two - the above group followed by an optional semicolon --- ical2org.awk | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ical2org.awk b/ical2org.awk index 21fea50..e4c3993 100755 --- a/ical2org.awk +++ b/ical2org.awk @@ -327,7 +327,7 @@ BEGIN { /^RRULE:FREQ=(DAILY|WEEKLY|MONTHLY|YEARLY)/ { # handle BYDAY values for events that repeat weekly for multiple days # (e.g. a "Gym" event - BYDAY=MO,WE,TH,SA) - by_day = $2 ~ /BYDAY=/ ? gensub(/.*BYDAY=(.*);.*/, "\\1", 1, $2) : "" + by_day = $2 ~ /BYDAY=/ ? gensub(/.*BYDAY=(([A-Z]{2},?)+);?.*/, "\\1", 1, $2) : "" # get the d, w, m or y value freq = tolower(gensub(/.*FREQ=(.).*/, "\\1", 1, $0))