Add 2010 to copyright years.
[bpt/emacs.git] / lisp / calendar / icalendar.el
index 62cc247..624997a 100644 (file)
@@ -1,6 +1,6 @@
 ;;; icalendar.el --- iCalendar implementation -*-coding: utf-8 -*-
 
-;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008
+;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009, 2010
 ;;   Free Software Foundation, Inc.
 
 ;; Author:         Ulf Jasper <ulf.jasper@web.de>
 
 ;; This file is part of GNU Emacs.
 
-;; GNU Emacs is free software; you can redistribute it and/or modify
+;; GNU Emacs 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, or (at your option)
-;; any later version.
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
 
 ;; GNU Emacs is distributed in the hope that it will be useful,
 ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -21,9 +21,7 @@
 ;; GNU General Public License for more details.
 
 ;; You should have received a copy of the GNU General Public License
-;; along with GNU Emacs; see the file COPYING.  If not, write to the
-;; Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
-;; Boston, MA 02110-1301, USA.
+;; along with GNU Emacs.  If not, see <http://www.gnu.org/licenses/>.
 
 ;;; Commentary:
 
 
 ;;; Code:
 
-(defconst icalendar-version "0.17"
+(defconst icalendar-version "0.19"
   "Version number of icalendar.el.")
 
 ;; ======================================================================
@@ -195,6 +193,41 @@ the class."
   :type 'string
   :group 'icalendar)
 
+(defcustom icalendar-recurring-start-year
+  2005
+  "Start year for recurring events.
+Some calendar browsers only propagate recurring events for
+several years beyond the start time.  Set this string to a year
+just before the start of your personal calendar."
+  :type 'integer
+  :group 'icalendar)
+
+(defcustom icalendar-export-hidden-diary-entries
+  t
+  "Determines whether hidden diary entries are exported.
+If non-nil hidden diary entries (starting with `&') get exported,
+if nil they are ignored."
+  :type 'boolean
+  :group 'icalendar)
+
+(defcustom icalendar-uid-format
+  "emacs%t%c"
+  "Format of unique ID code (UID) for each iCalendar object.  
+The following specifiers are available: 
+%c COUNTER, an integer value that is increased each time a uid is
+   generated. This may be necessary for systems which do not
+   provide time-resolution finer than a second.
+%h HASH, a hash value of the diary entry,
+%s DTSTART, the start date (excluding time) of the diary entry,
+%t TIMESTAMP, a unique creation timestamp,
+%u USERNAME, the user-login-name.
+
+For example, a value of \"%s_%h@mydomain.com\" will generate a
+UID code for each entry composed of the time of the event, a hash
+code for the event, and your personal domain name."
+  :type 'string
+  :group 'icalendar)
+
 (defvar icalendar-debug nil
   "Enable icalendar debug messages.")
 
@@ -421,7 +454,7 @@ The strings are suitable for assembling into a TZ variable."
            (cons
             (concat
              ;; Fake a name.
-             (if dst-p "(DST?)" "(STD?)")
+             (if dst-p "DST" "STD")
              ;; For TZ, OFFSET is added to the local time.  So,
              ;; invert the values.
              (if (eq (aref offset 0) ?-) "+" "-")
@@ -433,6 +466,10 @@ The strings are suitable for assembling into a TZ variable."
                    (week (if (eq day -1)
                              byday
                            (substring byday 0 -2))))
+               ;; "Translate" the icalendar way to specify the last
+               ;; (sun|mon|...)day in month to the tzset way.
+               (if (string= week "-1")  ; last day as icalendar calls it
+                   (setq week "5"))     ; last day as tzset calls it
               (concat "M" bymonth "." week "." (if (eq day -1) "0"
                                                  (int-to-string day))
                       ;; Start time.
@@ -606,11 +643,11 @@ valid (year > 1900 or something)."
                 ;;(or (nth 6 time1) (nth 6 time2)) ;; FIXME?
                 )))
 
-(defun icalendar--datetime-to-noneuropean-date (datetime &optional separator)
-  "Convert the decoded DATETIME to non-european-style format.
+(defun icalendar--datetime-to-american-date (datetime &optional separator)
+  "Convert the decoded DATETIME to American-style format.
 Optional argument SEPARATOR gives the separator between month,
 day, and year.  If nil a blank character is used as separator.
-Non-European format: \"month day year\"."
+American format: \"month day year\"."
   (if datetime
       (format "%d%s%d%s%d" (nth 4 datetime) ;month
               (or separator " ")
@@ -620,6 +657,9 @@ Non-European format: \"month day year\"."
     ;; datetime == nil
     nil))
 
+(define-obsolete-function-alias 'icalendar--datetime-to-noneuropean-date
+  'icalendar--datetime-to-american-date "icalendar 0.19")
+
 (defun icalendar--datetime-to-european-date (datetime &optional separator)
   "Convert the decoded DATETIME to European format.
 Optional argument SEPARATOR gives the separator between month,
@@ -635,15 +675,39 @@ FIXME"
     ;; datetime == nil
     nil))
 
+(defun icalendar--datetime-to-iso-date (datetime &optional separator)
+  "Convert the decoded DATETIME to ISO format.
+Optional argument SEPARATOR gives the separator between month,
+day, and year.  If nil a blank character is used as separator.
+ISO format: (year month day)."
+  (if datetime
+      (format "%d%s%d%s%d" (nth 5 datetime) ;year
+              (or separator " ")
+              (nth 4 datetime)            ;month
+              (or separator " ")
+              (nth 3 datetime))           ;day
+    ;; datetime == nil
+    nil))
+
+(defun icalendar--date-style ()
+  "Return current calendar date style.
+Convenience function to handle transition from old
+`european-calendar-style' to new `calendar-date-style'."
+  (if (boundp 'calendar-date-style)
+      calendar-date-style
+    (if (with-no-warnings european-calendar-style)
+        'european
+      'american)))
+
 (defun icalendar--datetime-to-diary-date (datetime &optional separator)
   "Convert the decoded DATETIME to diary format.
 Optional argument SEPARATOR gives the separator between month,
 day, and year.  If nil a blank character is used as separator.
-Call icalendar--datetime-to-(non)-european-date according to
-value of `european-calendar-style'."
-  (if european-calendar-style
-      (icalendar--datetime-to-european-date datetime separator)
-    (icalendar--datetime-to-noneuropean-date datetime separator)))
+Call icalendar--datetime-to-*-date according to the current
+calendar date style."
+  (funcall (intern-soft (format "icalendar--datetime-to-%s-date"
+                                (icalendar--date-style)))
+           datetime separator))
 
 (defun icalendar--datetime-to-colontime (datetime)
   "Extract the time part of a decoded DATETIME into 24-hour format.
@@ -709,10 +773,24 @@ If DAY-SHIFT is non-nil, the result is shifted by DAY-SHIFT days."
   "Convert diary-style DATESTRING to iso-style date.
 If DAY-SHIFT is non-nil, the result is shifted by DAY-SHIFT days
 -- DAY-SHIFT must be either nil or an integer.  This function
-takes care of european-style."
+tries to figure the date style from DATESTRING itself.  If that
+is not possible it uses the current calendar date style."
   (let ((day -1) month year)
     (save-match-data
-      (cond ( ;; numeric date
+      (cond ( ;; iso-style numeric date
+             (string-match (concat "\\s-*"
+                                   "\\([0-9]\\{4\\}\\)[ \t/]\\s-*"
+                                   "0?\\([1-9][0-9]?\\)[ \t/]\\s-*"
+                                   "0?\\([1-9][0-9]?\\)")
+                           datestring)
+             (setq year (read (substring datestring (match-beginning 1)
+                                         (match-end 1))))
+             (setq month (read (substring datestring (match-beginning 2)
+                                          (match-end 2))))
+             (setq day (read (substring datestring (match-beginning 3)
+                                        (match-end 3)))))
+            ( ;; non-iso numeric date -- must rely on configured
+              ;; calendar style
              (string-match (concat "\\s-*"
                                    "0?\\([1-9][0-9]?\\)[ \t/]\\s-*"
                                    "0?\\([1-9][0-9]?\\),?[ \t/]\\s-*"
@@ -724,11 +802,24 @@ takes care of european-style."
                                           (match-end 2))))
              (setq year (read (substring datestring (match-beginning 3)
                                          (match-end 3))))
-             (unless european-calendar-style
-               (let ((x month))
-                 (setq month day)
-                 (setq day x))))
-            ( ;; date contains month names -- european-style
+             (if (eq (icalendar--date-style) 'american)
+                 (let ((x month))
+                   (setq month day)
+                   (setq day x))))
+            ( ;; date contains month names -- iso style
+             (string-match (concat "\\s-*"
+                                   "\\([0-9]\\{4\\}\\)[ \t/]\\s-*"
+                                   "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*"
+                                   "0?\\([123]?[0-9]\\)")
+                           datestring)
+             (setq year (read (substring datestring (match-beginning 1)
+                                        (match-end 1))))
+             (setq month (icalendar--get-month-number
+                          (substring datestring (match-beginning 2)
+                                     (match-end 2))))
+             (setq day (read (substring datestring (match-beginning 3)
+                                        (match-end 3)))))
+            ( ;; date contains month names -- european style
              (string-match (concat "\\s-*"
                                    "0?\\([123]?[0-9]\\)[ \t/]\\s-*"
                                    "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*"
@@ -741,7 +832,7 @@ takes care of european-style."
                                      (match-end 2))))
              (setq year (read (substring datestring (match-beginning 3)
                                          (match-end 3)))))
-            ( ;; date contains month names -- non-european-style
+            ( ;; date contains month names -- american style
              (string-match (concat "\\s-*"
                                    "\\([A-Za-z][^ ]+\\)[ \t/]\\s-*"
                                    "0?\\([123]?[0-9]\\),?[ \t/]\\s-*"
@@ -761,6 +852,7 @@ takes care of european-style."
                     (+ (calendar-absolute-from-gregorian (list month day
                                                                year))
                        (or day-shift 0)))))
+          (icalendar--dmsg (format "%04d%02d%02d" (nth 2 mdy) (nth 0 mdy) (nth 1 mdy)))
           (format "%04d%02d%02d" (nth 2 mdy) (nth 0 mdy) (nth 1 mdy)))
       nil)))
 
@@ -771,8 +863,12 @@ would be \"pm\"."
   (if timestring
       (let ((starttimenum (read (icalendar--rris ":" "" timestring))))
         ;; take care of am/pm style
-        (if (and ampmstring (string= "pm" ampmstring))
+        ;; Be sure *not* to convert 12:00pm - 12:59pm to 2400-2459
+        (if (and ampmstring (string= "pm" ampmstring) (< starttimenum 1200))
             (setq starttimenum (+ starttimenum 1200)))
+       ;; Similar effect with 12:00am - 12:59am (need to convert to 0000-0059)
+        (if (and ampmstring (string= "am" ampmstring) (>= starttimenum 1200))
+            (setq starttimenum (- starttimenum 1200)))
         (format "T%04d00" starttimenum))
     nil))
 
@@ -804,7 +900,41 @@ Finto iCalendar file: ")
     (icalendar-export-region (point-min) (point-max) ical-filename)))
 
 (defalias 'icalendar-convert-diary-to-ical 'icalendar-export-file)
-(make-obsolete 'icalendar-convert-diary-to-ical 'icalendar-export-file)
+(make-obsolete 'icalendar-convert-diary-to-ical 'icalendar-export-file "22.1")
+
+(defvar icalendar--uid-count 0
+  "Auxiliary counter for creating unique ids.")
+
+(defun icalendar--create-uid (entry-full contents)
+  "Construct a unique iCalendar UID for a diary entry.
+ENTRY-FULL is the full diary entry string.  CONTENTS is the
+current iCalendar object, as a string.  Increase
+`icalendar--uid-count'.  Returns the UID string."
+  (let ((uid icalendar-uid-format))
+    
+    (setq uid (replace-regexp-in-string 
+              "%c" 
+              (format "%d" icalendar--uid-count)
+               uid t t))
+    (setq icalendar--uid-count (1+ icalendar--uid-count))
+    (setq uid (replace-regexp-in-string 
+              "%t"
+              (format "%d%d%d" (car (current-time))
+                      (cadr (current-time))
+                      (car (cddr (current-time)))) 
+              uid t t))
+    (setq uid (replace-regexp-in-string 
+              "%h" 
+              (format "%d" (abs (sxhash entry-full))) uid t t))
+    (setq uid (replace-regexp-in-string 
+              "%u" (or user-login-name "UNKNOWN_USER") uid t t))
+    (let ((dtstart (if (string-match "^DTSTART[^:]*:\\([0-9]*\\)" contents)
+                       (substring contents (match-beginning 1) (match-end 1))
+                   "DTSTART")))
+          (setq uid (replace-regexp-in-string "%s" dtstart uid t t)))
+
+    ;; Return the UID string
+    uid))
 
 ;;;###autoload
 (defun icalendar-export-region (min max ical-filename)
@@ -821,6 +951,7 @@ FExport diary data into iCalendar file: ")
         (start 0)
         (entry-main "")
         (entry-rest "")
+       (entry-full "")
         (header "")
         (contents-n-summary)
         (contents)
@@ -837,21 +968,22 @@ FExport diary data into iCalendar file: ")
     (save-excursion
       (goto-char min)
       (while (re-search-forward
-              "^\\([^ \t\n].+\\)\\(\\(\n[ \t].*\\)*\\)" max t)
+              ;; possibly ignore hidden entries beginning with "&"
+              (if icalendar-export-hidden-diary-entries
+                  "^\\([^ \t\n#].+\\)\\(\\(\n[ \t].*\\)*\\)"
+                "^\\([^ \t\n&#].+\\)\\(\\(\n[ \t].*\\)*\\)") max t)
         (setq entry-main (match-string 1))
         (if (match-beginning 2)
             (setq entry-rest (match-string 2))
           (setq entry-rest ""))
-        (setq header (format "\nBEGIN:VEVENT\nUID:emacs%d%d%d"
-                             (car (current-time))
-                             (cadr (current-time))
-                             (car (cddr (current-time)))))
+       (setq entry-full (concat entry-main entry-rest))
+
         (condition-case error-val
             (progn
               (setq contents-n-summary
                     (icalendar--convert-to-ical nonmarker entry-main))
               (setq other-elements (icalendar--parse-summary-and-rest
-                                    (concat entry-main entry-rest)))
+                                   entry-full))
               (setq contents (concat (car contents-n-summary)
                                      "\nSUMMARY:" (cadr contents-n-summary)))
               (let ((cla (cdr (assoc 'cla other-elements)))
@@ -875,6 +1007,9 @@ FExport diary data into iCalendar file: ")
                 ;;    (setq contents (concat contents "\nSUMMARY:" sum)))
                 (if url
                     (setq contents (concat contents "\nURL:" url))))
+
+             (setq header (concat "\nBEGIN:VEVENT\nUID:" 
+                                  (icalendar--create-uid entry-full contents)))
               (setq result (concat result header contents "\nEND:VEVENT")))
           ;; handle errors
           (error
@@ -883,7 +1018,7 @@ FExport diary data into iCalendar file: ")
              (set-buffer (get-buffer-create "*icalendar-errors*"))
              (insert (format "Error in line %d -- %s: `%s'\n"
                              (count-lines (point-min) (point))
-                             (cadr error-val)
+                             error-val
                              entry-main))))))
 
       ;; we're done, insert everything into the file
@@ -947,22 +1082,31 @@ Returns an alist."
              (p-sta (or (string-match "%t" icalendar-import-format) -1))
              (p-url (or (string-match "%u" icalendar-import-format) -1))
              (p-list (sort (list p-cla p-des p-loc p-org p-sta p-sum p-url) '<))
+            (ct 0)
              pos-cla pos-des pos-loc pos-org pos-sta pos-sum pos-url)
         (dotimes (i (length p-list))
+         ;; Use 'ct' to keep track of current position in list
           (cond ((and (>= p-cla 0) (= (nth i p-list) p-cla))
-                 (setq pos-cla (+ 2 (* 2 i))))
+                (setq ct (+ ct 1))
+                 (setq pos-cla (* 2 ct)))
                 ((and (>= p-des 0) (= (nth i p-list) p-des))
-                 (setq pos-des (+ 2 (* 2 i))))
+                (setq ct (+ ct 1))
+                 (setq pos-des (* 2 ct)))
                 ((and (>= p-loc 0) (= (nth i p-list) p-loc))
-                 (setq pos-loc (+ 2 (* 2 i))))
+                (setq ct (+ ct 1))
+                 (setq pos-loc (* 2 ct)))
                 ((and (>= p-org 0) (= (nth i p-list) p-org))
-                 (setq pos-org (+ 2 (* 2 i))))
+                (setq ct (+ ct 1))
+                 (setq pos-org (* 2 ct)))
                 ((and (>= p-sta 0) (= (nth i p-list) p-sta))
-                 (setq pos-sta (+ 2 (* 2 i))))
+                (setq ct (+ ct 1))
+                 (setq pos-sta (* 2 ct)))
                 ((and (>= p-sum 0) (= (nth i p-list) p-sum))
-                 (setq pos-sum (+ 2 (* 2 i))))
+                (setq ct (+ ct 1))
+                 (setq pos-sum (* 2 ct)))
                 ((and (>= p-url 0) (= (nth i p-list) p-url))
-                 (setq pos-url (+ 2 (* 2 i))))))
+                (setq ct (+ ct 1))
+                 (setq pos-url (* 2 ct)))) )
         (mapc (lambda (ij)
                 (setq s (icalendar--rris (car ij) (cadr ij) s t t)))
               (list
@@ -981,8 +1125,10 @@ Returns an alist."
                      (concat "\\(" icalendar-import-format-status "\\)??"))
                (list "%u"
                      (concat "\\(" icalendar-import-format-url "\\)??"))))
