From 712b0bb4b575b45759953119c7ddf6e91f43fad7 Mon Sep 17 00:00:00 2001 From: Paul Beckingham Date: Thu, 2 Jan 2014 00:45:16 -0500 Subject: [PATCH] ISO8601 - Merged libexpr ISO8601 code. --- src/CMakeLists.txt | 1 + src/ISO8601.cpp | 880 ++++++++++++++++++++++++++++++++++++++++++++ src/ISO8601.h | 110 ++++++ test/CMakeLists.txt | 2 +- test/iso8601d.t.cpp | 315 ++++++++++++++++ test/iso8601p.t.cpp | 122 ++++++ 6 files changed, 1429 insertions(+), 1 deletion(-) create mode 100644 src/ISO8601.cpp create mode 100644 src/ISO8601.h create mode 100644 test/iso8601d.t.cpp create mode 100644 test/iso8601p.t.cpp diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a5e7a3ac0..21dcdbcbf 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -18,6 +18,7 @@ set (task_SRCS A3.cpp A3.h E9.cpp E9.h File.cpp File.h Hooks.cpp Hooks.h + ISO8601.cpp ISO8601.h JSON.cpp JSON.h LRParser.cpp LRParser.h Msg.cpp Msg.h diff --git a/src/ISO8601.cpp b/src/ISO8601.cpp new file mode 100644 index 000000000..fe610118f --- /dev/null +++ b/src/ISO8601.cpp @@ -0,0 +1,880 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2006 - 2014, Paul Beckingham, Federico Hernandez. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// http://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include + +//////////////////////////////////////////////////////////////////////////////// +ISO8601d::ISO8601d () +{ + clear (); +} + +//////////////////////////////////////////////////////////////////////////////// +ISO8601d::~ISO8601d () +{ +} + +//////////////////////////////////////////////////////////////////////////////// +ISO8601d::operator time_t () const +{ + return _value; +} + +//////////////////////////////////////////////////////////////////////////////// +// By default, ISO8601d allows ambiguous dates, such as YYYY, YYYYMMDD, HHMMSS. +// These are also valid numbers. Setting ambiguity to false inhibites the +// parsing of these as dates. +void ISO8601d::ambiguity (bool value) +{ + _ambiguity = value; +} + +//////////////////////////////////////////////////////////////////////////////// +// Supported: +// +// result ::= date-ext 'T' time-ext 'Z' # UTC +// | date-ext 'T' time-ext offset-ext # Specified TZ +// | date-ext 'T' time-ext # Local +// | date-ext # Local +// | date 'T' time 'Z' +// | date 'T' time offset-ext +// | date 'T' time +// | date +// | time-ext 'Z' +// | time-ext offset-ext +// | time-ext +// | time 'Z' +// | time offset +// | time +// ; +// +// date-ext ::= ±YYYYY-MM-DD Νot needed +// | ±YYYYY-Www-D Νot needed +// | ±YYYYY-Www Νot needed +// | ±YYYYY-DDD Νot needed +// | YYYY-MM-DD +// | YYYY-DDD +// | YYYY-Www-D +// | YYYY-Www +// ; +// +// date ::= ±YYYYYMMDD Νot needed +// | ±YYYYYWwwD Νot needed +// | ±YYYYYWww Νot needed +// | ±YYYYYDDD Νot needed +// | ±YYYYYMM Νot needed +// | ±YYYYY Νot needed +// | ±YYY Νot needed +// | YYYYMMDD Ambiguous (number) +// | YYYYWwwD +// | YYYYWww +// | YYYYDDD Ambiguous (number) +// | YYYY-MM +// | YYYY Ambiguous (number) +// | YY Ambiguous (number) +// ; +// +// time-ext ::= hh:mm:ss[,ss] +// | hh:mm[,mm] +// | hh[,hh] Ambiguous (number) +// ; +// +// time ::= hhmmss[,ss] Ambiguous (number) +// | hhmm[,mm] Ambiguous (number) +// | hh[,hh] Ambiguous (number) +// ; +// +// time-utc-ext ::= hh:mm[:ss] 'Z' ; +// time-utc ::= hh[mm[ss]] 'Z' ; +// +// offset-ext ::= ±hh[:mm] ; +// offset ::= ±hh[mm] ; +// +// Not yet supported: +// +// recurrence ::= +// | 'R' [n] '/' designated '/' datetime-ext # duration end +// | 'R' [n] '/' designated '/' datetime # duration end +// | 'R' [n] '/' designated # duration +// | 'R' [n] '/' datetime-ext '/' designated # start duration +// | 'R' [n] '/' datetime-ext '/' datetime-ext # start end +// | 'R' [n] '/' datetime '/' designated # start duration +// | 'R' [n] '/' datetime '/' datetime # start end +// ; +// +bool ISO8601d::parse (const std::string& input, std::string::size_type& start) +{ + std::string::size_type i = start; + Nibbler n (input.substr (i)); + + if (parse_date_time_ext (n) || // Most complex first. + parse_date_ext (n) || + parse_time_utc_ext (n) || + parse_time_off_ext (n) || + parse_date_time (n) || + parse_date (n, _ambiguity) || + parse_time_utc (n) || + parse_time_off (n) || + parse_time_ext (n) || // Time last, as it is the most permissive. + parse_time (n, _ambiguity)) + { + // Check the values and determine time_t. + if (validate ()) + { + // Record cursor position. + start = n.cursor (); + + resolve (); + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +void ISO8601d::clear () +{ + _ambiguity = true; + _year = 0; + _month = 0; + _week = 0; + _weekday = 0; + _julian = 0; + _day = 0; + _seconds = 0; + _offset = 0; + _utc = false; + _value = 0; + _default_seconds = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +void ISO8601d::set_default_time (int hours, int minutes, int seconds) +{ + _default_seconds = (hours * 3600) + (minutes * 60) + seconds; +} + +//////////////////////////////////////////////////////////////////////////////// +// date-ext 'T' time-ext 'Z' +// date-ext 'T' time-ext offset-ext +// date-ext 'T' time-ext +bool ISO8601d::parse_date_time_ext (Nibbler& n) +{ + n.save (); + if (parse_date_ext (n)) + { + if (n.skip ('T') && + parse_time_ext (n)) + { + if (n.skip ('Z')) + _utc = true; + else if (parse_off_ext (n)) + ; + + if (! isdigit (n.next ())) + return true; + } + + // Restore date_ext + _year = 0; + _month = 0; + _week = 0; + _weekday = 0; + _julian = 0; + _day = 0; + } + + n.restore (); + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// date 'T' time 'Z' +// date 'T' time offset +// date 'T' time +bool ISO8601d::parse_date_time (Nibbler& n) +{ + Nibbler backup (n); + if (parse_date (n, true)) + { + if (n.skip ('T') && + parse_time (n, true)) + { + if (n.skip ('Z')) + { + _utc = true; + if (!isdigit (n.next ())) + return true; + } + else if (parse_off (n)) + { + if (!isdigit (n.next ())) + return true; + } + + if (!isdigit (n.next ())) + return true; + } + + // Restore date + _year = 0; + _month = 0; + _week = 0; + _weekday = 0; + _julian = 0; + _day = 0; + } + + n = backup; + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// YYYY-MM-DD +// YYYY-DDD +// YYYY-Www-D +// YYYY-Www +bool ISO8601d::parse_date_ext (Nibbler& n) +{ + Nibbler backup (n); + int year; + if (n.getDigit4 (year) && + n.skip ('-')) + { + int month; + int day; + if (n.skip ('W') && + n.getDigit2 (_week)) + { + if (n.skip ('-') && + n.getDigit (_weekday)) + { + } + + _year = year; + if (!isdigit (n.next ())) + return true; + } + else if (n.getDigit3 (_julian)) + { + _year = year; + if (!isdigit (n.next ())) + return true; + } + else if (n.getDigit2 (month) && + n.skip ('-') && + n.getDigit2 (day)) + { + _year = year; + _month = month; + _day = day; + if (!isdigit (n.next ())) + return true; + } + } + + n = backup; + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// YYYYMMDD Ambiguous (number) +// YYYYWwwD +// YYYYWww +// YYYYDDD Ambiguous (number) +// YYYY-MM +bool ISO8601d::parse_date (Nibbler& n, bool ambiguous) +{ + Nibbler backup (n); + int year; + if (n.getDigit4 (year)) + { + int month; + if (n.skip ('W')) + { + int week; + if (n.getDigit2 (week)) + { + _week = week; + + int day; + if (n.getDigit (day)) + _weekday = day; + + _year = year; + if (!isdigit (n.next ())) + return true; + } + } + else if (n.skip ('-')) + { + if (n.getDigit2 (_month)) + { + _year = year; + if (!isdigit (n.next ())) + return true; + } + } + else if (n.getDigit4 (month)) + { + _year = year; + _month = month / 100; + _day = month % 100; + if (!isdigit (n.next ())) + return true; + } + else if (ambiguous && n.getDigit3 (_julian)) + { + _year = year; + if (!isdigit (n.next ())) + return true; + } + } + + n = backup; + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// ±hh[:mm] +bool ISO8601d::parse_off_ext (Nibbler& n) +{ + Nibbler backup (n); + std::string sign; + if (n.getN (1, sign)) + { + if (sign == "+" || sign == "-") + { + int offset; + int hh; + int mm; + if (n.getDigit2 (hh) && + !n.getDigit (mm)) + { + offset = hh * 3600; + if (n.skip (':') && + n.getDigit2 (mm)) + offset += mm * 60; + + _offset = (sign == "-") ? -offset : offset; + if (!isdigit (n.next ())) + return true; + } + } + } + + n = backup; + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// ±hh[mm] +bool ISO8601d::parse_off (Nibbler& n) +{ + Nibbler backup (n); + std::string sign; + if (n.getN (1, sign)) + { + if (sign == "+" || sign == "-") + { + int offset; + int hh; + if (n.getDigit2 (hh)) + { + offset = hh * 3600; + int mm; + if (n.getDigit2 (mm)) + offset += mm * 60; + + _offset = (sign == "-") ? -offset : offset; + if (!isdigit (n.next ())) + return true; + } + } + } + + n = backup; + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// hh[:mm[:ss]] +bool ISO8601d::parse_time_ext (Nibbler& n) +{ + Nibbler backup (n); + int seconds = 0; + int hh; + int mm; + int ss; + if (n.getDigit2 (hh) && + !n.getDigit (mm)) + { + seconds = hh * 3600; + + if (n.skip (':') && + n.getDigit2 (mm) && + !n.getDigit (ss)) + { + seconds += mm * 60; + + if (n.skip (':') && + n.getDigit2 (ss)) + seconds += ss; + + _seconds = seconds; + return true; + } + + if (_ambiguity) + { + _seconds = seconds; + if (!isdigit (n.next ())) + return true; + } + } + + n = backup; + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// hh[mm[ss]] +bool ISO8601d::parse_time (Nibbler& n, bool ambiguous) +{ + if (!ambiguous) + return false; + + Nibbler backup (n); + int seconds = 0; + int hh; + if (n.getDigit2 (hh)) + { + seconds = hh * 3600; + + int mm; + if (n.getDigit2 (mm)) + { + seconds += mm * 60; + + int ss; + if (n.getDigit2 (ss)) + seconds += ss; + } + + _seconds = seconds; + if (!isdigit (n.next ())) + return true; + } + + n = backup; + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// time-ext 'Z' +bool ISO8601d::parse_time_utc_ext (Nibbler& n) +{ + n.save (); + if (parse_time_ext (n) && + n.skip ('Z')) + { + _utc = true; + if (!isdigit (n.next ())) + return true; + } + + n.restore (); + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// time 'Z' +bool ISO8601d::parse_time_utc (Nibbler& n) +{ + n.save (); + if (parse_time (n, true) && + n.skip ('Z')) + { + _utc = true; + if (!isdigit (n.next ())) + return true; + } + + n.restore (); + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// time-ext offset-ext +bool ISO8601d::parse_time_off_ext (Nibbler& n) +{ + Nibbler backup (n); + if (parse_time_ext (n) && + parse_off_ext (n)) + { + if (!isdigit (n.next ())) + return true; + } + + n = backup; + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// time offset +bool ISO8601d::parse_time_off (Nibbler& n) +{ + Nibbler backup (n); + if (parse_time (n, true) && + parse_off (n)) + { + if (!isdigit (n.next ())) + return true; + } + + n = backup; + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +// Using Zeller's Congruence. +int ISO8601d::dayOfWeek (int year, int month, int day) +{ + int adj = (14 - month) / 12; + int m = month + 12 * adj - 2; + int y = year - adj; + return (day + (13 * m - 1) / 5 + y + y / 4 - y / 100 + y / 400) % 7; +} + +//////////////////////////////////////////////////////////////////////////////// +// Validation via simple range checking. +bool ISO8601d::validate () +{ + // _year; + if ((_year && (_year < 1900 || _year > 2100)) || + (_month && (_month < 1 || _month > 12)) || + (_week && (_week < 1 || _week > 53)) || + (_weekday && (_weekday < 0 || _weekday > 6)) || + (_julian && (_julian < 0 || _julian > 366)) || + (_day && (_day < 1 || _day > 31)) || + (_seconds && (_seconds < 1 || _seconds > 86400)) || + (_offset && (_offset < -86400 || _offset > 86400))) + return false; + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +// int tm_sec; seconds (0 - 60) +// int tm_min; minutes (0 - 59) +// int tm_hour; hours (0 - 23) +// int tm_mday; day of month (1 - 31) +// int tm_mon; month of year (0 - 11) +// int tm_year; year - 1900 +// int tm_wday; day of week (Sunday = 0) +// int tm_yday; day of year (0 - 365) +// int tm_isdst; is summer time in effect? +// char *tm_zone; abbreviation of timezone name +// long tm_gmtoff; offset from UTC in seconds +void ISO8601d::resolve () +{ + // Don't touch the original values. + int year = _year; + int month = _month; + int week = _week; + int weekday = _weekday; + int julian = _julian; + int day = _day; + int seconds = _seconds; + int offset = _offset; + bool utc = _utc; + + struct tm t = {0}; + + // Requests that mktime determine summer time effect. + t.tm_isdst = -1; + + // Determine local time. + time_t now = time (NULL); + struct tm* local_now = localtime (&now); + struct tm* utc_now = gmtime (&now); + + // What is a complete TZ? + // utc + // offset + // local (get default) + if (utc) + offset = 0; + else if (! offset) + { +#ifdef HAVE_TM_GMTOFF + offset = local_now->tm_gmtoff; +#else + // TODO Umm... +#endif + } + + // Subtract the offset, to project local to UTC. + seconds -= offset; + + // If the time is specified without a date, if it is earlier than 'now', then + // it refers to tomorrow. + int seconds_utc_now = utc_now->tm_hour * 3600 + + utc_now->tm_min * 60 + + utc_now->tm_sec; + if (year == 0 && + month == 0 && + day == 0 && + week == 0 && + weekday == 0 && + seconds < seconds_utc_now) + { + seconds += 86400; + } + + // Conversion of week + weekday to julian. + if (week) + { + julian = (week * 7) + weekday - dayOfWeek (year, 1, 4) - 3; + } + else + { + // Default values for year, month, day: + // + // y m d --> y m d + // y m - --> y m 1 + // y - - --> y 1 1 + // - - - --> now now now + // + if (year == 0) + { + year = local_now->tm_year + 1900; + month = local_now->tm_mon + 1; + day = local_now->tm_mday; + } + else + { + if (month == 0) + { + month = 1; + day = 1; + } + else if (day == 0) + day = 1; + } + } + + if (julian) + { + month = 1; + day = julian; + } + + t.tm_year = year - 1900; + t.tm_mon = month - 1; + t.tm_mday = day; + + // What is a complete time spec? + // seconds + if (seconds) + { + if (seconds > 86400) + { + int days = seconds / 86400; + t.tm_mday += days; + seconds -= days * 86400; + } + + t.tm_hour = seconds / 3600; + t.tm_min = (seconds % 3600) / 60; + t.tm_sec = seconds % 60; + } + else + { + // User-provided default. + t.tm_hour = _default_seconds / 3600; + t.tm_min = (_default_seconds % 3600) / 60; + t.tm_sec = _default_seconds % 60; + } + + _value = timegm (&t); +} + +//////////////////////////////////////////////////////////////////////////////// +ISO8601p::ISO8601p () +{ + clear (); +} + +//////////////////////////////////////////////////////////////////////////////// +ISO8601p::~ISO8601p () +{ +} + +//////////////////////////////////////////////////////////////////////////////// +ISO8601p::operator time_t () const +{ + return _value; +} + +//////////////////////////////////////////////////////////////////////////////// +// Supported: +// +// duration ::= designated # duration +// +// designated ::= 'P' [nn 'Y'] [nn 'M'] [nn 'D'] ['T' [nn 'H'] [nn 'M'] [nn 'S']] +// +// Not supported: +// +// duration ::= designated '/' datetime-ext # duration end +// | degignated '/' datetime # duration end +// | designated # duration +// | 'P' datetime-ext '/' datetime-ext # start end +// | 'P' datetime '/' datetime # start end +// | 'P' datetime-ext # start +// | 'P' datetime # start +// | datetime-ext '/' designated # start duration +// | datetime-ext '/' 'P' datetime-ext # start end +// | datetime-ext '/' datetime-ext # start end +// | datetime '/' designated # start duration +// | datetime '/' 'P' datetime # start end +// | datetime '/' datetime # start end +// ; +// +bool ISO8601p::parse (const std::string& input, std::string::size_type& start) +{ + std::string::size_type i = start; + Nibbler n (input.substr (i)); + + if (parse_designated (n)) + { + // Check the values and determine time_t. + if (validate ()) + { + // Record cursor position. + start = n.cursor (); + + resolve (); + return true; + } + } + + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +void ISO8601p::clear () +{ + _year = 0; + _month = 0; + _day = 0; + _hours = 0; + _minutes = 0; + _seconds = 0; + _value = 0; +} + +//////////////////////////////////////////////////////////////////////////////// +// 'P' [nn 'Y'] [nn 'M'] [nn 'D'] ['T' [nn 'H'] [nn 'M'] [nn 'S']] +bool ISO8601p::parse_designated (Nibbler& n) +{ + Nibbler backup (n); + + if (n.skip ('P')) + { + int value; + n.save (); + if (n.getUnsignedInt (value) && n.skip ('Y')) + _year = value; + else + n.restore (); + + n.save (); + if (n.getUnsignedInt (value) && n.skip ('M')) + _month = value; + else + n.restore (); + + n.save (); + if (n.getUnsignedInt (value) && n.skip ('D')) + _day = value; + else + n.restore (); + + if (n.skip ('T')) + { + n.save (); + if (n.getUnsignedInt (value) && n.skip ('H')) + _hours = value; + else + n.restore (); + + n.save (); + if (n.getUnsignedInt (value) && n.skip ('M')) + _minutes = value; + else + n.restore (); + + n.save (); + if (n.getUnsignedInt (value) && n.skip ('S')) + _seconds = value; + else + n.restore (); + } + + return true; + } + + n = backup; + return false; +} + +//////////////////////////////////////////////////////////////////////////////// +bool ISO8601p::validate () +{ + return _year || + _month || + _day || + _hours || + _minutes || + _seconds; +} + +//////////////////////////////////////////////////////////////////////////////// +// Allow un-normalized values. +void ISO8601p::resolve () +{ + _value = (_year * 365 * 86400) + + (_month * 30 * 86400) + + (_day * 86400) + + (_hours * 3600) + + (_minutes * 60) + + _seconds; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/src/ISO8601.h b/src/ISO8601.h new file mode 100644 index 000000000..85a5779de --- /dev/null +++ b/src/ISO8601.h @@ -0,0 +1,110 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2006 - 2014, Paul Beckingham, Federico Hernandez. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// http://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// +#ifndef INCLUDED_ISO8601 +#define INCLUDED_ISO8601 + +#include +#include + +// Date +class ISO8601d +{ +public: + ISO8601d (); + ~ISO8601d (); + ISO8601d (const ISO8601d&); // Unimplemented + ISO8601d& operator= (const ISO8601d&); // Unimplemented + operator time_t () const; + void ambiguity (bool); + bool parse (const std::string&, std::string::size_type&); + void clear (); + void set_default_time (int, int, int); + +private: + bool parse_date_time_ext (Nibbler&); + bool parse_date_time (Nibbler&); + bool parse_date_ext (Nibbler&); + bool parse_date (Nibbler&, bool); + bool parse_off_ext (Nibbler&); + bool parse_off (Nibbler&); + bool parse_time_ext (Nibbler&); + bool parse_time (Nibbler&, bool); + bool parse_time_utc_ext (Nibbler&); + bool parse_time_utc (Nibbler&); + bool parse_time_off_ext (Nibbler&); + bool parse_time_off (Nibbler&); + int dayOfWeek (int, int, int); + bool validate (); + void resolve (); + +public: + bool _ambiguity; + int _year; + int _month; + int _week; + int _weekday; + int _julian; + int _day; + int _seconds; + int _offset; + bool _utc; + time_t _value; + + int _default_seconds; +}; + +// Period +class ISO8601p +{ +public: + ISO8601p (); + ~ISO8601p (); + ISO8601p (const ISO8601p&); // Unimplemented + ISO8601p& operator= (const ISO8601p&); // Unimplemented + operator time_t () const; + bool parse (const std::string&, std::string::size_type&); + void clear (); + +private: + bool parse_designated (Nibbler&); + bool validate (); + void resolve (); + +public: + int _year; + int _month; + int _day; + int _hours; + int _minutes; + int _seconds; + time_t _value; +}; + +// TODO Recurrence + +#endif + +//////////////////////////////////////////////////////////////////////////////// diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 23566b00e..13a8ba8bf 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -9,7 +9,7 @@ include_directories (${CMAKE_SOURCE_DIR} set (test_SRCS autocomplete.t color.t config.t date.t directory.t dom.t duration.t file.t i18n.t json.t list.t msg.t nibbler.t path.t rx.t t.t t2.t taskmod.t tdb2.t text.t tree.t uri.t utf8.t util.t - view.t width.t json_test) + view.t width.t json_test iso8601d.t iso8601p.t) message ("-- Configuring run_all") if (${CMAKE_SOURCE_DIR} STREQUAL ${CMAKE_BINARY_DIR}) diff --git a/test/iso8601d.t.cpp b/test/iso8601d.t.cpp new file mode 100644 index 000000000..d7bddc1ec --- /dev/null +++ b/test/iso8601d.t.cpp @@ -0,0 +1,315 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2013 - 2014, Göteborg Bit Factory. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// http://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +Context context; + +#define AMBIGUOUS // Include ambiguous forms +#undef AMBIGUOUS // Exclude ambiguous forms + +//////////////////////////////////////////////////////////////////////////////// +void testParse ( + UnitTest& t, + const std::string& input, + int in_start, + int in_year, + int in_month, + int in_week, + int in_weekday, + int in_julian, + int in_day, + int in_seconds, + int in_offset, + bool in_utc, + time_t in_value) +{ + std::string label = std::string ("parse (\"") + input + "\") --> "; + + ISO8601d iso; + std::string::size_type start = 0; + + t.ok (iso.parse (input, start), label + "true"); + t.is ((int) start, in_start, label + "[]"); + t.is (iso._year, in_year, label + "_year"); + t.is (iso._month, in_month, label + "_month"); + t.is (iso._week, in_week, label + "_week"); + t.is (iso._weekday, in_weekday, label + "_weekday"); + t.is (iso._julian, in_julian, label + "_julian"); + t.is (iso._day, in_day, label + "_day"); + t.is (iso._seconds, in_seconds, label + "_seconds"); + t.is (iso._offset, in_offset, label + "_offset"); + t.is (iso._utc, in_utc, label + "_utc"); + t.is ((size_t) iso._value, (size_t) in_value, label + "_value"); +} + +//////////////////////////////////////////////////////////////////////////////// +int main (int argc, char** argv) +{ + UnitTest t (1610 +#ifdef AMBIGUOUS + + 48 +#endif + ); + + ISO8601d iso; + std::string::size_type start = 0; + t.notok (iso.parse ("foo", start), "foo --> false"); + t.is ((int)start, 0, "foo[0]"); + + // Determine local and UTC time. + time_t now = time (NULL); + struct tm* local_now = localtime (&now); + int local_s = (local_now->tm_hour * 3600) + + (local_now->tm_min * 60) + + local_now->tm_sec; + local_now->tm_hour = 0; + local_now->tm_min = 0; + local_now->tm_sec = 0; + time_t local = timelocal (local_now); + std::cout << "# local midnight today " << local << "\n"; + + local_now->tm_year = 2013 - 1900; + local_now->tm_mon = 12 - 1; + local_now->tm_mday = 6; + time_t local6 = timelocal (local_now); + std::cout << "# local midnight 2013-12-06 " << local6 << "\n"; + + local_now->tm_year = 2013 - 1900; + local_now->tm_mon = 12 - 1; + local_now->tm_mday = 1; + time_t local1 = timelocal (local_now); + std::cout << "# local midnight 2013-12-01 " << local1 << "\n"; + + struct tm* utc_now = gmtime (&now); + int utc_s = (utc_now->tm_hour * 3600) + + (utc_now->tm_min * 60) + + utc_now->tm_sec; + utc_now->tm_hour = 0; + utc_now->tm_min = 0; + utc_now->tm_sec = 0; + time_t utc = timegm (utc_now); + std::cout << "# utc midnight today " << utc << "\n"; + + utc_now->tm_year = 2013 - 1900; + utc_now->tm_mon = 12 - 1; + utc_now->tm_mday = 6; + time_t utc6 = timegm (utc_now); + std::cout << "# utc midnight 2013-12-06 " << utc6 << "\n"; + + utc_now->tm_year = 2013 - 1900; + utc_now->tm_mon = 12 - 1; + utc_now->tm_mday = 1; + time_t utc1 = timegm (utc_now); + std::cout << "# utc midnight 2013-12-01 " << utc1 << "\n"; + + int hms = (12 * 3600) + (34 * 60) + 56; // The time 12:34:56 in seconds. + int hm = (12 * 3600) + (34 * 60); // The time 12:34:00 in seconds. + int h = (12 * 3600); // The time 12:00:00 in seconds. + int z = 3600; // TZ offset. + + int ld = local_s > hms ? 86400 : 0; // Local extra day if now > hms. + int ud = utc_s > hms ? 86400 : 0; // UTC extra day if now > hms. + std::cout << "# ld " << ld << "\n"; + std::cout << "# ud " << ud << "\n"; + + // Aggregated. + // input i Year Mo Wk WD Jul Da Secs TZ UTC time_t + testParse (t, "12:34:56 ", 8, 0, 0, 0, 0, 0, 0, hms, 0, false, local+hms+ld ); + + // time-ext + // input i Year Mo Wk WD Jul Da Secs TZ UTC time_t + testParse (t, "12:34:56Z", 9, 0, 0, 0, 0, 0, 0, hms, 0, true, utc+hms+ud ); + testParse (t, "12:34Z", 6, 0, 0, 0, 0, 0, 0, hm, 0, true, utc+hm+ud ); + testParse (t, "12Z", 3, 0, 0, 0, 0, 0, 0, h, 0, true, utc+h+ud ); + testParse (t, "12:34:56+01:00", 14, 0, 0, 0, 0, 0, 0, hms, 3600, false, utc+hms-z+ud ); + testParse (t, "12:34:56+01", 11, 0, 0, 0, 0, 0, 0, hms, 3600, false, utc+hms-z+ud ); + testParse (t, "12:34+01:00", 11, 0, 0, 0, 0, 0, 0, hm, 3600, false, utc+hm-z+ud ); + testParse (t, "12:34+01", 8, 0, 0, 0, 0, 0, 0, hm, 3600, false, utc+hm-z+ud ); + testParse (t, "12+01:00", 8, 0, 0, 0, 0, 0, 0, h, 3600, false, utc+h-z+ud ); + testParse (t, "12+01", 5, 0, 0, 0, 0, 0, 0, h, 3600, false, utc+h-z+ud ); + testParse (t, "12:34:56", 8, 0, 0, 0, 0, 0, 0, hms, 0, false, local+hms+ld ); + testParse (t, "12:34", 5, 0, 0, 0, 0, 0, 0, hm, 0, false, local+hm+ld ); +#ifdef AMBIGUOUS + testParse (t, "12", 2, 0, 0, 0, 0, 0, 0, h, 0, false, local+h+ld ); +#endif + + // time + // input i Year Mo Wk WD Jul Da Secs TZ UTC time_t + testParse (t, "123456Z", 7, 0, 0, 0, 0, 0, 0, hms, 0, true, utc+hms+ud ); + testParse (t, "1234Z", 5, 0, 0, 0, 0, 0, 0, hm, 0, true, utc+hm+ud ); + testParse (t, "123456+0100", 11, 0, 0, 0, 0, 0, 0, hms, 3600, false, utc+hms-z+ud ); + testParse (t, "123456+01", 9, 0, 0, 0, 0, 0, 0, hms, 3600, false, utc+hms-z+ud ); + testParse (t, "1234+0100", 9, 0, 0, 0, 0, 0, 0, hm, 3600, false, utc+hm-z+ud ); + testParse (t, "1234+01", 7, 0, 0, 0, 0, 0, 0, hm, 3600, false, utc+hm-z+ud ); + testParse (t, "12+0100", 7, 0, 0, 0, 0, 0, 0, h, 3600, false, utc+h-z+ud ); +#ifdef AMBIGUOUS + testParse (t, "123456", 6, 0, 0, 0, 0, 0, 0, hms, 0, false, local+hms+ld ); +#endif + + // datetime-ext + // input i Year Mo Wk WD Jul Da Secs TZ UTC time_t + testParse (t, "2013-12-06", 10, 2013, 12, 0, 0, 0, 6, 0, 0, false, local6 ); + testParse (t, "2013-340", 8, 2013, 0, 0, 0, 340, 0, 0, 0, false, local6 ); + testParse (t, "2013-W49-5", 10, 2013, 0, 49, 5, 0, 0, 0, 0, false, local6 ); + testParse (t, "2013-W49", 8, 2013, 0, 49, 0, 0, 0, 0, 0, false, local1 ); + + testParse (t, "2013-12-06T12:34:56", 19, 2013, 12, 0, 0, 0, 6, hms, 0, false, local6+hms); + testParse (t, "2013-12-06T12:34", 16, 2013, 12, 0, 0, 0, 6, hm, 0, false, local6+hm ); + testParse (t, "2013-340T12:34:56", 17, 2013, 0, 0, 0, 340, 0, hms, 0, false, local6+hms); + testParse (t, "2013-340T12:34", 14, 2013, 0, 0, 0, 340, 0, hm, 0, false, local6+hm ); + testParse (t, "2013-W49-5T12:34:56", 19, 2013, 0, 49, 5, 0, 0, hms, 0, false, local6+hms); + testParse (t, "2013-W49-5T12:34", 16, 2013, 0, 49, 5, 0, 0, hm, 0, false, local6+hm ); + testParse (t, "2013-W49T12:34:56", 17, 2013, 0, 49, 0, 0, 0, hms, 0, false, local1+hms); + testParse (t, "2013-W49T12:34", 14, 2013, 0, 49, 0, 0, 0, hm, 0, false, local1+hm ); + + testParse (t, "2013-12-06T12:34:56Z", 20, 2013, 12, 0, 0, 0, 6, hms, 0, true, utc6+hms ); + testParse (t, "2013-12-06T12:34Z", 17, 2013, 12, 0, 0, 0, 6, hm, 0, true, utc6+hm ); + testParse (t, "2013-340T12:34:56Z", 18, 2013, 0, 0, 0, 340, 0, hms, 0, true, utc6+hms ); + testParse (t, "2013-340T12:34Z", 15, 2013, 0, 0, 0, 340, 0, hm, 0, true, utc6+hm ); + testParse (t, "2013-W49-5T12:34:56Z", 20, 2013, 0, 49, 5, 0, 0, hms, 0, true, utc6+hms ); + testParse (t, "2013-W49-5T12:34Z", 17, 2013, 0, 49, 5, 0, 0, hm, 0, true, utc6+hm ); + testParse (t, "2013-W49T12:34:56Z", 18, 2013, 0, 49, 0, 0, 0, hms, 0, true, utc1+hms ); + testParse (t, "2013-W49T12:34Z", 15, 2013, 0, 49, 0, 0, 0, hm, 0, true, utc1+hm ); + + testParse (t, "2013-12-06T12:34:56+01:00", 25, 2013, 12, 0, 0, 0, 6, hms, 3600, false, utc6+hms-z); + testParse (t, "2013-12-06T12:34:56+01", 22, 2013, 12, 0, 0, 0, 6, hms, 3600, false, utc6+hms-z); + testParse (t, "2013-12-06T12:34:56-01:00", 25, 2013, 12, 0, 0, 0, 6, hms, -3600, false, utc6+hms+z); + testParse (t, "2013-12-06T12:34:56-01", 22, 2013, 12, 0, 0, 0, 6, hms, -3600, false, utc6+hms+z); + testParse (t, "2013-12-06T12:34+01:00", 22, 2013, 12, 0, 0, 0, 6, hm, 3600, false, utc6+hm-z ); + testParse (t, "2013-12-06T12:34+01", 19, 2013, 12, 0, 0, 0, 6, hm, 3600, false, utc6+hm-z ); + testParse (t, "2013-12-06T12:34-01:00", 22, 2013, 12, 0, 0, 0, 6, hm, -3600, false, utc6+hm+z ); + testParse (t, "2013-12-06T12:34-01", 19, 2013, 12, 0, 0, 0, 6, hm, -3600, false, utc6+hm+z ); + testParse (t, "2013-340T12:34:56+01:00", 23, 2013, 0, 0, 0, 340, 0, hms, 3600, false, utc6+hms-z); + testParse (t, "2013-340T12:34:56+01", 20, 2013, 0, 0, 0, 340, 0, hms, 3600, false, utc6+hms-z); + testParse (t, "2013-340T12:34:56-01:00", 23, 2013, 0, 0, 0, 340, 0, hms, -3600, false, utc6+hms+z); + testParse (t, "2013-340T12:34:56-01", 20, 2013, 0, 0, 0, 340, 0, hms, -3600, false, utc6+hms+z); + testParse (t, "2013-340T12:34+01:00", 20, 2013, 0, 0, 0, 340, 0, hm, 3600, false, utc6+hm-z ); + testParse (t, "2013-340T12:34+01", 17, 2013, 0, 0, 0, 340, 0, hm, 3600, false, utc6+hm-z ); + testParse (t, "2013-340T12:34-01:00", 20, 2013, 0, 0, 0, 340, 0, hm, -3600, false, utc6+hm+z ); + testParse (t, "2013-340T12:34-01", 17, 2013, 0, 0, 0, 340, 0, hm, -3600, false, utc6+hm+z ); + testParse (t, "2013-W49-5T12:34:56+01:00", 25, 2013, 0, 49, 5, 0, 0, hms, 3600, false, utc6+hms-z); + testParse (t, "2013-W49-5T12:34:56+01", 22, 2013, 0, 49, 5, 0, 0, hms, 3600, false, utc6+hms-z); + testParse (t, "2013-W49-5T12:34:56-01:00", 25, 2013, 0, 49, 5, 0, 0, hms, -3600, false, utc6+hms+z); + testParse (t, "2013-W49-5T12:34:56-01", 22, 2013, 0, 49, 5, 0, 0, hms, -3600, false, utc6+hms+z); + testParse (t, "2013-W49-5T12:34+01:00", 22, 2013, 0, 49, 5, 0, 0, hm, 3600, false, utc6+hm-z ); + testParse (t, "2013-W49-5T12:34+01", 19, 2013, 0, 49, 5, 0, 0, hm, 3600, false, utc6+hm-z ); + testParse (t, "2013-W49-5T12:34-01:00", 22, 2013, 0, 49, 5, 0, 0, hm, -3600, false, utc6+hm+z ); + testParse (t, "2013-W49-5T12:34-01", 19, 2013, 0, 49, 5, 0, 0, hm, -3600, false, utc6+hm+z ); + testParse (t, "2013-W49T12:34:56+01:00", 23, 2013, 0, 49, 0, 0, 0, hms, 3600, false, utc1+hms-z); + testParse (t, "2013-W49T12:34:56+01", 20, 2013, 0, 49, 0, 0, 0, hms, 3600, false, utc1+hms-z); + testParse (t, "2013-W49T12:34:56-01:00", 23, 2013, 0, 49, 0, 0, 0, hms, -3600, false, utc1+hms+z); + testParse (t, "2013-W49T12:34:56-01", 20, 2013, 0, 49, 0, 0, 0, hms, -3600, false, utc1+hms+z); + testParse (t, "2013-W49T12:34+01:00", 20, 2013, 0, 49, 0, 0, 0, hm, 3600, false, utc1+hm-z ); + testParse (t, "2013-W49T12:34+01", 17, 2013, 0, 49, 0, 0, 0, hm, 3600, false, utc1+hm-z ); + testParse (t, "2013-W49T12:34-01:00", 20, 2013, 0, 49, 0, 0, 0, hm, -3600, false, utc1+hm+z ); + testParse (t, "2013-W49T12:34-01", 17, 2013, 0, 49, 0, 0, 0, hm, -3600, false, utc1+hm+z ); + + // datetime +#ifdef AMBIGUOUS + testParse (t, "20131206", 8, 2013, 12, 0, 0, 0, 6, 0, 0, false, local6 ); +#endif + testParse (t, "2013W495", 8, 2013, 0, 49, 5, 0, 0, 0, 0, false, local6 ); + testParse (t, "2013W49", 7, 2013, 0, 49, 0, 0, 0, 0, 0, false, local1 ); +#ifdef AMBIGUOUS + testParse (t, "2013340", 7, 2013, 0, 0, 0, 340, 0, 0, 0, false, local6 ); +#endif + testParse (t, "2013-12", 7, 2013, 12, 0, 0, 0, 0, 0, 0, false, local1 ); + + testParse (t, "20131206T123456", 15, 2013, 12, 0, 0, 0, 6, hms, 0, false, local6+hms); + testParse (t, "20131206T12", 11, 2013, 12, 0, 0, 0, 6, h, 0, false, local6+h ); + testParse (t, "2013W495T123456", 15, 2013, 0, 49, 5, 0, 0, hms, 0, false, local6+hms); + testParse (t, "2013W495T12", 11, 2013, 0, 49, 5, 0, 0, h, 0, false, local6+h ); + testParse (t, "2013W49T123456", 14, 2013, 0, 49, 0, 0, 0, hms, 0, false, local1+hms); + testParse (t, "2013W49T12", 10, 2013, 0, 49, 0, 0, 0, h, 0, false, local1+h ); + testParse (t, "2013340T123456", 14, 2013, 0, 0, 0, 340, 0, hms, 0, false, local6+hms); + testParse (t, "2013340T12", 10, 2013, 0, 0, 0, 340, 0, h, 0, false, local6+h ); + testParse (t, "2013-12T1234", 12, 2013, 12, 0, 0, 0, 0, hm, 0, false, local1+hm ); + testParse (t, "2013-12T12", 10, 2013, 12, 0, 0, 0, 0, h, 0, false, local1+h ); + + testParse (t, "20131206T123456Z", 16, 2013, 12, 0, 0, 0, 6, hms, 0, true, utc6+hms ); + testParse (t, "20131206T12Z", 12, 2013, 12, 0, 0, 0, 6, h, 0, true, utc6+h ); + testParse (t, "2013W495T123456Z", 16, 2013, 0, 49, 5, 0, 0, hms, 0, true, utc6+hms ); + testParse (t, "2013W495T12Z", 12, 2013, 0, 49, 5, 0, 0, h, 0, true, utc6+h ); + testParse (t, "2013W49T123456Z", 15, 2013, 0, 49, 0, 0, 0, hms, 0, true, utc1+hms ); + testParse (t, "2013W49T12Z", 11, 2013, 0, 49, 0, 0, 0, h, 0, true, utc1+h ); + testParse (t, "2013340T123456Z", 15, 2013, 0, 0, 0, 340, 0, hms, 0, true, utc6+hms ); + testParse (t, "2013340T12Z", 11, 2013, 0, 0, 0, 340, 0, h, 0, true, utc6+h ); + testParse (t, "2013-12T123456Z", 15, 2013, 12, 0, 0, 0, 0, hms, 0, true, utc1+hms ); + testParse (t, "2013-12T12Z", 11, 2013, 12, 0, 0, 0, 0, h, 0, true, utc1+h ); + + testParse (t, "20131206T123456+0100", 20, 2013, 12, 0, 0, 0, 6, hms, 3600, false, utc6+hms-z); + testParse (t, "20131206T123456+01", 18, 2013, 12, 0, 0, 0, 6, hms, 3600, false, utc6+hms-z); + testParse (t, "20131206T123456-0100", 20, 2013, 12, 0, 0, 0, 6, hms, -3600, false, utc6+hms+z); + testParse (t, "20131206T123456-01", 18, 2013, 12, 0, 0, 0, 6, hms, -3600, false, utc6+hms+z); + testParse (t, "20131206T12+0100", 16, 2013, 12, 0, 0, 0, 6, h, 3600, false, utc6+h-z ); + testParse (t, "20131206T12+01", 14, 2013, 12, 0, 0, 0, 6, h, 3600, false, utc6+h-z ); + testParse (t, "20131206T12-0100", 16, 2013, 12, 0, 0, 0, 6, h, -3600, false, utc6+h+z ); + testParse (t, "20131206T12-01", 14, 2013, 12, 0, 0, 0, 6, h, -3600, false, utc6+h+z ); + testParse (t, "2013W495T123456+0100", 20, 2013, 0, 49, 5, 0, 0, hms, 3600, false, utc6+hms-z); + testParse (t, "2013W495T123456+01", 18, 2013, 0, 49, 5, 0, 0, hms, 3600, false, utc6+hms-z); + testParse (t, "2013W495T123456-0100", 20, 2013, 0, 49, 5, 0, 0, hms, -3600, false, utc6+hms+z); + testParse (t, "2013W495T123456-01", 18, 2013, 0, 49, 5, 0, 0, hms, -3600, false, utc6+hms+z); + testParse (t, "2013W495T12+0100", 16, 2013, 0, 49, 5, 0, 0, h, 3600, false, utc6+h-z ); + testParse (t, "2013W495T12+01", 14, 2013, 0, 49, 5, 0, 0, h, 3600, false, utc6+h-z ); + testParse (t, "2013W495T12-0100", 16, 2013, 0, 49, 5, 0, 0, h, -3600, false, utc6+h+z ); + testParse (t, "2013W495T12-01", 14, 2013, 0, 49, 5, 0, 0, h, -3600, false, utc6+h+z ); + testParse (t, "2013W49T123456+0100", 19, 2013, 0, 49, 0, 0, 0, hms, 3600, false, utc1+hms-z); + testParse (t, "2013W49T123456+01", 17, 2013, 0, 49, 0, 0, 0, hms, 3600, false, utc1+hms-z); + testParse (t, "2013W49T123456-0100", 19, 2013, 0, 49, 0, 0, 0, hms, -3600, false, utc1+hms+z); + testParse (t, "2013W49T123456-01", 17, 2013, 0, 49, 0, 0, 0, hms, -3600, false, utc1+hms+z); + testParse (t, "2013W49T12+0100", 15, 2013, 0, 49, 0, 0, 0, h, 3600, false, utc1+h-z ); + testParse (t, "2013W49T12+01", 13, 2013, 0, 49, 0, 0, 0, h, 3600, false, utc1+h-z ); + testParse (t, "2013W49T12-0100", 15, 2013, 0, 49, 0, 0, 0, h, -3600, false, utc1+h+z ); + testParse (t, "2013W49T12-01", 13, 2013, 0, 49, 0, 0, 0, h, -3600, false, utc1+h+z ); + testParse (t, "2013340T123456+0100", 19, 2013, 0, 0, 0, 340, 0, hms, 3600, false, utc6+hms-z); + testParse (t, "2013340T123456+01", 17, 2013, 0, 0, 0, 340, 0, hms, 3600, false, utc6+hms-z); + testParse (t, "2013340T123456-0100", 19, 2013, 0, 0, 0, 340, 0, hms, -3600, false, utc6+hms+z); + testParse (t, "2013340T123456-01", 17, 2013, 0, 0, 0, 340, 0, hms, -3600, false, utc6+hms+z); + testParse (t, "2013340T12+0100", 15, 2013, 0, 0, 0, 340, 0, h, 3600, false, utc6+h-z ); + testParse (t, "2013340T12+01", 13, 2013, 0, 0, 0, 340, 0, h, 3600, false, utc6+h-z ); + testParse (t, "2013340T12-0100", 15, 2013, 0, 0, 0, 340, 0, h, -3600, false, utc6+h+z ); + testParse (t, "2013340T12-01", 13, 2013, 0, 0, 0, 340, 0, h, -3600, false, utc6+h+z ); + testParse (t, "2013-12T123456+0100", 19, 2013, 12, 0, 0, 0, 0, hms, 3600, false, utc1+hms-z); + testParse (t, "2013-12T123456+01", 17, 2013, 12, 0, 0, 0, 0, hms, 3600, false, utc1+hms-z); + testParse (t, "2013-12T123456-0100", 19, 2013, 12, 0, 0, 0, 0, hms, -3600, false, utc1+hms+z); + testParse (t, "2013-12T123456-01", 17, 2013, 12, 0, 0, 0, 0, hms, -3600, false, utc1+hms+z); + testParse (t, "2013-12T12+0100", 15, 2013, 12, 0, 0, 0, 0, h, 3600, false, utc1+h-z ); + testParse (t, "2013-12T12+01", 13, 2013, 12, 0, 0, 0, 0, h, 3600, false, utc1+h-z ); + testParse (t, "2013-12T12-0100", 15, 2013, 12, 0, 0, 0, 0, h, -3600, false, utc1+h+z ); + testParse (t, "2013-12T12-01", 13, 2013, 12, 0, 0, 0, 0, h, -3600, false, utc1+h+z ); + + // TODO Test validation of individual values. + + return 0; +} + +//////////////////////////////////////////////////////////////////////////////// diff --git a/test/iso8601p.t.cpp b/test/iso8601p.t.cpp new file mode 100644 index 000000000..aa414652d --- /dev/null +++ b/test/iso8601p.t.cpp @@ -0,0 +1,122 @@ +//////////////////////////////////////////////////////////////////////////////// +// +// Copyright 2013 - 2014, Göteborg Bit Factory. +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL +// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// +// http://www.opensource.org/licenses/mit-license.php +// +//////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +Context context; + +//////////////////////////////////////////////////////////////////////////////// +void testParse ( + UnitTest& t, + const std::string& input, + int in_start, + int in_year, + int in_month, + int in_day, + int in_hours, + int in_minutes, + int in_seconds, + time_t in_value) +{ + std::string label = std::string ("parse (\"") + input + "\") --> "; + + ISO8601p iso; + std::string::size_type start = 0; + + t.ok (iso.parse (input, start), label + "true"); + t.is ((int) start, in_start, label + "[]"); + t.is (iso._year, in_year, label + "_year"); + t.is (iso._month, in_month, label + "_month"); + t.is (iso._day, in_day, label + "_day"); + t.is (iso._hours, in_hours, label + "_hours"); + t.is (iso._minutes, in_minutes, label + "_minutes"); + t.is (iso._seconds, in_seconds, label + "_seconds"); + t.is ((size_t) iso._value, (size_t) in_value, label + "_value"); +} + +//////////////////////////////////////////////////////////////////////////////// +int main (int argc, char** argv) +{ + UnitTest t (183); + + ISO8601p iso; + std::string::size_type start = 0; + t.notok (iso.parse ("foo", start), "foo --> false"); + t.is ((int)start, 0, "foo[0]"); + + t.notok (iso.parse ("P", start), "P --> false"); + t.is ((int)start, 0, "P[0]"); + + t.notok (iso.parse ("PT", start), "PT --> false"); + t.is ((int)start, 0, "PT[0]"); + + t.notok (iso.parse ("P1", start), "P1 --> false"); + t.is ((int)start, 0, "P1[0]"); + + t.notok (iso.parse ("P1T", start), "P1T --> false"); + t.is ((int)start, 0, "P1T[0]"); + + t.notok (iso.parse ("PT1", start), "PT1 --> false"); + t.is ((int)start, 0, "PT1[0]"); + + int year = 365 * 86400; + int month = 30 * 86400; + int day = 86400; + int h = 3600; + int m = 60; + + // Designated. + // input i Year Mo Da Ho Mi Se time_t + testParse (t, "P1Y", 3, 1, 0, 0, 0, 0, 0, year); + testParse (t, "P1M", 3, 0, 1, 0, 0, 0, 0, month); + testParse (t, "P1D", 3, 0, 0, 1, 0, 0, 0, day); + testParse (t, "P1Y1M", 5, 1, 1, 0, 0, 0, 0, year + month); + testParse (t, "P1Y1D", 5, 1, 0, 1, 0, 0, 0, year + day); + testParse (t, "P1M1D", 5, 0, 1, 1, 0, 0, 0, month + day); + testParse (t, "P1Y1M1D", 7, 1, 1, 1, 0, 0, 0, year + month + day); + testParse (t, "PT1H", 4, 0, 0, 0, 1, 0, 0, h); + testParse (t, "PT1M", 4, 0, 0, 0, 0, 1, 0, m); + testParse (t, "PT1S", 4, 0, 0, 0, 0, 0, 1, 1); + testParse (t, "PT1H1M", 6, 0, 0, 0, 1, 1, 0, h + m); + testParse (t, "PT1H1S", 6, 0, 0, 0, 1, 0, 1, h + 1); + testParse (t, "PT1M1S", 6, 0, 0, 0, 0, 1, 1, m + 1); + testParse (t, "PT1H1M1S", 8, 0, 0, 0, 1, 1, 1, h + m + 1); + testParse (t, "P1Y1M1DT1H1M1S", 14, 1, 1, 1, 1, 1, 1, year + month + day + h + m + 1); + + testParse (t, "PT24H", 5, 0, 0, 0, 24, 0, 0, day); + testParse (t, "PT40000000S", 11, 0, 0, 0, 0, 0, 40000000, 40000000); + testParse (t, "PT3600S", 7, 0, 0, 0, 0, 0, 3600, h); + testParse (t, "PT60M", 5, 0, 0, 0, 0, 60, 0, h); + + return 0; +} + +////////////////////////////////////////////////////////////////////////////////