: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 \"DTSTART_HASH@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.")
;; 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))
(defvar icalendar--uid-count 0
"Auxiliary counter for creating unique ids.")
-(defun icalendar--create-uid ()
- "Create a unique identifier.
-Use `current-time' and a counter to create unique ids. The
-counter is necessary for systems which do not provide resolution
-finer than a second."
- (setq icalendar--uid-count (1+ icalendar--uid-count))
- (format "emacs%d%d%d%d"
- (car (current-time))
- (cadr (current-time))
- (car (cddr (current-time)))
- icalendar--uid-count))
+(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)
(start 0)
(entry-main "")
(entry-rest "")
+ (entry-full "")
(header "")
(contents-n-summary)
(contents)
(if (match-beginning 2)
(setq entry-rest (match-string 2))
(setq entry-rest ""))
- (setq header (format "\nBEGIN:VEVENT\nUID:%s"
- (icalendar--create-uid)))
+ (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)))
;; (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
(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
(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))
(icalendar-testsuite--test-first-weekday-of-year)
(icalendar-testsuite--test-datestring-to-isodate)
(icalendar-testsuite--test-datetime-to-diary-date)
+ (icalendar-testsuite--test-diarytime-to-isotime)
(icalendar-testsuite--test-calendar-style)
(icalendar-testsuite--test-create-uid))
(icalendar-import-format-url " URL %s")
(icalendar-import-format-class " CLA %s")
(result))
- ;; FIXME: need a trailing blank char!
- (setq result (icalendar--parse-summary-and-rest "SUM sum ORG org "))
+ (setq result (icalendar--parse-summary-and-rest "SUM sum ORG org"))
(assert (string= (cdr (assoc 'org result)) "org"))
(setq result (icalendar--parse-summary-and-rest
- "SUM sum DES des LOC loc ORG org STA sta URL url CLA cla "))
+ "SUM sum DES des LOC loc ORG org STA sta URL url CLA cla"))
(assert (string= (cdr (assoc 'des result)) "des"))
(assert (string= (cdr (assoc 'loc result)) "loc"))
(assert (string= (cdr (assoc 'org result)) "org"))
(assert (string= (icalendar--datetime-to-diary-date datetime)
"12 31 2008"))))
+(defun icalendar-testsuite--test-diarytime-to-isotime ()
+ "Test method for `icalendar--diarytime-to-isotime'."
+ (assert (string= (icalendar--diarytime-to-isotime "0100" "")
+ "T010000"))
+ (assert (string= (icalendar--diarytime-to-isotime "0100" "am")
+ "T010000"))
+ (assert (string= (icalendar--diarytime-to-isotime "0100" "pm")
+ "T130000"))
+ (assert (string= (icalendar--diarytime-to-isotime "1200" "")
+ "T120000"))
+ (assert (string= (icalendar--diarytime-to-isotime "17:17" "")
+ "T171700"))
+ (assert (string= (icalendar--diarytime-to-isotime "1200" "am")
+ "T000000"))
+ (assert (string= (icalendar--diarytime-to-isotime "1201" "am")
+ "T000100"))
+ (assert (string= (icalendar--diarytime-to-isotime "1259" "am")
+ "T005900"))
+ (assert (string= (icalendar--diarytime-to-isotime "1200" "pm")
+ "T120000"))
+ (assert (string= (icalendar--diarytime-to-isotime "1201" "pm")
+ "T120100"))
+ (assert (string= (icalendar--diarytime-to-isotime "1259" "pm")
+ "T125900")))
+
(defun icalendar-testsuite--test-calendar-style ()
"Test method for `icalendar--date-style'."
(dolist (calendar-date-style '(iso american european))
(defun icalendar-testsuite--test-create-uid ()
"Test method for `icalendar--create-uid'."
- (let (t-ct
- (icalendar--uid-count 77))
+ (let* ((icalendar-uid-format "xxx-%t-%c-%h-%u-%s")
+ t-ct
+ (icalendar--uid-count 77)
+ (entry-full "30.06.1964 07:01 blahblah")
+ (hash (format "%d" (abs (sxhash entry-full))))
+ (contents "DTSTART:19640630T070100\nblahblah")
+ (username (or user-login-name "UNKNOWN_USER"))
+ )
;; FIXME! If a test fails 'current-time is screwed. FIXME!
(fset 't-ct (symbol-function 'current-time))
(fset 'current-time (lambda () '(1 2 3)))
(assert (= 77 icalendar--uid-count))
- (assert (string= "emacs12378" (icalendar--create-uid)))
+ (assert (string= (concat "xxx-123-77-" hash "-" username "-19640630")
+ (icalendar--create-uid entry-full contents)))
(assert (= 78 icalendar--uid-count))
(fset 'current-time (symbol-function 't-ct))
+
+ (setq contents "blahblah")
+ (setq icalendar-uid-format "yyy%syyy")
+ (assert (string= (concat "yyyDTSTARTyyy")
+ (icalendar--create-uid entry-full contents)))
))
+
;; ======================================================================
;; Test methods for exporting from diary to icalendar
;; ======================================================================