Index: libabac/abac_xml.c
===================================================================
--- libabac/abac_xml.c	(revision 373169a552222eedf6ed5b71aa68ec98088757b9)
+++ libabac/abac_xml.c	(revision d9506ae5fec8f38b1f5afd7efc0d191f9a2e45b4)
@@ -448,42 +448,86 @@
 }
 
+static int is_time_separator(char *c) {
+    return (*c == '\0' || *c == 'T');
+}
+
+static int is_tz_separator(char *c) {
+    return (*c == '\0' || *c == 'Z' || *c == '-' || *c=='+' );
+}
+
 static int parse_ISO_time(xmlChar *expires, struct tm *out) {
-    /* Parse an ISO 8601 formatted datetime.  Strptime isn't quite smart enough
-     * to handle this without help.  Use the length of the expires string to
-     * guess the strptime format that will work for that sort of ISO 8601 time
-     * and call strptime with it.  If there are errors in parsing return 0,
-     * else return 1. */
-    size_t len = strlen((char *) expires);
-
-    if ( len == 19) {
-	/* format should be 2014-02-02T14:26:40  unnanotated Zulu time*/
-	fprintf(stderr, "Unannotated %s\n", expires);
-	return ( strptime((char *) expires, "%FT%T", out) != NULL );
-    } else if ( len == 20 && expires[19] == 'Z') {
-	/* format should be 2014-02-02T14:26:40Z annotated Zulu time */
-	fprintf(stderr, "annotated Z %s\n", expires);
-	return ( strptime((char *) expires, "%FT%TZ", out) != NULL );
-    } else if ( len == 25) {
-	/* format should be 2014-02-02T14:26:40+00:00 explicit timezone */
-	/* strptime does not cope with (of all things) colons in the zulu
-	 * offset.  This branch copies that into a clean string and parses it.
-	 */
-	char tmp[25]; /* 24 letters and a NUL */
-	int i = 0;    /* scratch */
-	for ( i = 0; i < 22; i++ )
-	    tmp[i] = expires[i];
-	for ( i = 23; i < 25; i++ )
-	    tmp[i] = expires[i];
-	tmp[24] = 0;
-
-	fprintf(stderr, "annotated %s %s\n", expires, tmp);
-	return ( strptime((char *) tmp, "%FT%T%z", out) != NULL );
+    /* Parse an ISO 8601 formatted datetime.  Strptime isn't nearly smart
+     * enough to handle this without help.  ISO 8061 has a demented range of
+     * valid formats.  We parse this into "basic" format dates and times,
+     * separated by the T that splits a timestamp.  A "basic" format date only
+     * has the digits necessary for that representation.  Once the whole thing
+     * is cannonicalized, we call strptime on it.
+     *
+     * Return 0 on error.
+     */
+
+#define DATELEN 8
+#define TIMELEN 6
+#define ZONELEN 5
+/* 1 for the terminal \0 */
+#define PARSABLELEN DATELEN + TIMELEN + ZONELEN + 1
+
+    char *timeSep = NULL;
+    char *tzSep = NULL;
+    char *c = NULL;
+    int tot = 0;
+    char parsable[PARSABLELEN];
+
+    for (timeSep = (char *) expires; !is_time_separator(timeSep); timeSep++)
+	;
+    if (*timeSep == '\0') return 0;
+    for (tzSep = timeSep+1; !is_tz_separator(tzSep); tzSep++)
+	;
+
+    /* Found our markers, parse it into the parsable array */
+    /* Copy exactly DATELEN digits into parsable (%Y%m%d). */
+    tot = 0;
+    for ( c = (char *) expires; c != timeSep; c++ ) {
+	if (isdigit(*c)) {
+	    if ( tot >= DATELEN) return 0;
+	    parsable[tot++] = *c;
+	}
+    }
+    if ( tot != DATELEN ) return 0;
+
+    /* Copy exactly TIMELEN digits into parsable (%H%M%S) after the date. */
+    tot = 0;
+    for ( c = (char *) timeSep+1; c != tzSep; c++ ) {
+	if (isdigit(*c)) {
+	    /* Only copy the first TIMELEN.  If the stamp includes fractions of
+	     * a second, ignore 'em. */
+	    if ( tot >= TIMELEN) break;
+	    parsable[DATELEN + tot++] = *c;
+	}
+    }
+    if ( tot != TIMELEN ) return 0;
+    /* null-terminate parsable */
+    parsable[DATELEN+TIMELEN] = '\0';
+
+    /* If no timezone is given or Z is given, copy a GMT offset into parsable,
+     * otherwise parse ZONELEN digits or +/- into parsable after the DATE and
+     * TIME. */
+    if ( *tzSep == 'Z' || *tzSep == '\0' ) {
+	strncat(parsable, "+0000", ZONELEN);
     }
     else {
-	/* No idea what this is but this routine cannot parse it  ... */
-	return 0;
-    }
-}
-
+	tot = 0;
+	for ( c = (char *) tzSep; *c != '\0'; c++ ) {
+	    if (isdigit(*c) || *c == '+' || *c == '-') {
+		if ( tot >= ZONELEN) return 0;
+		parsable[DATELEN + TIMELEN + tot++] = *c;
+	    }
+	}
+	if ( tot != ZONELEN ) return 0;
+    }
+    /* null-terminate parsable */
+    parsable[DATELEN+TIMELEN+ZONELEN] = '\0';
+    return ( strptime(parsable, "%Y%m%d%H%M%S%z", out) != NULL );
+}
 
 /* Parse the content of the expires field and compare it to the time passed in
