From mboxrd@z Thu Jan 1 00:00:00 1970 Path: news.gmane.io!.POSTED.blaine.gmane.org!not-for-mail From: Jan Tatarik Newsgroups: gmane.emacs.bugs Subject: bug#39782: 28.0.50; gnus-icalendar does not understand multiple repeating days Date: Fri, 31 Jul 2020 13:39:03 +0200 Message-ID: <87wo2j9anc.fsf@liveintent.com> References: <87r1yihl6l.fsf@everybody.org> <87v9ijr4h9.fsf@gnus.org> <87blkb5w77.fsf@everybody.org> <87h7u2ik65.fsf@gnus.org> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Injection-Info: ciao.gmane.io; posting-host="blaine.gmane.org:116.202.254.214"; logging-data="11079"; mail-complaints-to="usenet@ciao.gmane.io" User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux) Cc: "Mark A. Hershberger" , 39782@debbugs.gnu.org To: Lars Ingebrigtsen Original-X-From: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Fri Jul 31 13:40:12 2020 Return-path: Envelope-to: geb-bug-gnu-emacs@m.gmane-mx.org Original-Received: from lists.gnu.org ([209.51.188.17]) by ciao.gmane.io with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.92) (envelope-from ) id 1k1TOW-0002lF-4o for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 31 Jul 2020 13:40:12 +0200 Original-Received: from localhost ([::1]:43196 helo=lists1p.gnu.org) by lists.gnu.org with esmtp (Exim 4.90_1) (envelope-from ) id 1k1TOU-0002Xb-NV for geb-bug-gnu-emacs@m.gmane-mx.org; Fri, 31 Jul 2020 07:40:10 -0400 Original-Received: from eggs.gnu.org ([2001:470:142:3::10]:39508) by lists.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.90_1) (envelope-from ) id 1k1TOM-0002X9-Ff for bug-gnu-emacs@gnu.org; Fri, 31 Jul 2020 07:40:02 -0400 Original-Received: from debbugs.gnu.org ([209.51.188.43]:54112) by eggs.gnu.org with esmtps (TLS1.2:ECDHE_RSA_AES_128_GCM_SHA256:128) (Exim 4.90_1) (envelope-from ) id 1k1TOM-0002KS-5x for bug-gnu-emacs@gnu.org; Fri, 31 Jul 2020 07:40:02 -0400 Original-Received: from Debian-debbugs by debbugs.gnu.org with local (Exim 4.84_2) (envelope-from ) id 1k1TOM-0007fh-0M for bug-gnu-emacs@gnu.org; Fri, 31 Jul 2020 07:40:02 -0400 X-Loop: help-debbugs@gnu.org Resent-From: Jan Tatarik Original-Sender: "Debbugs-submit" Resent-CC: bug-gnu-emacs@gnu.org Resent-Date: Fri, 31 Jul 2020 11:40:01 +0000 Resent-Message-ID: Resent-Sender: help-debbugs@gnu.org X-GNU-PR-Message: followup 39782 X-GNU-PR-Package: emacs Original-Received: via spool by 39782-submit@debbugs.gnu.org id=B39782.159619556629441 (code B ref 39782); Fri, 31 Jul 2020 11:40:01 +0000 Original-Received: (at 39782) by debbugs.gnu.org; 31 Jul 2020 11:39:26 +0000 Original-Received: from localhost ([127.0.0.1]:37425 helo=debbugs.gnu.org) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1k1TNl-0007el-Ti for submit@debbugs.gnu.org; Fri, 31 Jul 2020 07:39:26 -0400 Original-Received: from mail-wr1-f49.google.com ([209.85.221.49]:35754) by debbugs.gnu.org with esmtp (Exim 4.84_2) (envelope-from ) id 1k1TNi-0007eX-BA for 39782@debbugs.gnu.org; Fri, 31 Jul 2020 07:39:24 -0400 Original-Received: by mail-wr1-f49.google.com with SMTP id f1so27151547wro.2 for <39782@debbugs.gnu.org>; Fri, 31 Jul 2020 04:39:22 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:references:date:in-reply-to:message-id :user-agent:mime-version; bh=BZYjFmgY/7Pt7W9SwW5KqgbhZg25dZ/42MHgZK6+W64=; b=KAR7/meqiJfx6SSvb0lCGEw+V5GrLTC4aHyWWR5jbHt9KOZ1soQQkDlua5jNJuZ0Pm DEWdufWVw8+j9t2UC59YpFypopLz/bXbviiIqlfG0PbS8UosbcZ00mJ4P18bfMgCTCUB r7DsVBRBeURrDTiAi4HepKuxrKRdcef6HqprjP8iCOQpui6zN/SoNkYU4c0GaiiY2dw0 MacE15PqngIMWZCk7FOGIxvRI0DyUzP6sEkg7J0kzq5GWqCMqpT1ylY322/D41DZAiAt QwiXZ30SBl8elfG6dFsaUd3cXN3EGKAELrtGzkwCuTTU0QrkBc2yoS6GS4HSS3vbnvst b3bw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:references:date:in-reply-to :message-id:user-agent:mime-version; bh=BZYjFmgY/7Pt7W9SwW5KqgbhZg25dZ/42MHgZK6+W64=; b=L3L9k0/nHXxg9ASOOA65xMdyszCZTxwNdOpUbkY6KSiE4ulYolDXJPfMpEr90cUMsz BaZ2e24/ESsPUDqvhI6fCOyEWn8jd8VHsJGzFpLxNm9oHnwCHSsES4OE8ccjWop22nKr gRVEyjJ6G+Q/zBnHzUoD3UDDWBxXx2Id3NAlHgxvkeSQhfNnPmPAseqtKpjR1oah/VLr QIe9T7ApcgH/xADGewapPfThjc3gmsPwwALAsL9QURRcrCu8zmIoUlER+RIDWotNmqh4 8mLWacmnLrqHwMEfHJFbDrqDdCeg8S6dNz24xLlT0Qdi4iXSuEYn/xs3rmqu68yszRVf K+HA== X-Gm-Message-State: AOAM533oL3h1jmFiYsps8wfAsyuFQWa4rsB5MsAsF/1BCID3drNTlj8V U2g4rbDDmPqoGc8vSshBDVfKW2Y+Bh4= X-Google-Smtp-Source: ABdhPJwMKBKVGYnAZVnjumIexVkq+QJbuQEHMgbQ58tXIXFCszlDAUJQG/0jMvCJbNkSt85oHf1RjQ== X-Received: by 2002:adf:8486:: with SMTP id 6mr3176245wrg.109.1596195556210; Fri, 31 Jul 2020 04:39:16 -0700 (PDT) Original-Received: from jan-laptop (89.141.250.60.dyn.user.ono.com. [89.141.250.60]) by smtp.gmail.com with ESMTPSA id i14sm16881552wrc.19.2020.07.31.04.39.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Fri, 31 Jul 2020 04:39:15 -0700 (PDT) In-Reply-To: <87h7u2ik65.fsf@gnus.org> (Lars Ingebrigtsen's message of "Mon, 20 Jul 2020 11:56:50 +0200") X-BeenThere: debbugs-submit@debbugs.gnu.org X-Mailman-Version: 2.1.18 Precedence: list X-BeenThere: bug-gnu-emacs@gnu.org List-Id: "Bug reports for GNU Emacs, the Swiss army knife of text editors" List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: bug-gnu-emacs-bounces+geb-bug-gnu-emacs=m.gmane-mx.org@gnu.org Original-Sender: "bug-gnu-emacs" Xref: news.gmane.io gmane.emacs.bugs:183725 Archived-At: --=-=-= Content-Type: text/plain Here's a quick-and-dirty fix (am on vacation now). See if it works for you. --=-=-= Content-Type: text/x-diff Content-Disposition: inline; filename=bug-39782.patch Content-Description: gnus-icalendar with simple BYDAYS support diff --git a/lisp/gnus/gnus-icalendar.el b/lisp/gnus/gnus-icalendar.el index 305e17fd8f..2860c40581 100644 --- a/lisp/gnus/gnus-icalendar.el +++ b/lisp/gnus/gnus-icalendar.el @@ -138,6 +138,22 @@ gnus-icalendar-event-reply (or (match-string 1 rrule) default-interval))) +(cl-defmethod gnus-icalendar-event:recurring-days ((event gnus-icalendar-event)) + "Return, when available, the week day numbers on which the EVENT recurs." + (let ((rrule (gnus-icalendar-event:recur event)) + (weekday-map '(("SU" . 0) + ("MO" . 1) + ("TU" . 2) + ("WE" . 3) + ("TH" . 4) + ("FR" . 5) + ("SA" . 6)))) + (when (string-match "BYDAY=\\([^;]+\\)" rrule) + (let ((bydays (split-string (match-string 1 rrule) ","))) + (seq-map + (lambda (x) (cdr (assoc x weekday-map))) + (seq-filter (lambda (x) (string-match "^[A-Z]\\{2\\}$" x)) bydays)))))) + (cl-defmethod gnus-icalendar-event:start ((event gnus-icalendar-event)) (format-time-string "%Y-%m-%d %H:%M" (gnus-icalendar-event:start-time event))) @@ -400,23 +416,28 @@ gnus-icalendar-org-enabled-p (when org-freq (format "+%s%s" (gnus-icalendar-event:recurring-interval event) org-freq))))) -(cl-defmethod gnus-icalendar-event:org-timestamp ((event gnus-icalendar-event)) - "Build `org-mode' timestamp from EVENT start/end dates and recurrence info." - (let* ((start (gnus-icalendar-event:start-time event)) - (end (gnus-icalendar-event:end-time event)) - (start-date (format-time-string "%Y-%m-%d" start)) +(defun find-day (start-date end-date day) + (let ((time-1-day 86400)) + (if (= (decoded-time-weekday (decode-time start-date)) + day) + (list start-date end-date) + (find-day (time-add start-date time-1-day) + (time-add end-date time-1-day) + day)))) + +(defun gnus-icalendar-event--org-timestamp (start end org-repeat) + (let* ((start-date (format-time-string "%Y-%m-%d" start)) (start-time (format-time-string "%H:%M" start)) (start-at-midnight (string= start-time "00:00")) (end-date (format-time-string "%Y-%m-%d" end)) (end-time (format-time-string "%H:%M" end)) (end-at-midnight (string= end-time "00:00")) (start-end-date-diff - (time-to-number-of-days (time-subtract - (org-time-string-to-time end-date) - (org-time-string-to-time start-date)))) - (org-repeat (gnus-icalendar-event:org-repeat event)) + (time-to-number-of-days (time-subtract + (org-time-string-to-time end-date) + (org-time-string-to-time start-date)))) (repeat (if org-repeat (concat " " org-repeat) "")) - (time-1-day 86400)) + (time-1-day 86400)) ;; NOTE: special care is needed with appointments ending at midnight ;; (typically all-day events): the end time has to be changed to 23:59 to @@ -445,7 +466,25 @@ gnus-icalendar-org-enabled-p ;; A .:. - A .:. -> A .:.-.:. ;; A .:. - B .:. ((zerop start-end-date-diff) (format "<%s %s-%s%s>" start-date start-time end-time repeat)) - (t (format "<%s %s>--<%s %s>" start-date start-time end-date end-time))))) + (t (format "<%s %s>--<%s %s>" start-date start-time end-date end-time)))) + ) + +(cl-defmethod gnus-icalendar-event:org-timestamp ((event gnus-icalendar-event)) + "Build `org-mode' timestamp from EVENT start/end dates and recurrence info." + ;; if org-repeat +1d or +1w and byday: generate one timestamp per + ;; byday, starting at start-date. Change +1d to +7d. + (let ((start (gnus-icalendar-event:start-time event)) + (end (gnus-icalendar-event:end-time event)) + (org-repeat (gnus-icalendar-event:org-repeat event)) + (recurring-days (gnus-icalendar-event:recurring-days event))) + (if (and (or (string= org-repeat "+1d") + (string= org-repeat "+1w")) + recurring-days) + (let ((repeat "+1w") + (dates (seq-sort-by 'car 'time-less-p (seq-map (lambda (x) (find-day start end x)) recurring-days)))) + (mapconcat (lambda (x) (gnus-icalendar-event--org-timestamp (car x) (cadr x) repeat)) dates "\n")) + (gnus-icalendar-event--org-timestamp start end org-repeat))) +) (defun gnus-icalendar--format-summary-line (summary &optional location) (if location diff --git a/test/lisp/gnus/gnus-icalendar-tests.el b/test/lisp/gnus/gnus-icalendar-tests.el new file mode 100644 index 0000000000..ca686da72c --- /dev/null +++ b/test/lisp/gnus/gnus-icalendar-tests.el @@ -0,0 +1,229 @@ +;;; gnus-icalendar-tests.el --- tests -*- lexical-binding: t; -*- + +;; Copyright (C) 2020 Jan Tatarik + +;; Author: Jan Tatarik +;; Keywords: + +;; This program is free software; you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more details. + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see . + +;;; Commentary: + +;; + +;;; Code: + +(require 'ert) +(require 'gnus-icalendar) + + +(defun gnus-icalendar-tests--get-ical-event (ical-string &optional participant) + "Return gnus-icalendar event for ICAL-STRING." + (let (event) + (with-temp-buffer + (insert ical-string) + (setq event (gnus-icalendar-event-from-buffer (buffer-name) participant))) + event)) + +(defun icalendar-tests--get-ical-event (ical-string) + "Return iCalendar event for ICAL-STRING." + (save-excursion + (with-temp-buffer + (insert ical-string) + (goto-char (point-min)) + (car (icalendar--read-element nil nil))))) + +(ert-deftest gnus-icalendar-parse () + "test" + (let ((event (gnus-icalendar-tests--get-ical-event " +BEGIN:VCALENDAR +PRODID:-//Google Inc//Google Calendar 70.9054//EN +VERSION:2.0 +CALSCALE:GREGORIAN +METHOD:REQUEST +BEGIN:VTIMEZONE +TZID:America/New_York +X-LIC-LOCATION:America/New_York +BEGIN:DAYLIGHT +TZOFFSETFROM:-0500 +TZOFFSETTO:-0400 +TZNAME:EDT +DTSTART:19700308T020000 +RRULE:FREQ=YEARLY;BYMONTH=3;BYDAY=2SU +END:DAYLIGHT +BEGIN:STANDARD +TZOFFSETFROM:-0400 +TZOFFSETTO:-0500 +TZNAME:EST +DTSTART:19701101T020000 +RRULE:FREQ=YEARLY;BYMONTH=11;BYDAY=1SU +END:STANDARD +END:VTIMEZONE +BEGIN:VEVENT +DTSTART;TZID=America/New_York:20201208T090000 +DTEND;TZID=America/New_York:20201208T100000 +DTSTAMP:20200728T182853Z +ORGANIZER;CN=Company Events:mailto:liveintent.com_3bm6fh805bme9uoeliqcle1sa + g@group.calendar.google.com +UID:iipdt88slddpeu7hheuu09sfmd@google.com +X-MICROSOFT-CDO-OWNERAPPTID:-362490173 +RECURRENCE-ID;TZID=America/New_York:20201208T091500 +CREATED:20200309T134939Z +DESCRIPTION:In this meeting\, we will cover topics from product and enginee + ring presentations and demos to new hire announcements to watching the late +LAST-MODIFIED:20200728T182852Z +LOCATION:New York-22-Town Hall Space (250) [Chrome Box] +SEQUENCE:4 +STATUS:CONFIRMED +SUMMARY:Townhall | All Company Meeting +TRANSP:OPAQUE +END:VEVENT +END:VCALENDAR +"))) + + (should (eq (eieio-object-class event) 'gnus-icalendar-event-request)) + (should (not (gnus-icalendar-event:recurring-p event))) + (should (string= (gnus-icalendar-event:start event) "2020-12-08 15:00")) + (with-slots (organizer summary description location end-time uid rsvp participation-type) event + (should (string= organizer "liveintent.com_3bm6fh805bme9uoeliqcle1sag@group.calendar.google.com")) + (should (string= summary "Townhall | All Company Meeting")) + (should (string= description "In this meeting\, we will cover topics from product and engineering presentations and demos to new hire announcements to watching the late")) + (should (string= location "New York-22-Town Hall Space (250) [Chrome Box]")) + (should (string= (format-time-string "%Y-%m-%d %H:%M" end-time) "2020-12-08 16:00")) + (should (string= uid "iipdt88slddpeu7hheuu09sfmd@google.com")) + (should (not rsvp)) +(should (eq participation-type 'non-participant))))) + +(ert-deftest gnus-icalendary-byday () + "" + (let ((event (gnus-icalendar-tests--get-ical-event " +BEGIN:VCALENDAR +PRODID:Zimbra-Calendar-Provider +VERSION:2.0 +METHOD:REQUEST +BEGIN:VTIMEZONE +TZID:America/New_York +BEGIN:STANDARD +DTSTART:16010101T020000 +TZOFFSETTO:-0500 +TZOFFSETFROM:-0400 +RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU +TZNAME:EST +END:STANDARD +BEGIN:DAYLIGHT +DTSTART:16010101T020000 +TZOFFSETTO:-0400 +TZOFFSETFROM:-0500 +RRULE:FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU +TZNAME:EDT +END:DAYLIGHT +END:VTIMEZONE +BEGIN:VEVENT +UID:903a5415-9067-4f63-b499-1b6205f49c88 +RRULE:FREQ=DAILY;UNTIL=20200825T035959Z;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR +SUMMARY:appointment every weekday\, start jul 24\, 2020\, end aug 24\, 2020 +ATTENDEE;CN=Mark Hershberger;ROLE=REQ-PARTICIPANT;PARTSTAT=NEEDS-ACTION;RSVP + =TRUE:mailto:hexmode gmail.com +ORGANIZER;CN=Mark A. Hershberger:mailto:mah nichework.com +DTSTART;TZID=\"America/New_York\":20200724T090000 +DTEND;TZID=\"America/New_York\":20200724T093000 +STATUS:CONFIRMED +CLASS:PUBLIC +X-MICROSOFT-CDO-INTENDEDSTATUS:BUSY +TRANSP:OPAQUE +LAST-MODIFIED:20200719T150815Z +DTSTAMP:20200719T150815Z +SEQUENCE:0 +DESCRIPTION:The following is a new meeting request: +BEGIN:VALARM +ACTION:DISPLAY +TRIGGER;RELATED=START:-PT5M +DESCRIPTION:Reminder +END:VALARM +END:VEVENT +END:VCALENDAR" (list "Mark Hershberger")))) + + (should (eq (eieio-object-class event) 'gnus-icalendar-event-request)) + (should (gnus-icalendar-event:recurring-p event)) + (should (string= (gnus-icalendar-event:recurring-interval event) "1")) + (should (string= (gnus-icalendar-event:start event) "2020-07-24 15:00")) + (with-slots (organizer summary description location end-time uid rsvp participation-type) event + (should (string= organizer "mah nichework.com")) + (should (string= summary "appointment every weekday\, start jul 24\, 2020\, end aug 24\, 2020")) + (should (string= description "The following is a new meeting request:")) + (should (null location)) + (should (string= (format-time-string "%Y-%m-%d %H:%M" end-time) "2020-07-24 15:30")) + (should (string= uid "903a5415-9067-4f63-b499-1b6205f49c88")) + (should rsvp) + (should (eq participation-type 'required))) + (should (equal (gnus-icalendar-event:recurring-days event) '(1 2 3 4 5))) + (should (string= (gnus-icalendar-event:org-timestamp event) "<2020-07-24 15:00-15:30 +1w> +<2020-07-27 15:00-15:30 +1w> +<2020-07-28 15:00-15:30 +1w> +<2020-07-29 15:00-15:30 +1w> +<2020-07-30 15:00-15:30 +1w>")) + )) + + +;; (VCALENDAR nil +;; ((PRODID nil "Zimbra-Calendar-Provider") +;; (VERSION nil "2.0") +;; (METHOD nil "REQUEST")) +;; ((VTIMEZONE nil +;; ((TZID nil "America/New_York")) +;; ((STANDARD nil +;; ((DTSTART nil "16010101T020000") +;; (TZOFFSETTO nil "-0500") +;; (TZOFFSETFROM nil "-0400") +;; (RRULE nil "FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=11;BYDAY=1SU") +;; (TZNAME nil "EST")) +;; nil) +;; (DAYLIGHT nil +;; ((DTSTART nil "16010101T020000") +;; (TZOFFSETTO nil "-0400") +;; (TZOFFSETFROM nil "-0500") +;; (RRULE nil "FREQ=YEARLY;WKST=MO;INTERVAL=1;BYMONTH=3;BYDAY=2SU") +;; (TZNAME nil "EDT")) +;; nil))) +;; (VEVENT nil +;; ((UID nil "903a5415-9067-4f63-b499-1b6205f49c88") +;; (RRULE nil "FREQ=DAILY;UNTIL=20200825T035959Z;INTERVAL=1;BYDAY=MO,TU,WE,TH,FR") +;; (SUMMARY nil "appointment every weekday, start jul 24, 2020, end aug 24, 2020") +;; (ATTENDEE +;; (CN "Mark Hershberger" ROLE "REQ-PARTICIPANT" PARTSTAT "NEEDS-ACTION" CN "Mark A. Hershberger") +;; "mailto:mah nichework.com") +;; (DTSTART +;; (TZID "America/New_York") +;; "20200724T090000") +;; (DTEND +;; (TZID "America/New_York") +;; "20200724T093000") +;; (STATUS nil "CONFIRMED") +;; (CLASS nil "PUBLIC") +;; (X-MICROSOFT-CDO-INTENDEDSTATUS nil "BUSY") +;; (TRANSP nil "OPAQUE") +;; (LAST-MODIFIED nil "20200719T150815Z") +;; (DTSTAMP nil "20200719T150815Z") +;; (SEQUENCE nil "0") +;; (DESCRIPTION nil "The following is a new meeting request:")) +;; ((VALARM nil +;; ((ACTION nil "DISPLAY") +;; (TRIGGER +;; (RELATED "START") +;; "-PT5M") +;; (DESCRIPTION nil "Reminder")) +;; nil))))) + +(provide 'gnus-icalendar-tests) +;;; gnus-icalendar-tests.el ends here --=-=-=--