diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el index ba7a62f8b..a955469b5 100644 --- a/lisp/ox-icalendar.el +++ b/lisp/ox-icalendar.el @@ -341,7 +341,7 @@ A headline is blocked when either (1- (length org-icalendar-date-time-format))) ?Z)) (defvar org-agenda-default-appointment-duration) ; From org-agenda.el. -(defun org-icalendar-convert-timestamp (timestamp keyword &optional end utc) +(defun org-icalendar-convert-timestamp (timestamp keyword &optional end tz) "Convert TIMESTAMP to iCalendar format. TIMESTAMP is a timestamp object. KEYWORD is added in front of @@ -352,8 +352,11 @@ Also increase the hour by two (if time string contains a time), or the day by one (if it does not contain a time) when no explicit ending time is specified. -When optional argument UTC is non-nil, time will be expressed in -Universal Time, ignoring `org-icalendar-date-time-format'." +When optional argument TZ is non-nil, timezone data time will be +added to the timestamp. It can be the string \"UTC\", to use UTC +time, or a string in the IANA TZ database +format (e.g. \"Europe/London\"). In either case, the value of +`org-icalendar-date-time-format' will be ignored." (let* ((year-start (org-element-property :year-start timestamp)) (year-end (org-element-property :year-end timestamp)) (month-start (org-element-property :month-start timestamp)) @@ -387,8 +390,9 @@ Universal Time, ignoring `org-icalendar-date-time-format'." (concat keyword (format-time-string - (cond (utc ":%Y%m%dT%H%M%SZ") + (cond ((string-equal tz "UTC") ":%Y%m%dT%H%M%SZ") ((not with-time-p) ";VALUE=DATE:%Y%m%d") + ((stringp tz) (concat ";TZID=" tz ":%Y%m%dT%H%M%S")) (t (replace-regexp-in-string "%Z" org-icalendar-timezone org-icalendar-date-time-format @@ -396,7 +400,10 @@ Universal Time, ignoring `org-icalendar-date-time-format'." ;; Convert timestamp into internal time in order to use ;; `format-time-string' and fix any mistake (i.e. MI >= 60). (encode-time 0 mi h d m y) - (and (or utc (and with-time-p (org-icalendar-use-UTC-date-time-p))) + (and (or (string-equal tz "UTC") + (and (null tz) + with-time-p + (org-icalendar-use-UTC-date-time-p))) t))))) (defun org-icalendar-dtstamp () @@ -545,7 +552,8 @@ inlinetask within the section." contents 0 (min (length contents) org-icalendar-include-body)))) (org-icalendar-include-body (org-trim contents))))))) - (cat (org-icalendar-get-categories entry info))) + (cat (org-icalendar-get-categories entry info)) + (tz (org-element-property :TIMEZONE entry))) (concat ;; Events: Delegate to `org-icalendar--vevent' to generate ;; "VEVENT" component from scheduled, deadline, or any @@ -556,14 +564,14 @@ inlinetask within the section." org-icalendar-use-deadline) (org-icalendar--vevent entry deadline (concat "DL-" uid) - (concat "DL: " summary) loc desc cat))) + (concat "DL: " summary) loc desc cat tz))) (let ((scheduled (org-element-property :scheduled entry))) (and scheduled (memq (if todo-type 'event-if-todo 'event-if-not-todo) org-icalendar-use-scheduled) (org-icalendar--vevent entry scheduled (concat "SC-" uid) - (concat "S: " summary) loc desc cat))) + (concat "S: " summary) loc desc cat tz))) ;; When collecting plain timestamps from a headline and its ;; title, skip inlinetasks since collection will happen once ;; ENTRY is one of them. @@ -581,7 +589,7 @@ inlinetask within the section." ((t) t))) (let ((uid (format "TS%d-%s" (cl-incf counter) uid))) (org-icalendar--vevent - entry ts uid summary loc desc cat)))) + entry ts uid summary loc desc cat tz)))) info nil (and (eq type 'headline) 'inlinetask)) "")) ;; Task: First check if it is appropriate to export it. If @@ -626,7 +634,7 @@ inlinetask within the section." contents)))) (defun org-icalendar--vevent - (entry timestamp uid summary location description categories) + (entry timestamp uid summary location description categories timezone) "Create a VEVENT component. ENTRY is either a headline or an inlinetask element. TIMESTAMP @@ -635,7 +643,8 @@ is the unique identifier for the event. SUMMARY defines a short summary or subject for the event. LOCATION defines the intended venue for the event. DESCRIPTION provides the complete description of the event. CATEGORIES defines the categories the -event belongs to. +event belongs to. If TIMEZONE is non-nil, use the specified +timezone for this event only. Return VEVENT component as a string." (org-icalendar-fold-string @@ -645,8 +654,8 @@ Return VEVENT component as a string." (concat "BEGIN:VEVENT\n" (org-icalendar-dtstamp) "\n" "UID:" uid "\n" - (org-icalendar-convert-timestamp timestamp "DTSTART") "\n" - (org-icalendar-convert-timestamp timestamp "DTEND" t) "\n" + (org-icalendar-convert-timestamp timestamp "DTSTART" nil timezone) "\n" + (org-icalendar-convert-timestamp timestamp "DTEND" t timezone) "\n" ;; RRULE. (when (org-element-property :repeater-type timestamp) (format "RRULE:FREQ=%s;INTERVAL=%d\n"