From 1ea97795be8f9b8d63c5266ac7ea4889c3224a4d Mon Sep 17 00:00:00 2001
From: Lukas Fleischer <calcurse@cryptocrack.de>
Date: Mon, 23 Feb 2015 10:43:54 +0100
Subject: Add support for UTC times in iCal imports

According to the iCal standard (4.3.12 Time):

    UTC time, or absolute time, is identified by a LATIN CAPITAL LETTER
    Z suffix character (US-ASCII decimal 90), the UTC designator,
    appended to the time value.

Parse such time values properly when importing iCal files.

Fixes GitHub issue #3.

Signed-off-by: Lukas Fleischer <calcurse@cryptocrack.de>
---
 src/calcurse.h          |  1 +
 src/ical.c              | 21 +++++++++++++--------
 src/utils.c             | 25 +++++++++++++++++++++++++
 test/Makefile.am        |  2 ++
 test/data/ical-007.ical | 13 +++++++++++++
 test/ical-007.sh        | 24 ++++++++++++++++++++++++
 6 files changed, 78 insertions(+), 8 deletions(-)
 create mode 100644 test/data/ical-007.ical
 create mode 100755 test/ical-007.sh

diff --git a/src/calcurse.h b/src/calcurse.h
index 6700d5b..6e686e4 100644
--- a/src/calcurse.h
+++ b/src/calcurse.h
@@ -1054,6 +1054,7 @@ long get_item_time(long);
 int get_item_hour(long);
 int get_item_min(long);
 long date2sec(struct date, unsigned, unsigned);
+long utcdate2sec(struct date, unsigned, unsigned);
 char *date_sec2date_str(long, const char *);
 void date_sec2date_fmt(long, const char *, char *);
 long date_sec_change(long, int, int);
diff --git a/src/ical.c b/src/ical.c
index 4d1edab..e3ddc07 100644
--- a/src/ical.c
+++ b/src/ical.c
@@ -477,7 +477,7 @@ ical_chk_header(FILE * fd, char *buf, char *lstore, unsigned *lineno,
  * where DATE is 'YYYYMMDD' and TIME is 'HHMMSS'.
  * The time and 'T' separator are optional (in the case of an day-long event).
  *
- * Optionnaly, if the type pointer is given, specify if it is an event
+ * Optionally, if the type pointer is given, specify if it is an event
  * (no time is given, meaning it is an all-day event), or an appointment
  * (time is given).
  *
@@ -485,22 +485,27 @@ ical_chk_header(FILE * fd, char *buf, char *lstore, unsigned *lineno,
  */
 static long ical_datetime2long(char *datestr, ical_vevent_e * type)
 {
-	const int NOTFOUND = 0, FORMAT_DATE = 3, FORMAT_DATETIME = 5;
+	const int NOTFOUND = 0, FORMAT_DATE = 3, FORMAT_DATETIME = 6,
+	      FORMAT_DATETIMEZ = 7;
 	struct date date;
-	unsigned hour, min;
-	long datelong;
+	unsigned hour, min, sec;
+	char c;
+	long datelong ;
 	int format;
 
-	format = sscanf(datestr, "%04u%02u%02uT%02u%02u",
-			&date.yyyy, &date.mm, &date.dd, &hour, &min);
+	format = sscanf(datestr, "%04u%02u%02uT%02u%02u%02u%c",
+			&date.yyyy, &date.mm, &date.dd, &hour, &min, &sec, &c);
 	if (format == FORMAT_DATE) {
 		if (type)
 			*type = EVENT;
 		datelong = date2sec(date, 0, 0);
-	} else if (format == FORMAT_DATETIME) {
+	} else if (format == FORMAT_DATETIME || format == FORMAT_DATETIMEZ) {
 		if (type)
 			*type = APPOINTMENT;
-		datelong = date2sec(date, hour, min);
+		if (format == FORMAT_DATETIMEZ && c == 'Z')
+			datelong = utcdate2sec(date, hour, min);
+		else
+			datelong = date2sec(date, hour, min);
 	} else {
 		datelong = NOTFOUND;
 	}
diff --git a/src/utils.c b/src/utils.c
index b52de74..c7dc61c 100644
--- a/src/utils.c
+++ b/src/utils.c
@@ -386,6 +386,31 @@ long date2sec(struct date day, unsigned hour, unsigned min)
 	return t;
 }
 
+time_t
+utcdate2sec(struct date day, unsigned hour, unsigned min)
+{
+	char *tz;
+	time_t t;
+
+	tz = getenv("TZ");
+	if (tz)
+		tz = mem_strdup(tz);
+	setenv("TZ", "", 1);
+	tzset();
+
+	t = date2sec(day, hour, min);
+
+	if (tz) {
+		setenv("TZ", tz, 1);
+		mem_free(tz);
+	} else {
+	    unsetenv("TZ");
+	}
+	tzset();
+
+	return t;
+}
+
 /* Return a string containing the date, given a date in seconds. */
 char *date_sec2date_str(long sec, const char *datefmt)
 {
diff --git a/test/Makefile.am b/test/Makefile.am
index fe205a2..efc3d12 100644
--- a/test/Makefile.am
+++ b/test/Makefile.am
@@ -48,6 +48,7 @@ TESTS = \
 	ical-004.sh \
 	ical-005.sh \
 	ical-006.sh \
+	ical-007.sh \
 	next-001.sh \
 	search-001.sh \
 	bug-002.sh \
@@ -111,4 +112,5 @@ EXTRA_DIST = \
 	data/ical-004.ical \
 	data/ical-005.ical \
 	data/ical-006.ical \
+	data/ical-007.ical \
 	data/todo
diff --git a/test/data/ical-007.ical b/test/data/ical-007.ical
new file mode 100644
index 0000000..e46c3fb
--- /dev/null
+++ b/test/data/ical-007.ical
@@ -0,0 +1,13 @@
+BEGIN:VCALENDAR
+VERSION:2.0
+BEGIN:VEVENT
+SUMMARY:Local time
+DTSTART:20150223T110000
+DURATION:PT1H
+END:VEVENT
+BEGIN:VEVENT
+SUMMARY:UTC
+DTSTART:20150223T110000Z
+DURATION:PT1H
+END:VEVENT
+END:VCALENDAR
diff --git a/test/ical-007.sh b/test/ical-007.sh
new file mode 100755
index 0000000..8f0f472
--- /dev/null
+++ b/test/ical-007.sh
@@ -0,0 +1,24 @@
+#!/bin/sh
+
+. "${TEST_INIT:-./test-init.sh}"
+
+if [ "$1" = 'actual' ]; then
+  mkdir .calcurse || exit 1
+  cp "$DATA_DIR/conf" .calcurse || exit 1
+  TZ="America/New_York" "$CALCURSE" -D "$PWD/.calcurse" \
+    -i "$DATA_DIR/ical-007.ical"
+  "$CALCURSE" -D "$PWD/.calcurse" -s2015-02-23
+  rm -rf .calcurse || exit 1
+elif [ "$1" = 'expected' ]; then
+  cat <<EOD
+Import process report: 0018 lines read
+2 apps / 0 events / 0 todos / 0 skipped
+02/23/15:
+ - 06:00 -> 07:00
+	UTC
+ - 11:00 -> 12:00
+	Local time
+EOD
+else
+  ./run-test "$0"
+fi
-- 
cgit v1.2.3-70-g09d2