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>
This commit is contained in:
Lukas Fleischer 2015-02-23 10:43:54 +01:00
parent 4efa3f3fbf
commit 1ea97795be
6 changed files with 78 additions and 8 deletions

View File

@ -1054,6 +1054,7 @@ long get_item_time(long);
int get_item_hour(long); int get_item_hour(long);
int get_item_min(long); int get_item_min(long);
long date2sec(struct date, unsigned, unsigned); long date2sec(struct date, unsigned, unsigned);
long utcdate2sec(struct date, unsigned, unsigned);
char *date_sec2date_str(long, const char *); char *date_sec2date_str(long, const char *);
void date_sec2date_fmt(long, const char *, char *); void date_sec2date_fmt(long, const char *, char *);
long date_sec_change(long, int, int); long date_sec_change(long, int, int);

View File

@ -477,7 +477,7 @@ ical_chk_header(FILE * fd, char *buf, char *lstore, unsigned *lineno,
* where DATE is 'YYYYMMDD' and TIME is 'HHMMSS'. * where DATE is 'YYYYMMDD' and TIME is 'HHMMSS'.
* The time and 'T' separator are optional (in the case of an day-long event). * 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 * (no time is given, meaning it is an all-day event), or an appointment
* (time is given). * (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) 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; struct date date;
unsigned hour, min; unsigned hour, min, sec;
long datelong; char c;
long datelong ;
int format; int format;
format = sscanf(datestr, "%04u%02u%02uT%02u%02u", format = sscanf(datestr, "%04u%02u%02uT%02u%02u%02u%c",
&date.yyyy, &date.mm, &date.dd, &hour, &min); &date.yyyy, &date.mm, &date.dd, &hour, &min, &sec, &c);
if (format == FORMAT_DATE) { if (format == FORMAT_DATE) {
if (type) if (type)
*type = EVENT; *type = EVENT;
datelong = date2sec(date, 0, 0); datelong = date2sec(date, 0, 0);
} else if (format == FORMAT_DATETIME) { } else if (format == FORMAT_DATETIME || format == FORMAT_DATETIMEZ) {
if (type) if (type)
*type = APPOINTMENT; *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 { } else {
datelong = NOTFOUND; datelong = NOTFOUND;
} }

View File

@ -386,6 +386,31 @@ long date2sec(struct date day, unsigned hour, unsigned min)
return t; 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. */ /* Return a string containing the date, given a date in seconds. */
char *date_sec2date_str(long sec, const char *datefmt) char *date_sec2date_str(long sec, const char *datefmt)
{ {

View File

@ -48,6 +48,7 @@ TESTS = \
ical-004.sh \ ical-004.sh \
ical-005.sh \ ical-005.sh \
ical-006.sh \ ical-006.sh \
ical-007.sh \
next-001.sh \ next-001.sh \
search-001.sh \ search-001.sh \
bug-002.sh \ bug-002.sh \
@ -111,4 +112,5 @@ EXTRA_DIST = \
data/ical-004.ical \ data/ical-004.ical \
data/ical-005.ical \ data/ical-005.ical \
data/ical-006.ical \ data/ical-006.ical \
data/ical-007.ical \
data/todo data/todo

13
test/data/ical-007.ical Normal file
View File

@ -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

24
test/ical-007.sh Executable file
View File

@ -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