-        (setq s (concat "^" (icalendar--rris "%s" "\\(.*?\\)" s nil t)
-                        " $"))
+       ;; Need the \' regexp in order to detect multi-line items
+        (setq s (concat "\\`" 
+                          (icalendar--rris "%s" "\\(.*?\\)" s nil t)
+                        "\\'"))
         (if (string-match s summary-and-rest)
             (let (cla des loc org sta sum url)
               (if (and pos-sum (match-beginning pos-sum))
@@ -1026,20 +1172,22 @@ Returns an alist."
   "Convert \"ordinary\" diary entry to icalendar format.
 NONMARKER is a regular expression matching the start of non-marking
 entries.  ENTRY-MAIN is the first line of the diary entry."
-  (if (string-match (concat nonmarker
-                            "\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\)\\s-*"
-                            "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?"
-                            "\\("
-                            "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?"
-                            "\\)?"
-                            "\\s-*\\(.*?\\) ?$")
-                    entry-main)
+  (if (string-match
+       (concat nonmarker
+               "\\([^ /]+[ /]+[^ /]+[ /]+[^ ]+\\)\\s-*" ; date
+               "\\(0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?" ; start time
+               "\\("
+               "-0?\\([1-9][0-9]?:[0-9][0-9]\\)\\([ap]m\\)?\\)?" ; end time
+               "\\)?"
+               "\\s-*\\(.*?\\) ?$")
+       entry-main)
       (let* ((datetime (substring entry-main (match-beginning 1)
                                   (match-end 1)))
              (startisostring (icalendar--datestring-to-isodate
                               datetime))
              (endisostring (icalendar--datestring-to-isodate
                             datetime 1))
+             (endisostring1)
              (starttimestring (icalendar--diarytime-to-isotime
                                (if (match-beginning 3)
                                    (substring entry-main
@@ -1069,13 +1217,27 @@ entries.  ENTRY-MAIN is the first line of the diary entry."
 
         (unless startisostring
           (error "Could not parse date"))
+
+        ;; If only start-date is specified, then end-date is next day,
+        ;; otherwise it is same day.
+        (setq endisostring1 (if starttimestring
+                                startisostring
+                              endisostring))
+
         (when starttimestring
           (unless endtimestring
             (let ((time
                    (read (icalendar--rris "^T0?" ""
                                           starttimestring))))
+              (if (< time 230000)
+                  ;; Case: ends on same day
               (setq endtimestring (format "T%06d"
-                                          (+ 10000 time))))))
+                                              (+ 10000 time)))
+                ;; Case: ends on next day
+                (setq endtimestring (format "T%06d"
+                                              (- time 230000)))
+                (setq endisostring1 endisostring)) )))
+
         (list (concat "\nDTSTART;"
                       (if starttimestring "VALUE=DATE-TIME:"
                         "VALUE=DATE:")
@@ -1084,14 +1246,25 @@ entries.  ENTRY-MAIN is the first line of the diary entry."
                       "\nDTEND;"
                       (if endtimestring "VALUE=DATE-TIME:"
                         "VALUE=DATE:")
-                      (if starttimestring
-                          startisostring
-                        endisostring)
+                      endisostring1
                       (or endtimestring ""))
               summary))
     ;; no match
     nil))
 
+(defun icalendar-first-weekday-of-year (abbrevweekday year)
+  "Find the first ABBREVWEEKDAY in a given YEAR.
+Returns day number."
+  (let* ((day-of-week-jan01 (calendar-day-of-week (list 1 1 year)))
+         (result (+ 1
+                    (- (icalendar--get-weekday-number abbrevweekday)
+                       day-of-week-jan01))))
+    (cond ((<= result 0)
+           (setq result (+ result 7)))
+          ((> result 7)
+           (setq result (- result 7))))
+    result))
+
 (defun icalendar--convert-weekly-to-ical (nonmarker entry-main)
   "Convert weekly diary entry to icalendar format.
 NONMARKER is a regular expression matching the start of non-marking
@@ -1150,21 +1323,23 @@ entries.  ENTRY-MAIN is the first line of the diary entry."
                       (if starttimestring
                           "VALUE=DATE-TIME:"
                         "VALUE=DATE:")
-                      ;; find the correct week day,
-                      ;; 1st january 2000 was a saturday
-                      (format
-                       "200001%02d"
-                       (+ (icalendar--get-weekday-number day) 2))
+                      ;; Find the first requested weekday of the
+                      ;; start year
+                      (funcall 'format "%04d%02d%02d"
+                               icalendar-recurring-start-year 1
+                               (icalendar-first-weekday-of-year
+                                day icalendar-recurring-start-year))
                       (or starttimestring "")
                       "\nDTEND;"
                       (if endtimestring
                           "VALUE=DATE-TIME:"
                         "VALUE=DATE:")
-                      (format
-                       "200001%02d"
-                       ;; end is non-inclusive!
-                       (+ (icalendar--get-weekday-number day)
-                          (if endtimestring 2 3)))
+                      (funcall 'format "%04d%02d%02d"
+                               ;; end is non-inclusive!
+                               icalendar-recurring-start-year 1
+                               (+ (icalendar-first-weekday-of-year
+                                   day icalendar-recurring-start-year)
+                          (if endtimestring 0 1)))
                       (or endtimestring "")
                       "\nRRULE:FREQ=WEEKLY;INTERVAL=1;BYDAY="
                       day)
@@ -1177,7 +1352,7 @@ entries.  ENTRY-MAIN is the first line of the diary entry."
 NONMARKER is a regular expression matching the start of non-marking
 entries.  ENTRY-MAIN is the first line of the diary entry."
   (if (string-match (concat nonmarker
-                            (if european-calendar-style
+                            (if (eq (icalendar--date-style) 'european)
                                 "0?\\([1-9]+[0-9]?\\)\\s-+\\([a-z]+\\)\\s-+"
                               "\\([a-z]+\\)\\s-+0?\\([1-9]+[0-9]?\\)\\s-+")
                             "\\*?\\s-*"
@@ -1188,8 +1363,8 @@ entries.  ENTRY-MAIN is the first line of the diary entry."
                             "\\s-*\\([^0-9]+.*?\\) ?$" ; must not match years
                             )
                     entry-main)
-      (let* ((daypos (if european-calendar-style 1 2))
-             (monpos (if european-calendar-style 2 1))
+      (let* ((daypos (if (eq (icalendar--date-style) 'european) 1 2))
+             (monpos (if (eq (icalendar--date-style) 'european) 2 1))
              (day (read (substring entry-main
                                    (match-beginning daypos)
                                    (match-end daypos))))
@@ -1245,9 +1420,9 @@ entries.  ENTRY-MAIN is the first line of the diary entry."
                        (if endtimestring 0 1))
                       (or endtimestring "")
                       "\nRRULE:FREQ=YEARLY;INTERVAL=1;BYMONTH="
-                      (format "%2d" month)
+                      (format "%d" month)
                       ";BYMONTHDAY="
-                      (format "%2d" day))
+                      (format "%d" day))
               summary))
     ;; no match
     nil))
@@ -1610,7 +1785,7 @@ buffer `*icalendar-errors*'."
       nil)))
 
 (defalias 'icalendar-extract-ical-from-buffer 'icalendar-import-buffer)
-(make-obsolete 'icalendar-extract-ical-from-buffer 'icalendar-import-buffer)
+(make-obsolete 'icalendar-extract-ical-from-buffer 'icalendar-import-buffer "22.1")
 
 (defun icalendar--format-ical-event (event)
   "Create a string representation of an iCalendar EVENT."
@@ -1934,17 +2109,23 @@ END-T is the event's end time in diary format."
           ((string-equal frequency "YEARLY")
            (icalendar--dmsg "yearly")
            (if until
-               (setq result (format
-                             (concat "%%%%(and (diary-date %s %s t) "
-                                     "(diary-block %s %s)) %s%s%s")
-                             (if european-calendar-style (nth 3 dtstart-dec)
-                               (nth 4 dtstart-dec))
-                             (if european-calendar-style (nth 4 dtstart-dec)
-                               (nth 3 dtstart-dec))
-                             dtstart-conv
-                             until-conv
-                             (or start-t "")
-                             (if end-t "-" "") (or end-t "")))
+               (let ((day (nth 3 dtstart-dec))
+                     (month (nth 4 dtstart-dec)))
+                 (setq result (concat "%%(and (diary-date "
+                                      (cond ((eq (icalendar--date-style) 'iso)
+                                             (format "t %d %d" month day))
+                                            ((eq (icalendar--date-style) 'european)
+                                             (format "%d %d t" day month))
+                                            ((eq (icalendar--date-style) 'american)
+                                             (format "%d %d t" month day)))
+                                      ") (diary-block "
+                                      dtstart-conv
+                                      " "
+                                      until-conv
+                                      ")) "
+                                      (or start-t "")
+                                      (if end-t "-" "")
+                                      (or end-t ""))))
              (setq result (format
                            "%%%%(and (diary-anniversary %s)) %s%s%s"
                            dtstart-conv
@@ -1955,14 +2136,18 @@ END-T is the event's end time in diary format."
            (icalendar--dmsg "monthly")
            (setq result
                  (format
-                  "%%%%(and (diary-date %s %s %s) (diary-block %s %s)) %s%s%s"
-                  (if european-calendar-style (nth 3 dtstart-dec) "t")
-                  (if european-calendar-style "t" (nth 3 dtstart-dec))
-                  "t"
+                  "%%%%(and (diary-date %s) (diary-block %s %s)) %s%s%s"
+                  (let ((day (nth 3 dtstart-dec)))
+                    (cond ((eq (icalendar--date-style) 'iso)
+                           (format "t t %d" day))
+                          ((eq (icalendar--date-style) 'european)
+                           (format "%d t t" day))
+                          ((eq (icalendar--date-style) 'american)
+                           (format "t %d t" day))))
                   dtstart-conv
                   (if until
                       until-conv
-                    "1 1 9999") ;; FIXME: should be unlimited
+                    (if (eq (icalendar--date-style) 'iso) "9999 1 1" "1 1 9999")) ;; FIXME: should be unlimited
                   (or start-t "")
                   (if end-t "-" "") (or end-t ""))))
           ;; daily
@@ -2055,8 +2240,17 @@ the entry."
       (unless diary-file
         (setq diary-file
               (read-file-name "Add appointment to this diary file: ")))
-      ;; Note: make-diary-entry will add a trailing blank char.... :(
-      (make-diary-entry string non-marking diary-file)))
+      ;; Note: diary-make-entry will add a trailing blank char.... :(
+      (funcall (if (fboundp 'diary-make-entry)
+                   'diary-make-entry
+                 'make-diary-entry)
+               string non-marking diary-file)))
+  ;; Würgaround to remove the trailing blank char
+  (save-excursion
+    (set-buffer (find-file diary-file))
+    (goto-char (point-max))
+    (if (= (char-before) ? )
+        (delete-char -1)))
   ;; return diary-file in case it has been changed interactively
   diary-file)