| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395 |
- //
- // DateTimeParser.cpp
- //
- // $Id: //poco/1.4/Foundation/src/DateTimeParser.cpp#5 $
- //
- // Library: Foundation
- // Package: DateTime
- // Module: DateTimeParser
- //
- // Copyright (c) 2004-2006, Applied Informatics Software Engineering GmbH.
- // and Contributors.
- //
- // SPDX-License-Identifier: BSL-1.0
- //
- #include "Poco/DateTimeParser.h"
- #include "Poco/DateTimeFormat.h"
- #include "Poco/DateTime.h"
- #include "Poco/Exception.h"
- #include "Poco/Ascii.h"
- namespace Poco {
- #define SKIP_JUNK() \
- while (it != end && !Ascii::isDigit(*it)) ++it
- #define SKIP_DIGITS() \
- while (it != end && Ascii::isDigit(*it)) ++it
- #define PARSE_NUMBER(var) \
- while (it != end && Ascii::isDigit(*it)) var = var*10 + ((*it++) - '0')
- #define PARSE_NUMBER_N(var, n) \
- { int i = 0; while (i++ < n && it != end && Ascii::isDigit(*it)) var = var*10 + ((*it++) - '0'); }
- #define PARSE_FRACTIONAL_N(var, n) \
- { int i = 0; while (i < n && it != end && Ascii::isDigit(*it)) { var = var*10 + ((*it++) - '0'); i++; } while (i++ < n) var *= 10; }
- void DateTimeParser::parse(const std::string& fmt, const std::string& str, DateTime& dateTime, int& timeZoneDifferential)
- {
- if (fmt.empty() || str.empty())
- throw SyntaxException("Empty string.");
- int year = 0;
- int month = 0;
- int day = 0;
- int hour = 0;
- int minute = 0;
- int second = 0;
- int millis = 0;
- int micros = 0;
- int tzd = 0;
- std::string::const_iterator it = str.begin();
- std::string::const_iterator end = str.end();
- std::string::const_iterator itf = fmt.begin();
- std::string::const_iterator endf = fmt.end();
- while (itf != endf && it != end)
- {
- if (*itf == '%')
- {
- if (++itf != endf)
- {
- switch (*itf)
- {
- case 'w':
- case 'W':
- while (it != end && Ascii::isSpace(*it)) ++it;
- while (it != end && Ascii::isAlpha(*it)) ++it;
- break;
- case 'b':
- case 'B':
- month = parseMonth(it, end);
- break;
- case 'd':
- case 'e':
- case 'f':
- SKIP_JUNK();
- PARSE_NUMBER_N(day, 2);
- break;
- case 'm':
- case 'n':
- case 'o':
- SKIP_JUNK();
- PARSE_NUMBER_N(month, 2);
- break;
- case 'y':
- SKIP_JUNK();
- PARSE_NUMBER_N(year, 2);
- if (year >= 69)
- year += 1900;
- else
- year += 2000;
- break;
- case 'Y':
- SKIP_JUNK();
- PARSE_NUMBER_N(year, 4);
- break;
- case 'r':
- SKIP_JUNK();
- PARSE_NUMBER(year);
- if (year < 1000)
- {
- if (year >= 69)
- year += 1900;
- else
- year += 2000;
- }
- break;
- case 'H':
- case 'h':
- SKIP_JUNK();
- PARSE_NUMBER_N(hour, 2);
- break;
- case 'a':
- case 'A':
- hour = parseAMPM(it, end, hour);
- break;
- case 'M':
- SKIP_JUNK();
- PARSE_NUMBER_N(minute, 2);
- break;
- case 'S':
- SKIP_JUNK();
- PARSE_NUMBER_N(second, 2);
- break;
- case 's':
- SKIP_JUNK();
- PARSE_NUMBER_N(second, 2);
- if (it != end && (*it == '.' || *it == ','))
- {
- ++it;
- PARSE_FRACTIONAL_N(millis, 3);
- PARSE_FRACTIONAL_N(micros, 3);
- SKIP_DIGITS();
- }
- break;
- case 'i':
- SKIP_JUNK();
- PARSE_NUMBER_N(millis, 3);
- break;
- case 'c':
- SKIP_JUNK();
- PARSE_NUMBER_N(millis, 1);
- millis *= 100;
- break;
- case 'F':
- SKIP_JUNK();
- PARSE_FRACTIONAL_N(millis, 3);
- PARSE_FRACTIONAL_N(micros, 3);
- SKIP_DIGITS();
- break;
- case 'z':
- case 'Z':
- tzd = parseTZD(it, end);
- break;
- }
- ++itf;
- }
- }
- else ++itf;
- }
- if (month == 0) month = 1;
- if (day == 0) day = 1;
- if (DateTime::isValid(year, month, day, hour, minute, second, millis, micros))
- dateTime.assign(year, month, day, hour, minute, second, millis, micros);
- else
- throw SyntaxException("date/time component out of range");
- timeZoneDifferential = tzd;
- }
- DateTime DateTimeParser::parse(const std::string& fmt, const std::string& str, int& timeZoneDifferential)
- {
- DateTime result;
- parse(fmt, str, result, timeZoneDifferential);
- return result;
- }
-
- bool DateTimeParser::tryParse(const std::string& fmt, const std::string& str, DateTime& dateTime, int& timeZoneDifferential)
- {
- try
- {
- parse(fmt, str, dateTime, timeZoneDifferential);
- }
- catch (Exception&)
- {
- return false;
- }
- return true;
- }
- void DateTimeParser::parse(const std::string& str, DateTime& dateTime, int& timeZoneDifferential)
- {
- if (!tryParse(str, dateTime, timeZoneDifferential))
- throw SyntaxException("Unsupported or invalid date/time format");
- }
-
- DateTime DateTimeParser::parse(const std::string& str, int& timeZoneDifferential)
- {
- DateTime result;
- if (tryParse(str, result, timeZoneDifferential))
- return result;
- else
- throw SyntaxException("Unsupported or invalid date/time format");
- }
-
- bool DateTimeParser::tryParse(const std::string& str, DateTime& dateTime, int& timeZoneDifferential)
- {
- if (str.length() < 4) return false;
-
- if (str[3] == ',')
- return tryParse("%w, %e %b %r %H:%M:%S %Z", str, dateTime, timeZoneDifferential);
- else if (str[3] == ' ')
- return tryParse(DateTimeFormat::ASCTIME_FORMAT, str, dateTime, timeZoneDifferential);
- else if (str.find(',') < 10)
- return tryParse("%W, %e %b %r %H:%M:%S %Z", str, dateTime, timeZoneDifferential);
- else if (Ascii::isDigit(str[0]))
- {
- if (str.find(' ') != std::string::npos || str.length() == 10)
- return tryParse(DateTimeFormat::SORTABLE_FORMAT, str, dateTime, timeZoneDifferential);
- else if (str.find('.') != std::string::npos || str.find(',') != std::string::npos)
- return tryParse(DateTimeFormat::ISO8601_FRAC_FORMAT, str, dateTime, timeZoneDifferential);
- else
- return tryParse(DateTimeFormat::ISO8601_FORMAT, str, dateTime, timeZoneDifferential);
- }
- else return false;
- }
- int DateTimeParser::parseTZD(std::string::const_iterator& it, const std::string::const_iterator& end)
- {
- struct Zone
- {
- const char* designator;
- int timeZoneDifferential;
- };
- static Zone zones[] =
- {
- {"Z", 0},
- {"UT", 0},
- {"GMT", 0},
- {"BST", 1*3600},
- {"IST", 1*3600},
- {"WET", 0},
- {"WEST", 1*3600},
- {"CET", 1*3600},
- {"CEST", 2*3600},
- {"EET", 2*3600},
- {"EEST", 3*3600},
- {"MSK", 3*3600},
- {"MSD", 4*3600},
- {"NST", -3*3600-1800},
- {"NDT", -2*3600-1800},
- {"AST", -4*3600},
- {"ADT", -3*3600},
- {"EST", -5*3600},
- {"EDT", -4*3600},
- {"CST", -6*3600},
- {"CDT", -5*3600},
- {"MST", -7*3600},
- {"MDT", -6*3600},
- {"PST", -8*3600},
- {"PDT", -7*3600},
- {"AKST", -9*3600},
- {"AKDT", -8*3600},
- {"HST", -10*3600},
- {"AEST", 10*3600},
- {"AEDT", 11*3600},
- {"ACST", 9*3600+1800},
- {"ACDT", 10*3600+1800},
- {"AWST", 8*3600},
- {"AWDT", 9*3600}
- };
- int tzd = 0;
- while (it != end && Ascii::isSpace(*it)) ++it;
- if (it != end)
- {
- if (Ascii::isAlpha(*it))
- {
- std::string designator;
- designator += *it++;
- if (it != end && Ascii::isAlpha(*it)) designator += *it++;
- if (it != end && Ascii::isAlpha(*it)) designator += *it++;
- if (it != end && Ascii::isAlpha(*it)) designator += *it++;
- for (unsigned i = 0; i < sizeof(zones)/sizeof(Zone); ++i)
- {
- if (designator == zones[i].designator)
- {
- tzd = zones[i].timeZoneDifferential;
- break;
- }
- }
- }
- if (it != end && (*it == '+' || *it == '-'))
- {
- int sign = *it == '+' ? 1 : -1;
- ++it;
- int hours = 0;
- PARSE_NUMBER_N(hours, 2);
- if (it != end && *it == ':') ++it;
- int minutes = 0;
- PARSE_NUMBER_N(minutes, 2);
- tzd += sign*(hours*3600 + minutes*60);
- }
- }
- return tzd;
- }
- int DateTimeParser::parseMonth(std::string::const_iterator& it, const std::string::const_iterator& end)
- {
- std::string month;
- while (it != end && (Ascii::isSpace(*it) || Ascii::isPunct(*it))) ++it;
- bool isFirst = true;
- while (it != end && Ascii::isAlpha(*it))
- {
- char ch = (*it++);
- if (isFirst) { month += Ascii::toUpper(ch); isFirst = false; }
- else month += Ascii::toLower(ch);
- }
- if (month.length() < 3) throw SyntaxException("Month name must be at least three characters long", month);
- for (int i = 0; i < 12; ++i)
- {
- if (DateTimeFormat::MONTH_NAMES[i].find(month) == 0)
- return i + 1;
- }
- throw SyntaxException("Not a valid month name", month);
- }
- int DateTimeParser::parseDayOfWeek(std::string::const_iterator& it, const std::string::const_iterator& end)
- {
- std::string dow;
- while (it != end && (Ascii::isSpace(*it) || Ascii::isPunct(*it))) ++it;
- bool isFirst = true;
- while (it != end && Ascii::isAlpha(*it))
- {
- char ch = (*it++);
- if (isFirst) { dow += Ascii::toUpper(ch); isFirst = false; }
- else dow += Ascii::toLower(ch);
- }
- if (dow.length() < 3) throw SyntaxException("Weekday name must be at least three characters long", dow);
- for (int i = 0; i < 7; ++i)
- {
- if (DateTimeFormat::WEEKDAY_NAMES[i].find(dow) == 0)
- return i;
- }
- throw SyntaxException("Not a valid weekday name", dow);
- }
- int DateTimeParser::parseAMPM(std::string::const_iterator& it, const std::string::const_iterator& end, int hour)
- {
- std::string ampm;
- while (it != end && (Ascii::isSpace(*it) || Ascii::isPunct(*it))) ++it;
- while (it != end && Ascii::isAlpha(*it))
- {
- char ch = (*it++);
- ampm += Ascii::toUpper(ch);
- }
- if (ampm == "AM")
- {
- if (hour == 12)
- return 0;
- else
- return hour;
- }
- else if (ampm == "PM")
- {
- if (hour < 12)
- return hour + 12;
- else
- return hour;
- }
- else throw SyntaxException("Not a valid AM/PM designator", ampm);
- }
- } // namespace Poco
|