From mboxrd@z Thu Jan 1 00:00:00 1970 From: Eric Abrahamsen Subject: Re: TIMEZONE property for ical export Date: Sat, 19 Aug 2017 10:13:02 -0700 Message-ID: <87a82vv5up.fsf@ericabrahamsen.net> References: <87ziawx1f7.fsf@ericabrahamsen.net> <87fucolzv5.fsf@nicolasgoaziou.fr> Mime-Version: 1.0 Content-Type: multipart/mixed; boundary="=-=-=" Return-path: Received: from eggs.gnu.org ([2001:4830:134:3::10]:46822) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1dj7JJ-00010t-Or for emacs-orgmode@gnu.org; Sat, 19 Aug 2017 13:13:23 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1dj7JF-000299-LI for emacs-orgmode@gnu.org; Sat, 19 Aug 2017 13:13:21 -0400 Received: from [195.159.176.226] (port=44896 helo=blaine.gmane.org) by eggs.gnu.org with esmtps (TLS1.0:RSA_AES_128_CBC_SHA1:16) (Exim 4.71) (envelope-from ) id 1dj7JF-00028p-9H for emacs-orgmode@gnu.org; Sat, 19 Aug 2017 13:13:17 -0400 Received: from list by blaine.gmane.org with local (Exim 4.84_2) (envelope-from ) id 1dj7J5-0005Jg-Qp for emacs-orgmode@gnu.org; Sat, 19 Aug 2017 19:13:07 +0200 List-Id: "General discussions about Org-mode." List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: emacs-orgmode-bounces+geo-emacs-orgmode=m.gmane.org@gnu.org Sender: "Emacs-orgmode" To: emacs-orgmode@gnu.org --=-=-= Content-Type: text/plain Nicolas Goaziou writes: > Hello, > > Eric Abrahamsen writes: > >> Out of curiosity, what's your stance on supporting time zones in Org's >> timestamps? It would be an enormous amount of work, obviously, but in >> principle? > > In principle I agree this would be a very good thing, if possible at > all. > >> That's pretty much what it was before I messed with it, and I was a >> little confused because no callers of `org-icalendar-convert-timestamp' >> ever used the optional fourth argument at all. My thinking was that >> users might want to explicitly set the timezone to "UTC". To be honest >> I'm not sure why they would, but I also don't know why we'd accept t >> when nothing uses it. I found the whole handling of utc-or-not a bit >> confusing. What do you think? > > I have no strong opinion about this. I was merely concerned about > consistency. E.g., when using `format-time-string', I never think about > "UTC" for the last optional argument. As it is now, a program can call `org-icalendar-convert-timestamp' and pass in t for the "tz" argument, and that will go to `format-time-string' correctly. If a user sets the property to "UTC", that will also get converted to t. Again, I'm not sure this is actually useful, but the call to `format-time-string' should be consistent. > BTW, as long as Org doesn't support time zones, the "ox-icalendar" > property may be prefixed with "ICALENDAR_" (e.g., ICALENDAR_TZ). Done! How does the attached patch look? Eric --=-=-= Content-Type: text/x-diff Content-Disposition: attachment; filename=0001-Add-per-entry-timezone-support-for-icalendar-export.patch >From 16e86dbdde1e0accfa7d882adfc173d60df15fe4 Mon Sep 17 00:00:00 2001 From: Eric Abrahamsen Date: Sat, 19 Aug 2017 10:09:00 -0700 Subject: [PATCH] Add per-entry timezone support for icalendar export * lisp/ox-icalendar.el (org-icalendar-entry): Look for an "ICALENDAR_TZ" property. (org-icalendar--vevent, org-icalendar--vtodo): Accept additional timezone argument. (org-icalendar-convert-timestamp): Change paramenter name to "tz", and accept a wider variety of values. --- doc/org.texi | 13 ++++++++++--- lisp/ox-icalendar.el | 44 +++++++++++++++++++++++++++----------------- 2 files changed, 37 insertions(+), 20 deletions(-) diff --git a/doc/org.texi b/doc/org.texi index 032087fc2..88c979982 100644 --- a/doc/org.texi +++ b/doc/org.texi @@ -14159,9 +14159,10 @@ and write it to @code{org-icalendar-combined-agenda-file} file name. @cindex property, SUMMARY @cindex property, DESCRIPTION @cindex property, LOCATION -The iCalendar export back-end includes SUMMARY, DESCRIPTION and LOCATION -properties from the Org entries when exporting. To force the back-end to -inherit the LOCATION property, configure the +@cindex property, ICALENDAR_TZ +The iCalendar export back-end includes SUMMARY, DESCRIPTION, LOCATION and +ICALENDAR_TZ properties from the Org entries when exporting. To force the +back-end to inherit the LOCATION property, configure the @code{org-use-property-inheritance} variable. When Org entries do not have SUMMARY, DESCRIPTION and LOCATION properties, @@ -14170,6 +14171,12 @@ derives the description from the body of the Org item. The @code{org-icalendar-include-body} variable limits the maximum number of characters of the content are turned into its description. +The ICALENDAR_TZ property can be used to specify a per-entry time zone, and +will be applied to any entry with timestamp information. Time zones should +be specified as per the IANA time zone database format, e.g.@: +``Asia/Almaty''. Alternately, the property value can be ``UTC'', to force +UTC time for this entry only. + Exporting to iCalendar format depends in large part on the capabilities of the destination application. Some are more lenient than others. Consult the Org mode FAQ for advice on specific applications. diff --git a/lisp/ox-icalendar.el b/lisp/ox-icalendar.el index ba7a62f8b..eb8491f7e 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 :ICALENDAR_TZ 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 @@ -595,7 +603,7 @@ inlinetask within the section." (not (org-icalendar-blocked-headline-p entry info)))) ((t) (eq todo-type 'todo)))) - (org-icalendar--vtodo entry uid summary loc desc cat)) + (org-icalendar--vtodo entry uid summary loc desc cat tz)) ;; Diary-sexp: Collect every diary-sexp element within ENTRY ;; and its title, and transcode them. If ENTRY is ;; a headline, skip inlinetasks: they will be handled @@ -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. TIMEZONE specifies a time zone 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" @@ -664,7 +673,7 @@ Return VEVENT component as a string." "END:VEVENT")))) (defun org-icalendar--vtodo - (entry uid summary location description categories) + (entry uid summary location description categories timezone) "Create a VTODO component. ENTRY is either a headline or an inlinetask element. UID is the @@ -672,6 +681,7 @@ unique identifier for the task. SUMMARY defines a short summary or subject for the task. LOCATION defines the intended venue for the task. DESCRIPTION provides the complete description of the task. CATEGORIES defines the categories the task belongs to. +TIMEZONE specifies a time zone for this TODO only. Return VTODO component as a string." (let ((start (or (and (memq 'todo-start org-icalendar-use-scheduled) @@ -690,11 +700,11 @@ Return VTODO component as a string." (concat "BEGIN:VTODO\n" "UID:TODO-" uid "\n" (org-icalendar-dtstamp) "\n" - (org-icalendar-convert-timestamp start "DTSTART") "\n" + (org-icalendar-convert-timestamp start "DTSTART" nil timezone) "\n" (and (memq 'todo-due org-icalendar-use-deadline) (org-element-property :deadline entry) (concat (org-icalendar-convert-timestamp - (org-element-property :deadline entry) "DUE") + (org-element-property :deadline entry) "DUE" nil timezone) "\n")) "SUMMARY:" summary "\n" (and (org-string-nw-p location) (format "LOCATION:%s\n" location)) -- 2.14.1 --=-=-=--