bug-gnu-emacs
[Top][All Lists]
Advanced

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

bug#34315: [PATCH] icalendar.el: DURATION fix + more robust timezone han


From: Lars Ingebrigtsen
Subject: bug#34315: [PATCH] icalendar.el: DURATION fix + more robust timezone handling
Date: Thu, 01 Oct 2020 03:45:59 +0200
User-agent: Gnus/5.13 (Gnus v5.13) Emacs/28.0.50 (gnu/linux)

thunk2@arcor.de (Thomas Plass) writes:

>  - in celebration, I'd like to submit yet another patch that extends
>    icalendar.el's timezone handling, this one intended to support
>    regions that at some point in the past made a permanent switch from
>    a DST/STD scheme to STD-only/DST-only.  Cases in point are China
>    and Turkey,
>    cf. https://en.wikipedia.org/wiki/Daylight_saving_time_in_Asia.
>
>    Ulf, would you care to review the code and the included test cases?

Thanks for the code.  It was in slightly inconvenient format -- we
prefer just simple patches, so I've reformatted it as such below.

However, the code makes two tests fail:

2 unexpected results:
   FAILED  icalendar--convert-tz-offset
   FAILED  icalendar--parse-vtimezone

I haven't actually looked at the failing cases, though.

In addition, the test cases included aren't actually used?  Could you
propose some code to use them?  (It should go in
test/lisp/calendar/icalendar-tests.el.)

-- 
(domestic pets only, the antidote for overdose, milk.)
   bloggy blog: http://lars.ingebrigtsen.no
diff --git a/lisp/calendar/icalendar.el b/lisp/calendar/icalendar.el
index dab277487e..e4760db3e4 100644
--- a/lisp/calendar/icalendar.el
+++ b/lisp/calendar/icalendar.el
@@ -519,46 +519,56 @@ icalendar--convert-tz-offset
         (dtstart (car (cddr (assq 'DTSTART alist))))
         (no-dst (or rdate-p (equal offsetto offsetfrom))))
     ;; FIXME: the presence of an RDATE is assumed to denote the first day of 
the year
-    (when (and offsetto dtstart (or rrule-value no-dst))
-      (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 (or no-dst (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 offsetto 0) ?-) "+" "-")
-             (substring offsetto 1 3)
-             ":"
-             (substring offsetto 3 5))
-            ;; The start time.
-             (let* ((day (if no-dst
-                             1
-                           (icalendar--get-weekday-number (substring byday 
-2))))
-                    (week (if no-dst
-                              "1"
-                            (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
+    (if (and offsetto dtstart (or rrule-value no-dst))
+        (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 (or no-dst (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 offsetto 0) ?-) "+" "-")
+               (substring offsetto 1 3)
+               ":"
+               (substring offsetto 3 5))
+              ;; The start time.
+               (let* ((day (if no-dst
+                               1
+                             (icalendar--get-weekday-number (substring byday 
-2))))
+                      (week (if no-dst
+                                "1"
+                              (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
                  (when no-dst (setq bymonth "1"))
                 (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)))))))))
+                        ;; Start time.
+                        "/"
+                        (substring dtstart -6 -4)
+                        ":"
+                        (substring dtstart -4 -2)
+                        ":"
+                        (substring dtstart -2))))))
+      ;; neither RRULE nor RDATE present: return the offset and a placeholder
+      (cons
+       (concat
+       ;; Fake a name.
+       (if dst-p "DST" "STD")
+       (if (eq (aref offsetto 0) ?-) "+" "-")
+       (substring offsetto 1 3)
+       ":"
+       (substring offsetto 3 5))
+       dtstart))))
 
 (defun icalendar--parse-vtimezone (alist)
   "Turn a VTIMEZONE ALIST into a cons (ID . TZ-STRING).
@@ -571,34 +581,59 @@ icalendar--parse-vtimezone
         (standard (cadr (cdar (icalendar--get-most-recent-observance 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))))))
