emacs-devel
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

Patch: initial timezone support for icalendar.el


From: Tom Tromey
Subject: Patch: initial timezone support for icalendar.el
Date: Wed, 09 Jan 2008 18:27:43 -0700

Today I found out that icalendar.el doesn't handle timezones in
appointments.  Luckily my meeting was scheduled in a timezone west of
here, or I probably would have missed it :-)

This patch adds some initial support.  It is nowhere near complete,
but it suffices to import the 3 events I have in a local Zimbra
calendar.  So, it is an improvement at least.

I think this general approach could probably be expanded to handle
more types of timezone.  I didn't try to handle timezones in durations
(I didn't even look at that part of the spec).

This only works on hosts where encode-time accepts a TZ rule in a
particular form.  I built it according to the glibc documentation;
offhand I don't know if that is POSIX- or GNU-specific.

Tom

2008-01-10  Tom Tromey  <address@hidden>

        * calendar/icalendar.el (icalendar--decode-isodatetime): Remove
        FIXME.
        (icalendar--convert-tz-offset): New function.
        (icalendar--parse-vtimezone): Likewise.
        (icalendar--convert-all-timezones): Likewise.
        (icalendar--find-time-zone): Likewise.
        (icalendar--decode-isodatetime): Add 'zone' argument; handle it.
        (icalendar--convert-ical-to-diary): Compute zone-map.  Pass
        timezone to icalendar--decode-isodatetime.

Index: lisp/calendar/icalendar.el
===================================================================
RCS file: /sources/emacs/emacs/lisp/calendar/icalendar.el,v
retrieving revision 1.24
diff -u -r1.24 icalendar.el
--- lisp/calendar/icalendar.el  26 Sep 2007 00:10:17 -0000      1.24
+++ lisp/calendar/icalendar.el  10 Jan 2008 01:59:21 -0000
@@ -1,6 +1,6 @@
 ;;; icalendar.el --- iCalendar implementation -*-coding: utf-8 -*-
 
-;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007  Free Software Foundation, 
Inc.
+;; Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007, 2008  Free Software 
Foundation, Inc.
 
 ;; Author:         Ulf Jasper <address@hidden>
 ;; Created:        August 2002
@@ -390,15 +390,90 @@
                 (append result (list (list param-name param-value)))))))
     result))
 
