dovecot-2.2: Adds ISO8601/RFC3339 date format parsing and constr...
dovecot at dovecot.org
dovecot at dovecot.org
Sat Jun 2 19:02:27 EEST 2012
details: http://hg.dovecot.org/dovecot-2.2/rev/21d67121985a
changeset: 14586:21d67121985a
user: Stephan Bosch <stephan at rename-it.nl>
date: Sat Jun 02 16:55:21 2012 +0300
description:
Adds ISO8601/RFC3339 date format parsing and construction support.
Interface is somewhat based on message date parser in src/lib-mail, but it
also provides access to struct tm.
diffstat:
src/lib/Makefile.am | 3 +
src/lib/iso8601-date.c | 305 ++++++++++++++++++++++++++++++++++++++++++++
src/lib/iso8601-date.h | 21 +++
src/lib/test-iso8601-date.c | 145 ++++++++++++++++++++
src/lib/test-lib.c | 1 +
src/lib/test-lib.h | 1 +
6 files changed, 476 insertions(+), 0 deletions(-)
diffs (truncated from 532 to 300 lines):
diff -r 8bb23c123ea3 -r 21d67121985a src/lib/Makefile.am
--- a/src/lib/Makefile.am Tue May 22 23:19:16 2012 +0300
+++ b/src/lib/Makefile.am Sat Jun 02 16:55:21 2012 +0300
@@ -52,6 +52,7 @@
ipwd.c \
iostream.c \
iostream-rawlog.c \
+ iso8601-date.c \
istream.c \
istream-base64-encoder.c \
istream-concat.c \
@@ -171,6 +172,7 @@
iostream-private.h \
iostream-rawlog.h \
iostream-rawlog-private.h \
+ iso8601-date.h \
istream.h \
istream-base64-encoder.h \
istream-concat.h \
@@ -252,6 +254,7 @@
test-crc32.c \
test-hash-format.c \
test-hex-binary.c \
+ test-iso8601-date.c \
test-istream-base64-encoder.c \
test-istream-concat.c \
test-istream-crlf.c \
diff -r 8bb23c123ea3 -r 21d67121985a src/lib/iso8601-date.c
--- /dev/null Thu Jan 01 00:00:00 1970 +0000
+++ b/src/lib/iso8601-date.c Sat Jun 02 16:55:21 2012 +0300
@@ -0,0 +1,305 @@
+#include "lib.h"
+#include "utc-offset.h"
+#include "utc-mktime.h"
+#include "iso8601-date.h"
+
+#include <ctype.h>
+
+/* RFC3339/ISO8601 date-time syntax
+
+ date-fullyear = 4DIGIT
+ date-month = 2DIGIT ; 01-12
+ date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on
+ ; month/year
+ time-hour = 2DIGIT ; 00-23
+ time-minute = 2DIGIT ; 00-59
+ time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second
+ ; rules
+ time-secfrac = "." 1*DIGIT
+ time-numoffset = ("+" / "-") time-hour ":" time-minute
+ time-offset = "Z" / time-numoffset
+
+ partial-time = time-hour ":" time-minute ":" time-second [time-secfrac]
+ full-date = date-fullyear "-" date-month "-" date-mday
+ full-time = partial-time time-offset
+
+ date-time = full-date "T" full-time
+ */
+
+struct iso8601_date_parser {
+ const unsigned char *cur, *end;
+
+ struct tm tm;
+ int timezone_offset;
+};
+
+static inline int
+iso8601_date_parse_number(struct iso8601_date_parser *parser,
+ int digits, int *number_r)
+{
+ int i;
+
+ if (parser->cur >= parser->end || !i_isdigit(parser->cur[0]))
+ return 0;
+
+ *number_r = parser->cur[0] - '0';
+ parser->cur++;
+
+ for (i=0; i < digits-1; i++) {
+ if (parser->cur >= parser->end || !i_isdigit(parser->cur[0]))
+ return -1;
+ *number_r = ((*number_r) * 10) + parser->cur[0] - '0';
+ parser->cur++;
+ }
+ return 1;
+}
+
+static int
+iso8601_date_parse_secfrac(struct iso8601_date_parser *parser)
+{
+ /* time-secfrac = "." 1*DIGIT
+
+ NOTE: Currently not applied anywhere, so fraction is just skipped.
+ */
+
+ /* "." */
+ if (parser->cur >= parser->end || parser->cur[0] != '.')
+ return 0;
+ parser->cur++;
+
+ /* 1DIGIT */
+ if (parser->cur >= parser->end || !i_isdigit(parser->cur[0]))
+ return -1;
+ parser->cur++;
+
+ /* *DIGIT */
+ while (parser->cur < parser->end && i_isdigit(parser->cur[0]))
+ parser->cur++;
+ return 1;
+}
+
+static int is08601_date_parse_time_offset(struct iso8601_date_parser *parser)
+{
+ int tz_sign = 1, tz_hour = 0, tz_min = 0;
+
+ /* time-offset = "Z" / time-numoffset
+ time-numoffset = ("+" / "-") time-hour ":" time-minute
+ time-hour = 2DIGIT ; 00-23
+ time-minute = 2DIGIT ; 00-59
+ */
+
+ if (parser->cur >= parser->end)
+ return 0;
+
+ /* time-offset = "Z" / time-numoffset */
+ switch (parser->cur[0]) {
+ case '-':
+ tz_sign = -1;
+
+ case '+':
+ parser->cur++;
+
+ /* time-hour = 2DIGIT */
+ if (iso8601_date_parse_number(parser, 2, &tz_hour) <= 0)
+ return -1;
+ if (tz_hour > 23)
+ return -1;
+
+ /* ":" */
+ if (parser->cur >= parser->end || parser->cur[0] != ':')
+ return -1;
+ parser->cur++;
+
+ /* time-minute = 2DIGIT */
+ if (iso8601_date_parse_number(parser, 2, &tz_min) <= 0)
+ return -1;
+ if (tz_min > 59)
+ return -1;
+ break;
+ case 'Z':
+ case 'z':
+ parser->cur++;
+ break;
+ default:
+ return -1;
+ }
+
+ parser->timezone_offset = tz_sign*(tz_hour*60 + tz_min);
+ return 1;
+}
+
+static int is08601_date_parse_full_time(struct iso8601_date_parser *parser)
+{
+ /* full-time = partial-time time-offset
+ partial-time = time-hour ":" time-minute ":" time-second [time-secfrac]
+ time-hour = 2DIGIT ; 00-23
+ time-minute = 2DIGIT ; 00-59
+ time-second = 2DIGIT ; 00-58, 00-59, 00-60 based on leap second
+ ; rules
+ */
+
+ /* time-hour = 2DIGIT */
+ if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_hour) <= 0)
+ return -1;
+
+ /* ":" */
+ if (parser->cur >= parser->end || parser->cur[0] != ':')
+ return -1;
+ parser->cur++;
+
+ /* time-minute = 2DIGIT */
+ if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_min) <= 0)
+ return -1;
+
+ /* ":" */
+ if (parser->cur >= parser->end || parser->cur[0] != ':')
+ return -1;
+ parser->cur++;
+
+ /* time-second = 2DIGIT */
+ if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_sec) <= 0)
+ return -1;
+
+ /* [time-secfrac] */
+ if (iso8601_date_parse_secfrac(parser) < 0)
+ return -1;
+
+ /* time-offset */
+ if (is08601_date_parse_time_offset(parser) <= 0)
+ return -1;
+ return 1;
+}
+
+static int is08601_date_parse_full_date(struct iso8601_date_parser *parser)
+{
+ /* full-date = date-fullyear "-" date-month "-" date-mday
+ date-fullyear = 4DIGIT
+ date-month = 2DIGIT ; 01-12
+ date-mday = 2DIGIT ; 01-28, 01-29, 01-30, 01-31 based on
+ ; month/year
+ */
+
+ /* date-fullyear = 4DIGIT */
+ if (iso8601_date_parse_number(parser, 4, &parser->tm.tm_year) <= 0)
+ return -1;
+ if (parser->tm.tm_year < 1900)
+ return -1;
+ parser->tm.tm_year -= 1900;
+
+ /* "-" */
+ if (parser->cur >= parser->end || parser->cur[0] != '-')
+ return -1;
+ parser->cur++;
+
+ /* date-month = 2DIGIT */
+ if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_mon) <= 0)
+ return -1;
+ parser->tm.tm_mon -= 1;
+
+ /* "-" */
+ if (parser->cur >= parser->end || parser->cur[0] != '-')
+ return -1;
+ parser->cur++;
+
+ /* time-second = 2DIGIT */
+ if (iso8601_date_parse_number(parser, 2, &parser->tm.tm_mday) <= 0)
+ return -1;
+ return 1;
+}
+
+static int iso8601_date_parse_date_time(struct iso8601_date_parser *parser)
+{
+ /* date-time = full-date "T" full-time */
+
+ /* full-date */
+ if (is08601_date_parse_full_date(parser) <= 0)
+ return -1;
+
+ /* "T" */
+ if (parser->cur >= parser->end ||
+ (parser->cur[0] != 'T' && parser->cur[0] != 't'))
+ return -1;
+ parser->cur++;
+
+ /* full-time */
+ if (is08601_date_parse_full_time(parser) <= 0)
+ return -1;
+
+ if (parser->cur != parser->end)
+ return -1;
+ return 1;
+}
+
+static bool
+iso8601_date_do_parse(const unsigned char *data, size_t size, struct tm *tm_r,
+ time_t *timestamp_r, int *timezone_offset_r)
+{
+ struct iso8601_date_parser parser;
+ time_t timestamp;
+
+ memset(&parser, 0, sizeof(parser));
+ parser.cur = data;
+ parser.end = data + size;
+
+ if (iso8601_date_parse_date_time(&parser) <= 0)
+ return FALSE;
+
+ parser.tm.tm_isdst = -1;
+ timestamp = utc_mktime(&parser.tm);
+ if (timestamp == (time_t)-1)
+ return FALSE;
+
+ if (timezone_offset_r != NULL)
+ *timezone_offset_r = parser.timezone_offset;
+ if (tm_r != NULL)
+ *tm_r = parser.tm;
+ if (timestamp_r != NULL)
+ *timestamp_r = timestamp - parser.timezone_offset * 60;
+ return TRUE;
+}
+
+bool iso8601_date_parse(const unsigned char *data, size_t size,
+ time_t *timestamp_r, int *timezone_offset_r)
+{
+ return iso8601_date_do_parse(data, size, NULL,
+ timestamp_r, timezone_offset_r);
+}
+
+bool iso8601_date_parse_tm(const unsigned char *data, size_t size,
+ struct tm *tm_r, int *timezone_offset_r)
More information about the dovecot-cvs
mailing list