+        (cons tz-id
+              (if (and (not (assq 'RRULE daylight))
+                       (not (assq 'RRULE standard)))
+                  (let ((daylight-rdate (and (assq 'RDATE daylight)
+                                             
(icalendar--get-most-recent-observance-from-sub-comp
+                                              daylight
+                                              '(RDATE))))
+                        (standard-rdate (and (assq 'RDATE standard)
+                                             
(icalendar--get-most-recent-observance-from-sub-comp
+                                              standard
+                                              '(RDATE)))))
+                    (if (and daylight-rdate
+                             standard-rdate
+                             (string-greaterp daylight-rdate standard-rdate))
+                        (car day)
+                      (car std)))
+               (if day
+                   (concat (car std) (car day)
+                           "," (cdr day) "," (cdr std))
+                 (car std)))))))
 
 (defun icalendar--get-most-recent-observance (alist sub-comp)
-  "Return the latest observance for SUB-COMP DAYLIGHT or STANDARD.
+  "Return the latest observance of all SUB-COMPs DAYLIGHT or STANDARD.
 ALIST is a VTIMEZONE potentially containing historical records."
-;FIXME?: "most recent" should be relative to a given date
+;FIXME: "most recent" should be relative to a given date and
+;       avoid selecting a SUB-COMP valid for dates in the future.
   (let ((components (icalendar--get-children alist sub-comp)))
     (list
      (car
       (sort components
             #'(lambda (a b)
-                (let* ((get-recent (lambda (n)
-                                     (car
-                                      (sort
-                                       (delq nil
-                                             (mapcar (lambda (p)
-                                                       (and (memq (car p) 
'(DTSTART RDATE))
-                                                            (car (cddr p))))
-                                                     n))
-                                       'string-greaterp))))
-                       (a-recent (funcall get-recent (car (cddr a))))
-                       (b-recent (funcall get-recent (car (cddr b)))))
+                (let* ((a-recent 
(icalendar--get-most-recent-observance-from-sub-comp
+                                  (car (cddr a))
+                                  '(DTSTART RDATE)))
+                       (b-recent 
(icalendar--get-most-recent-observance-from-sub-comp
+                                  (car (cddr b))
+                                  '(DTSTART RDATE))))
                   (string-greaterp a-recent b-recent))))))))
 
+(defun icalendar--get-most-recent-observance-from-sub-comp (alist sym-list)
+  "Return the latest observance for ALIST DAYLIGHT or STANDARD.
+ALIST is an individual DAYLIGHT or STANDARD.
+SYM-LIST is a list of property names DTSTART and/or RDATE
+for filtering ALIST."
+  (car
+   (sort
+    (delq nil
+          (mapcar (lambda (p)
+                    (when (memq (car p) sym-list)
+                      (car (cddr p))))
+                  alist))
+    'string-greaterp)))
+
 (defun icalendar--convert-all-timezones (icalendar)
   "Convert all timezones in the ICALENDAR into an alist.
 Each element of the alist is a cons (ID . TZ-STRING),
diff --git 
a/test/lisp/calendar/icalendar-resources/Asia_Istanbul_20200924T120000_in-calendar_VTIMEZONE_tzurl_org.ics
 
b/test/lisp/calendar/icalendar-resources/Asia_Istanbul_20200924T120000_in-calendar_VTIMEZONE_tzurl_org.ics
new file mode 100644
index 0000000000..6425909be6
--- /dev/null
+++ 
b/test/lisp/calendar/icalendar-resources/Asia_Istanbul_20200924T120000_in-calendar_VTIMEZONE_tzurl_org.ics
@@ -0,0 +1,219 @@
+BEGIN:VCALENDAR

+PRODID:manual

+VERSION:2.0

+BEGIN:VTIMEZONE

+TZID:Asia/Istanbul

+TZURL:http://tzurl.org/zoneinfo/Asia/Istanbul

+X-LIC-LOCATION:Asia/Istanbul

+BEGIN:STANDARD

+TZOFFSETFROM:+015552

+TZOFFSETTO:+015656

+TZNAME:IMT

+DTSTART:18800101T000000

+RDATE:18800101T000000

+END:STANDARD

+BEGIN:STANDARD

+TZOFFSETFROM:+015656

+TZOFFSETTO:+0200

+TZNAME:EET

+DTSTART:19101001T000000

+RDATE:19101001T000000

+END:STANDARD

+BEGIN:DAYLIGHT

+TZOFFSETFROM:+0200

+TZOFFSETTO:+0300

+TZNAME:EEST

+DTSTART:19160501T000000

+RDATE:19160501T000000

+RDATE:19200328T000000

+RDATE:19210403T000000

+RDATE:19220326T000000

+RDATE:19240513T000000

+RDATE:19250501T000000

+RDATE:19400630T000000

+RDATE:19401201T000000

+RDATE:19420401T000000

+RDATE:19450402T000000

+RDATE:19460601T000000

+RDATE:19470420T000000

+RDATE:19480418T000000

+RDATE:19490410T000000

+RDATE:19500419T000000

+RDATE:19510422T000000

+RDATE:19620715T000000

+RDATE:19640515T000000

+RDATE:19700503T000000

+RDATE:19710502T000000

+RDATE:19720507T000000

+RDATE:19730603T010000

+RDATE:19740331T020000

+RDATE:19750330T000000

+RDATE:19760601T000000

+RDATE:19770403T000000

+RDATE:19780402T000000

+RDATE:19860330T010000

+RDATE:19870329T010000

+RDATE:19880327T010000

+RDATE:19890326T010000

+RDATE:19900325T010000

+RDATE:19910331T010000

+RDATE:19920329T010000

+RDATE:19930328T010000

+RDATE:19940320T010000

+RDATE:19950326T010000

+RDATE:19960331T010000

+RDATE:19970330T010000

+RDATE:19980329T010000

+RDATE:19990328T010000

+RDATE:20000326T010000

+RDATE:20010325T010000

+RDATE:20020331T010000

+RDATE:20030330T010000

+RDATE:20040328T010000

+RDATE:20050327T010000

+RDATE:20060326T010000

+RDATE:20070325T030000

+RDATE:20080330T030000

+RDATE:20090329T030000

+RDATE:20100328T030000

+RDATE:20110328T030000

+RDATE:20120325T030000

+RDATE:20130331T030000

+RDATE:20140331T030000

+RDATE:20150329T030000

+RDATE:20160327T030000

+END:DAYLIGHT

+BEGIN:STANDARD

+TZOFFSETFROM:+0300

+TZOFFSETTO:+0200

+TZNAME:EET

+DTSTART:19161001T000000

+RDATE:19161001T000000

+RDATE:19201025T000000

+RDATE:19211003T000000

+RDATE:19221008T000000

+RDATE:19241001T000000

+RDATE:19251001T000000

+RDATE:19401005T000000

+RDATE:19410921T000000

+RDATE:19421101T000000

+RDATE:19451008T000000

+RDATE:19461001T000000

+RDATE:19471005T000000

+RDATE:19481003T000000

+RDATE:19491002T000000

+RDATE:19501008T000000

+RDATE:19511008T000000

+RDATE:19621008T000000

+RDATE:19641001T000000

+RDATE:19701004T000000

+RDATE:19711003T000000

+RDATE:19721008T000000

+RDATE:19731104T030000

+RDATE:19741103T050000

+RDATE:19751026T000000

+RDATE:19761031T000000

+RDATE:19771016T000000

+RDATE:19850928T000000

+RDATE:19860928T020000

+RDATE:19870927T020000

+RDATE:19880925T020000

+RDATE:19890924T020000

+RDATE:19900930T020000

+RDATE:19910929T020000

+RDATE:19920927T020000

+RDATE:19930926T020000

+RDATE:19940925T020000

+RDATE:19950924T020000

+RDATE:19961027T020000

+RDATE:19971026T020000

+RDATE:19981025T020000

+RDATE:19991031T020000

+RDATE:20001029T020000

+RDATE:20011028T020000

+RDATE:20021027T020000

+RDATE:20031026T020000

+RDATE:20041031T020000

+RDATE:20051030T020000

+RDATE:20061029T020000

+RDATE:20071028T040000

+RDATE:20081026T040000

+RDATE:20091025T040000

+RDATE:20101031T040000

+RDATE:20111030T040000

+RDATE:20121028T040000

+RDATE:20131027T040000

+RDATE:20141026T040000

+END:STANDARD

+BEGIN:DAYLIGHT

+TZOFFSETFROM:+0300

+TZOFFSETTO:+0400

+TZNAME:+04

+DTSTART:19781015T000000

+RDATE:19781015T000000

+RDATE:19800406T030000

+RDATE:19810329T030000

+RDATE:19820328T030000

+RDATE:19830731T000000

+END:DAYLIGHT

+BEGIN:DAYLIGHT

+TZOFFSETFROM:+0400

+TZOFFSETTO:+0400

+TZNAME:+04

+DTSTART:19790401T030000

+RDATE:19790401T030000

+END:DAYLIGHT

+BEGIN:STANDARD

+TZOFFSETFROM:+0400

+TZOFFSETTO:+0300

+TZNAME:+03

+DTSTART:19791015T000000

+RDATE:19791015T000000

+RDATE:19801013T000000

+RDATE:19811012T000000

+RDATE:19821011T000000

+RDATE:19831002T000000

+END:STANDARD

+BEGIN:DAYLIGHT

+TZOFFSETFROM:+0300

+TZOFFSETTO:+0300

+TZNAME:EEST

+DTSTART:19850420T000000

+RDATE:19850420T000000

+RDATE:20151025T040000

+END:DAYLIGHT

+BEGIN:STANDARD

+TZOFFSETFROM:+0200

+TZOFFSETTO:+0200

+TZNAME:EET

+DTSTART:20070101T000000

+RDATE:20070101T000000

+RDATE:20110327T030000

+RDATE:20140330T030000

+END:STANDARD

+BEGIN:STANDARD

+TZOFFSETFROM:+0300

+TZOFFSETTO:+0200

+DTSTART:20151108T040000

+RDATE:20151108T040000

+END:STANDARD

+BEGIN:STANDARD

+TZOFFSETFROM:+0300

+TZOFFSETTO:+0300

+TZNAME:+03

+DTSTART:20160907T000000

+RDATE:20160907T000000

+END:STANDARD

+END:VTIMEZONE

+BEGIN:VEVENT

+CLASS:PUBLIC

+DTSTART;TZID=Asia/Istanbul:20200924T120000

+DTEND;TZID=Asia/Istanbul:20200924T130000

+UID:Asia_Istanbul_20200924T120000_in-calendar_VTIMEZONE_tzurl_org.ics

+DTSTAMP:20190127T140400

+DESCRIPTION:date 2020-09-24, Istanbul local time 12:00 UTC+3, in-calendar

+  VTIMEZONE as returned by http://tzurl.org/zoneinfo/Asia/Istanbul

+SUMMARY:date 2020-09-24, Istanbul local time 12:00 UTC+3, in-calendar

+  VTIMEZONE as returned by http://tzurl.org/zoneinfo/Asia/Istanbul

+END:VEVENT

+END:VCALENDAR

diff --git 
a/test/lisp/calendar/icalendar-resources/Asia_Shanghai_20200916T070000_in-calendar_VTIMEZONE_multi_DAYLIGHT_STANDARD.ics
 
b/test/lisp/calendar/icalendar-resources/Asia_Shanghai_20200916T070000_in-calendar_VTIMEZONE_multi_DAYLIGHT_STANDARD.ics
new file mode 100644
index 0000000000..26dd3cbfb8
--- /dev/null
+++ 
b/test/lisp/calendar/icalendar-resources/Asia_Shanghai_20200916T070000_in-calendar_VTIMEZONE_multi_DAYLIGHT_STANDARD.ics
@@ -0,0 +1,116 @@
+BEGIN:VCALENDAR

+PRODID:manual

+VERSION:2.0

+CALSCALE:GREGORIAN

+METHOD:PUBLISH

+BEGIN:VTIMEZONE

+TZID:Asia/Shanghai

+X-LIC-LOCATION:Asia/Shanghai

+BEGIN:STANDARD

+TZNAME:CST

+DTSTART:19411001T000000

+TZOFFSETFROM:+0900

+TZOFFSETTO:+0800

+END:STANDARD

+BEGIN:DAYLIGHT

+TZNAME:CDT

+DTSTART:19860504T000000

+TZOFFSETFROM:+0800

+TZOFFSETTO:+0900

+END:DAYLIGHT

+BEGIN:STANDARD

+TZNAME:CST

+DTSTART:19860914T000000

+TZOFFSETFROM:+0900

+TZOFFSETTO:+0800

+END:STANDARD

+BEGIN:DAYLIGHT

+TZNAME:CDT

+DTSTART:19870412T000000

+TZOFFSETFROM:+0800

+TZOFFSETTO:+0900

+END:DAYLIGHT

+BEGIN:STANDARD

+TZNAME:CST

+DTSTART:19870913T000000

+TZOFFSETFROM:+0900

+TZOFFSETTO:+0800

+END:STANDARD

+BEGIN:DAYLIGHT

+TZNAME:CDT

+DTSTART:19880410T000000

+TZOFFSETFROM:+0800

+TZOFFSETFROM:+0800

+TZOFFSETTO:+0900

+END:DAYLIGHT

+BEGIN:STANDARD

+TZNAME:CST

+DTSTART:19880911T000000

+TZOFFSETFROM:+0900

+TZOFFSETTO:+0800

+END:STANDARD

+BEGIN:DAYLIGHT

+TZNAME:CDT

+DTSTART:19890416T000000

+TZOFFSETFROM:+0800

+TZOFFSETTO:+0900

+END:DAYLIGHT

+BEGIN:STANDARD

+TZNAME:CST

+DTSTART:19890917T000000

+TZOFFSETFROM:+0900

+TZOFFSETTO:+0800

+END:STANDARD

+BEGIN:DAYLIGHT

+TZNAME:CDT

+DTSTART:19900415T000000

+TZOFFSETFROM:+0800

+TZOFFSETTO:+0900

+END:DAYLIGHT

+BEGIN:STANDARD

+TZNAME:CST

+DTSTART:19900916T000000

+TZOFFSETFROM:+0900

+TZOFFSETTO:+0800

+END:STANDARD

+BEGIN:DAYLIGHT

+TZNAME:CDT

+DTSTART:19910414T000000

+TZOFFSETFROM:+0800

+TZOFFSETTO:+0900

+END:DAYLIGHT

+BEGIN:STANDARD

+TZNAME:CST

+DTSTART:19910915T000000

+TZOFFSETFROM:+0900

+TZOFFSETTO:+0800

+END:STANDARD

+END:VTIMEZONE

+BEGIN:VTIMEZONE

+TZID:Etc/UTC

+X-LIC-LOCATION:Etc/UTC

+BEGIN:STANDARD

+TZNAME:UTC

+DTSTART:19700101T000000

+TZOFFSETFROM:+0000

+TZOFFSETTO:+0000

+END:STANDARD

+END:VTIMEZONE

+BEGIN:VEVENT

+CLASS:PUBLIC

+DTSTART;TZID=Asia/Shanghai:20200916T070000

+DTEND;TZID=Asia/Shanghai:20200916T080000

+UID:Asia_Shanghai_20200916T070000_in-calendar_VTIMEZONE_multi_DAYLIGHT_STANDARD.ics

+DTSTAMP:20190127T140400

+DESCRIPTION:date 2020-09-16, Shanghai local time 07:00 UTC+8, in-calendar

+  VTIMEZONE with multiple DAYLIGHT and STANDARD sub-components, cf.

+ https://techcommunity.microsoft.com/t5/office-365/import-ics-to-office-365

+ -calendar-but-the-event-time-is-wrong/td-p/215332

+SUMMARY:date 2020-09-16, Shanghai local time 07:00 UTC+8, in-calendar

+  VTIMEZONE with multiple DAYLIGHT and STANDARD sub-components, cf.

+ https://techcommunity.microsoft.com/t5/office-365/import-ics-to-office-365

+ -calendar-but-the-event-time-is-wrong/td-p/215332

+END:VEVENT

+END:VCALENDAR

+

+ 

diff --git 
a/test/lisp/calendar/icalendar-resources/Asia_Shanghai_20200916T070000_in-calendar_VTIMEZONE_tzurl_org.ics
 
b/test/lisp/calendar/icalendar-resources/Asia_Shanghai_20200916T070000_in-calendar_VTIMEZONE_tzurl_org.ics
new file mode 100644
index 0000000000..ccd39bd114
--- /dev/null
+++ 
b/test/lisp/calendar/icalendar-resources/Asia_Shanghai_20200916T070000_in-calendar_VTIMEZONE_tzurl_org.ics
@@ -0,0 +1,65 @@
+BEGIN:VCALENDAR

+PRODID:manual

+VERSION:2.0

+BEGIN:VTIMEZONE

+TZID:Asia/Shanghai

+TZURL:http://tzurl.org/zoneinfo/Asia/Shanghai

+X-LIC-LOCATION:Asia/Shanghai

+BEGIN:STANDARD

+TZOFFSETFROM:+080543

+TZOFFSETTO:+0800

+TZNAME:CST

+DTSTART:19010101T000000

+RDATE:19010101T000000

+END:STANDARD

+BEGIN:DAYLIGHT

+TZOFFSETFROM:+0800

+TZOFFSETTO:+0900

+TZNAME:CDT

+DTSTART:19400601T000000

+RDATE:19400601T000000

+RDATE:19410315T000000

+RDATE:19420131T000000

+RDATE:19460515T000000

+RDATE:19470415T000000

+RDATE:19480501T000000

+RDATE:19490501T000000

+RDATE:19860504T020000

+RDATE:19870412T020000

+RDATE:19880417T020000

+RDATE:19890416T020000

+RDATE:19900415T020000

+RDATE:19910414T020000

+END:DAYLIGHT

+BEGIN:STANDARD

+TZOFFSETFROM:+0900

+TZOFFSETTO:+0800

+TZNAME:CST

+DTSTART:19401012T235959

+RDATE:19401012T235959

+RDATE:19411101T235959

+RDATE:19450901T235959

+RDATE:19460930T235959

+RDATE:19471031T235959

+RDATE:19480930T235959

+RDATE:19490528T000000

+RDATE:19860914T020000

+RDATE:19870913T020000

+RDATE:19880911T020000

+RDATE:19890917T020000

+RDATE:19900916T020000

+RDATE:19910915T020000

+END:STANDARD

+END:VTIMEZONE

+BEGIN:VEVENT

+CLASS:PUBLIC

+DTSTART;TZID=Asia/Shanghai:20200916T070000

+DTEND;TZID=Asia/Shanghai:20200916T080000

+UID:Asia_Shanghai_20200916T070000_in-calendar_VTIMEZONE_tzurl_org.ics

+DTSTAMP:20190127T140400

+DESCRIPTION:date 2020-09-16, Shanghai local time 07:00 UTC+8, in-calendar

+  VTIMEZONE as returned by http://tzurl.org/zoneinfo/Asia/Shanghai

+SUMMARY:date 2020-09-16, Shanghai local time 07:00 UTC+8, in-calendar

+  VTIMEZONE as returned by http://tzurl.org/zoneinfo/Asia/Shanghai

+END:VEVENT

+END:VCALENDAR


reply via email to

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