-(defun icalendar--decode-isodatetime (isodatetimestring &optional day-shift)
+(defun icalendar--convert-tz-offset (alist dst-p)
+  "Return a cons of two strings representing a timezone start.
+ALIST is an alist entry from a VTIMEZONE, like STANDARD.
+DST-P is non-nil if this is for daylight savings time.
+The strings are suitable for assembling into a TZ variable."
+  (let ((offset (car (cddr (assq 'TZOFFSETTO alist))))
+       (rrule-value (car (cddr (assq 'RRULE alist))))
+       (dtstart (car (cddr (assq 'DTSTART alist)))))
+    ;; FIXME: for now we only handle RRULE and not RDATE here.
+    (when (and offset rrule-value dtstart)
+      (let* ((rrule (icalendar--split-value rrule-value))
+            (freq (cadr (assq 'FREQ rrule)))
+            (bymonth (cadr (assq 'BYMONTH rrule)))
+            (byday (cadr (assq 'BYDAY rrule))))
+       ;; FIXME: we don't correctly handle WKST here.
+       (if (and (string= freq "YEARLY") bymonth)
+           (cons
+            (concat
+             ;; Fake a name.
+             (if dst-p "(DST?)" "(STD?)")
+             ;; For TZ, OFFSET is added to the local time.  So,
+             ;; invert the values.
+             (if (eq (aref offset 0) ?-) "+" "-")
+             (substring offset 1 3)
+             ":"
+             (substring offset 3 5))
+            ;; The start time.
+            (let* ((day (icalendar--get-weekday-number (substring byday -2)))
+                   (week (if (eq day -1)
+                             byday
+                           (substring byday 0 -2))))
+              (concat "M" bymonth "." week "." (if (eq day -1) "0"
+                                                 (int-to-string day))
+                      ;; Start time.
+                      "/"
+                      (substring dtstart -6 -4)
+                      ":"
+                      (substring dtstart -4 -2)
+                      ":"
+                      (substring dtstart -2)))))))))
+
+(defun icalendar--parse-vtimezone (alist)
+  "Turn a VTIMEZONE alist into a cons (ID . TZ-STRING).
+Return nil if timezone cannot be parsed."
+  (let* ((tz-id (icalendar--get-event-property alist 'TZID))
+        (daylight (cadr (cdar (icalendar--get-children alist 'DAYLIGHT))))
+        (day (and daylight (icalendar--convert-tz-offset daylight t)))
+        (standard (cadr (cdar (icalendar--get-children alist 'STANDARD))))
+        (std (and standard (icalendar--convert-tz-offset standard nil))))
+    (if (and tz-id std)
+       (cons tz-id
+             (if day
+                 (concat (car std) (car day)
+                         "," (cdr day) "," (cdr std))
+               (car std))))))
+
+(defun icalendar--convert-all-timezones (icalendar)
+  "Convert all timezones in the calendar into an alist.
+Each element of the alist is a cons (ID . TZ-STRING),
+like `icalendar--parse-vtimezone'."
+  (let (result)
+    (dolist (zone (icalendar--get-children (car icalendar) 'VTIMEZONE))
+      (setq zone (icalendar--parse-vtimezone zone))
+      (if zone
+         (setq result (cons zone result))))
+    result))
+
+(defun icalendar--find-time-zone (prop-list zone-map)
+  "Return a timezone string for the time zone in PROP-LIST, or nil if none.
+ZONE_MAP is a timezone alist as returned by 
`icalendar--convert-all-timezones'."
+  (let ((id (plist-get prop-list 'TZID)))
+    (if id
+       (cdr (assoc id zone-map)))))
+
+(defun icalendar--decode-isodatetime (isodatetimestring &optional day-shift
+                                                       zone)
   "Return ISODATETIMESTRING in format like `decode-time'.
 Converts from ISO-8601 to Emacs representation.  If
 ISODATETIMESTRING specifies UTC time (trailing letter Z) the
 decoded time is given in the local time zone!  If optional
 parameter DAY-SHIFT is non-nil the result is shifted by DAY-SHIFT
 days.
+ZONE, if provided, is the timezone, in any format understood by `encode-time'.
 
-FIXME: TZID-attributes are ignored....!
 FIXME: multiple comma-separated values should be allowed!"
   (icalendar--dmsg isodatetimestring)
   (if isodatetimestring
@@ -433,7 +508,7 @@
         ;; create the decoded date-time
         ;; FIXME!?!
         (condition-case nil
-            (decode-time (encode-time second minute hour day month year))
+            (decode-time (encode-time second minute hour day month year zone))
           (error
            (message "Cannot decode \"%s\"" isodatetimestring)
            ;; hope for the best...
@@ -1566,7 +1641,9 @@
          (error-string "")
          (event-ok t)
          (found-error nil)
+        (zone-map (icalendar--convert-all-timezones ical-list))
          e diary-string)
+    ;; Compute the 
     ;; step through all events/appointments
     (while ev
       (setq e (car ev))
@@ -1574,13 +1651,24 @@
       (setq event-ok nil)
       (condition-case error-val
           (let* ((dtstart (icalendar--get-event-property e 'DTSTART))
-                 (dtstart-dec (icalendar--decode-isodatetime dtstart))
+                (dtstart-zone (icalendar--find-time-zone
+                               (icalendar--get-event-property-attributes
+                                e 'DTSTART)
+                               zone-map))
+                 (dtstart-dec (icalendar--decode-isodatetime dtstart nil
+                                                            dtstart-zone))
                  (start-d (icalendar--datetime-to-diary-date
                            dtstart-dec))
                  (start-t (icalendar--datetime-to-colontime dtstart-dec))
                  (dtend (icalendar--get-event-property e 'DTEND))
-                 (dtend-dec (icalendar--decode-isodatetime dtend))
-                 (dtend-1-dec (icalendar--decode-isodatetime dtend -1))
+                (dtend-zone (icalendar--find-time-zone
+                             (icalendar--get-event-property-attributes
+                              e 'DTEND)
+                             zone-map))
+                 (dtend-dec (icalendar--decode-isodatetime dtend
+                                                          nil dtend-zone))
+                 (dtend-1-dec (icalendar--decode-isodatetime dtend -1
+                                                            dtend-zone))
                  end-d
                  end-1-d
                  end-t




reply via email to

[Prev in Thread] Current Thread [Next in Thread]