// // VMime library (http://www.vmime.org) // Copyright (C) 2002-2005 Vincent Richard // // This program is free software; you can redistribute it and/or // modify it under the terms of the GNU General Public License as // published by the Free Software Foundation; either version 2 of // the License, or (at your option) any later version. // // This program is distributed in the hope that it will be useful, // but WITHOUT ANY WARRANTY; without even the implied warranty of // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU // General Public License for more details. // // You should have received a copy of the GNU General Public License along // with this program; if not, write to the Free Software Foundation, Inc., // 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. // // Linking this library statically or dynamically with other modules is making // a combined work based on this library. Thus, the terms and conditions of // the GNU General Public License cover the whole combination. // #include #include "vmime/dateTime.hpp" #include "vmime/platformDependant.hpp" #include "vmime/parserHelpers.hpp" #include "vmime/utility/datetimeUtils.hpp" namespace vmime { /* RFC #822: 5. DATE AND TIME SPECIFICATION date-time = [ day "," ] date time ; dd mm yy ; hh:mm:ss zzz day = "Mon" / "Tue" / "Wed" / "Thu" / "Fri" / "Sat" / "Sun" date = 1*2DIGIT month 2DIGIT ; day month year ; e.g. 20 Jun 82 month = "Jan" / "Feb" / "Mar" / "Apr" / "May" / "Jun" / "Jul" / "Aug" / "Sep" / "Oct" / "Nov" / "Dec" time = hour zone ; ANSI and Military hour = 2DIGIT ":" 2DIGIT [":" 2DIGIT] ; 00:00:00 - 23:59:59 zone = "UT" / "GMT" ; Universal Time ; North American : UT / "EST" / "EDT" ; Eastern: - 5/ - 4 / "CST" / "CDT" ; Central: - 6/ - 5 / "MST" / "MDT" ; Mountain: - 7/ - 6 / "PST" / "PDT" ; Pacific: - 8/ - 7 / 1ALPHA ; Military: Z = UT; ; A:-1; (J not used) ; M:-12; N:+1; Y:+12 / ( ("+" / "-") 4DIGIT ) ; Local differential ; hours+min. (HHMM) */ void datetime::parse(const string& buffer, const string::size_type position, const string::size_type end, string::size_type* newPosition) { const string::value_type* const pend = buffer.data() + end; const string::value_type* p = buffer.data() + position; // Parse the date and time value while (p < pend && parserHelpers::isSpace(*p)) ++p; if (p < pend) { if (parserHelpers::isAlpha(*p)) { // Ignore week day while (p < pend && parserHelpers::isAlpha(*p)) ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; if (p < pend && *p == ',') ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; } bool dayParsed = false; if (parserHelpers::isAlpha(*p)) { // Ill-formed date/time, this may be the month, // so we skip day parsing (will be done later) } else { while (p < pend && !parserHelpers::isDigit(*p)) ++p; if (p < pend && parserHelpers::isDigit(*p)) { // Month day int day = 0; do { day = day * 10 + (*p - '0'); ++p; } while (p < pend && parserHelpers::isDigit(*p)); m_day = (day >= 1 && day <= 31) ? day : 1; while (p < pend && !parserHelpers::isSpace(*p)) ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; } else { m_day = 1; // Skip everything to the next field while (p < pend && !parserHelpers::isSpace(*p)) ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; } dayParsed = true; } if (p < pend && parserHelpers::isAlpha(*p)) { // Month char_t month[4] = { 0 }; int monthLength = 0; do { month[monthLength++] = *p; ++p; } while (monthLength < 3 && p < pend && parserHelpers::isAlpha(*p)); while (p < pend && parserHelpers::isAlpha(*p)) ++p; switch (month[0]) { case 'a': case 'A': { if (month[1] == 'u' || month[1] == 'U') m_month = AUGUST; else m_month = APRIL; // by default break; } case 'd': case 'D': { m_month = DECEMBER; break; } case 'f': case 'F': { m_month = FEBRUARY; break; } case 'j': case 'J': { if (month[1] == 'u' || month[1] == 'U') { if (month[2] == 'l' || month[2] == 'L') m_month = JULY; else // if (month[2] == 'n' || month[2] == 'N') m_month = JUNE; } else { m_month = JANUARY; // by default } break; } case 'm': case 'M': { if ((month[1] == 'a' || month[1] == 'A') && (month[2] == 'y' || month[2] == 'Y')) { m_month = MAY; } else { m_month = MARCH; // by default } break; } case 'n': case 'N': { m_month = NOVEMBER; break; } case 'o': case 'O': { m_month = OCTOBER; break; } case 's': case 'S': { m_month = SEPTEMBER; break; } default: { m_month = JANUARY; // by default break; } } while (p < pend && !parserHelpers::isSpace(*p)) ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; } else { m_month = JANUARY; if (parserHelpers::isDigit(*p)) { // Here, we expected a month, but it maybe // a ill-formed date, so try to parse a year // (we don't skip anything). } else { // Skip everything to the next field while (p < pend && !parserHelpers::isSpace(*p)) ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; } } if (!dayParsed && p < pend && parserHelpers::isDigit(*p)) { // Month day int day = 0; do { day = day * 10 + (*p - '0'); ++p; } while (p < pend && parserHelpers::isDigit(*p)); m_day = (day >= 1 && day <= 31) ? day : 1; while (p < pend && !parserHelpers::isSpace(*p)) ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; } if (p < pend && parserHelpers::isDigit(*p)) { // Check for ill-formed date/time and try to recover if (p + 2 < pend && *(p + 2) == ':') { // Skip year (default to current), and advance // to time parsing m_year = now().getYear(); } else { // Year int year = 0; do { year = year * 10 + (*p - '0'); ++p; } while (p < pend && parserHelpers::isDigit(*p)); if (year < 70) m_year = year + 2000; else if (year < 1000) m_year = year + 1900; else m_year = year; while (p < pend && !parserHelpers::isSpace(*p)) ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; } } else { m_year = 1970; // Skip everything to the next field while (p < pend && !parserHelpers::isSpace(*p)) ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; } if (p < pend && parserHelpers::isDigit(*p)) { // Hour int hour = 0; do { hour = hour * 10 + (*p - '0'); ++p; } while (p < pend && parserHelpers::isDigit(*p)); m_hour = (hour >= 0 && hour <= 23) ? hour : 0; while (p < pend && parserHelpers::isSpace(*p)) ++p; if (p < pend && *p == ':') { ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; if (p < pend && parserHelpers::isDigit(*p)) { // Minute int minute = 0; do { minute = minute * 10 + (*p - '0'); ++p; } while (p < pend && parserHelpers::isDigit(*p)); m_minute = (minute >= 0 && minute <= 59) ? minute : 0; while (p < pend && parserHelpers::isSpace(*p)) ++p; if (p < pend && *p == ':') { ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; if (p < pend && parserHelpers::isDigit(*p)) { // Second int second = 0; do { second = second * 10 + (*p - '0'); ++p; } while (p < pend && parserHelpers::isDigit(*p)); m_second = (second >= 0 && second <= 59) ? second : 0; while (p < pend && !parserHelpers::isSpace(*p)) ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; } else { m_second = 0; } } else { m_second = 0; } } else { m_minute = 0; } } else { m_minute = 0; } } else { m_hour = 0; // Skip everything to the next field while (p < pend && !parserHelpers::isSpace(*p)) ++p; while (p < pend && parserHelpers::isSpace(*p)) ++p; } if (p + 1 < pend && (*p == '+' || *p == '-') && parserHelpers::isDigit(*(p + 1))) { const char_t sign = *p; ++p; // Zone offset (in hour/minutes) int offset = 0; do { offset = offset * 10 + (*p - '0'); ++p; } while (p < pend && parserHelpers::isDigit(*p)); const int hourOff = offset / 100; const int minOff = offset % 100; if (sign == '+') m_zone = hourOff * 60 + minOff; else m_zone = -(hourOff * 60 + minOff); } else if (p < pend && isalpha(*p)) { bool done = false; // Zone offset (Time zone name) char_t zone[4] = { 0 }; int zoneLength = 0; do { zone[zoneLength++] = *p; ++p; } while (zoneLength < 3 && p < pend && parserHelpers::isDigit(*p)); switch (zone[0]) { case 'c': case 'C': { if (zoneLength >= 2) { if (zone[1] == 's' || zone[1] == 'S') m_zone = CST; else m_zone = CDT; done = true; } break; } case 'e': case 'E': { if (zoneLength >= 2) { if (zone[1] == 's' || zone[1] == 'S') m_zone = EST; else m_zone = EDT; done = true; } break; } case 'm': case 'M': { if (zoneLength >= 2) { if (zone[1] == 's' || zone[1] == 'S') m_zone = MST; else m_zone = MDT; done = true; } break; } case 'p': case 'P': { if (zoneLength >= 2) { if (zone[1] == 's' || zone[1] == 'S') m_zone = PST; else m_zone = PDT; done = true; } break; } case 'u': case 'U': { if (zoneLength >= 2) { m_zone = GMT; // = UTC done = true; } break; } } if (!done) { const char_t z = zone[0]; // Military time zone if (z != 'j' && z != 'J') { typedef std::map Map; static const Map::value_type offsetMapInit[] = { Map::value_type('a', -60), Map::value_type('b', -120), Map::value_type('c', -180), Map::value_type('d', -240), Map::value_type('e', -300), Map::value_type('f', -360), Map::value_type('g', -420), Map::value_type('h', -480), Map::value_type('i', -540), Map::value_type('k', -600), Map::value_type('l', -660), Map::value_type('m', -720), Map::value_type('n', 60), Map::value_type('o', 120), Map::value_type('p', 180), Map::value_type('q', 240), Map::value_type('r', 300), Map::value_type('s', 360), Map::value_type('t', 420), Map::value_type('u', 480), Map::value_type('v', 540), Map::value_type('w', 600), Map::value_type('x', 660), Map::value_type('y', 720), Map::value_type('z', 0), }; static const Map offsetMap (::vmime::begin(offsetMapInit), ::vmime::end(offsetMapInit)); Map::const_iterator pos = offsetMap.find(parserHelpers::toLower(z)); if (pos != offsetMap.end()) m_zone = (*pos).second; else m_zone = GMT; } else { m_zone = GMT; } } } else { m_zone = 0; } } else { m_year = 1970; m_month = JANUARY; m_day = 1; m_hour = 0; m_minute = 0; m_second = 0; m_zone = 0; } setParsedBounds(position, end); if (newPosition) *newPosition = end; } void datetime::generate(utility::outputStream& os, const string::size_type /* maxLineLength */, const string::size_type curLinePos, string::size_type* newLinePos) const { static const string::value_type* dayNames[] = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" }; static const string::value_type* monthNames[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" }; const int z = ((m_zone < 0) ? -m_zone : m_zone); const int zh = z / 60; const int zm = z % 60; std::ostringstream oss; oss << dayNames[getWeekDay()] << ", " << m_day << " " << monthNames[m_month - 1] << " " << m_year << " " << std::setfill('0') << std::setw(2) << m_hour << ":" << std::setfill('0') << std::setw(2) << m_minute << ":" << std::setfill('0') << std::setw(2) << m_second << " " << ((m_zone < 0) ? '-' : '+') << std::setfill('0') << std::setw(2) << zh << std::setfill('0') << std::setw(2) << zm; const string& str = oss.str(); os << str; if (newLinePos) *newLinePos = curLinePos + str.length(); } datetime::datetime() : m_year(1970), m_month(1), m_day(1), m_hour(0), m_minute(0), m_second(0), m_zone(0) { } datetime::datetime(const int year, const int month, const int day) : m_year(year), m_month(month), m_day(day), m_hour(0), m_minute(0), m_second(0), m_zone(0) { } datetime::datetime(const int year, const int month, const int day, const int hour, const int minute, const int second, const int zone) : m_year(year), m_month(month), m_day(day), m_hour(hour), m_minute(minute), m_second(second), m_zone(zone) { } datetime::datetime(const datetime& d) : component(), m_year(d.m_year), m_month(d.m_month), m_day(d.m_day), m_hour(d.m_hour), m_minute(d.m_minute), m_second(d.m_second), m_zone(d.m_zone) { } datetime::datetime(const time_t t, const int zone) { #ifdef _REENTRANT struct tm tms; if (!gmtime_r(&t, &tms)) localtime_r(&t, &tms); #else struct tm* gtm = gmtime(&t); struct tm* ltm = localtime(&t); struct tm tms; if (gtm) tms = *gtm; else if (ltm) tms = *ltm; #endif // _REENTRANT m_year = tms.tm_year + 1900; m_month = tms.tm_mon + 1; m_day = tms.tm_mday; m_hour = tms.tm_hour; m_minute = tms.tm_min; m_second = tms.tm_sec; m_zone = zone; } datetime::datetime(const string& date) { parse(date); } datetime::~datetime() { } void datetime::copyFrom(const component& other) { const datetime& d = dynamic_cast (other); m_year = d.m_year; m_month = d.m_month; m_day = d.m_day; m_hour = d.m_hour; m_minute = d.m_minute; m_second = d.m_second; m_zone = d.m_zone; } datetime& datetime::operator=(const datetime& other) { copyFrom(other); return (*this); } datetime& datetime::operator=(const string& s) { parse(s); return (*this); } void datetime::getTime(int& hour, int& minute, int& second, int& zone) const { hour = m_hour; minute = m_minute; second = m_second; zone = m_zone; } void datetime::getTime(int& hour, int& minute, int& second) const { hour = m_hour; minute = m_minute; second = m_second; } void datetime::getDate(int& year, int& month, int& day) const { year = m_year; month = m_month; day = m_day; } void datetime::setTime(const int hour, const int minute, const int second, const int zone) { m_hour = hour; m_minute = minute; m_second = second; m_zone = zone; } void datetime::setDate(const int year, const int month, const int day) { m_year = year; m_month = month; m_day = day; } const datetime datetime::now() { return (platformDependant::getHandler()->getCurrentLocalTime()); } ref datetime::clone() const { return vmime::create (*this); } const std::vector > datetime::getChildComponents() const { return std::vector >(); } const int datetime::getYear() const { return (m_year); } const int datetime::getMonth() const { return (m_month); } const int datetime::getDay() const { return (m_day); } const int datetime::getHour() const { return (m_hour); } const int datetime::getMinute() const { return (m_minute); } const int datetime::getSecond() const { return (m_second); } const int datetime::getZone() const { return (m_zone); } const int datetime::getWeekDay() const { return (utility::datetimeUtils::getDayOfWeek(m_year, m_month, m_day)); } void datetime::setYear(const int year) { m_year = year; } void datetime::setMonth(const int month) { m_month = std::min(std::max(month, 1), 12); } void datetime::setDay(const int day) { m_day = day; } void datetime::setHour(const int hour) { m_hour = hour; } void datetime::setMinute(const int minute) { m_minute = minute; } void datetime::setSecond(const int second) { m_second = second; } void datetime::setZone(const int zone) { m_zone = zone; } const bool datetime::operator==(const datetime& other) const { const datetime ut1 = utility::datetimeUtils::localTimeToUniversalTime(*this); const datetime ut2 = utility::datetimeUtils::localTimeToUniversalTime(other); return (ut1.m_year == ut2.m_year && ut1.m_month == ut2.m_month && ut1.m_day == ut2.m_day && ut1.m_hour == ut2.m_hour && ut1.m_minute == ut2.m_minute && ut1.m_second == ut2.m_second); } const bool datetime::operator!=(const datetime& other) const { const datetime ut1 = utility::datetimeUtils::localTimeToUniversalTime(*this); const datetime ut2 = utility::datetimeUtils::localTimeToUniversalTime(other); return (ut1.m_year != ut2.m_year || ut1.m_month != ut2.m_month || ut1.m_day != ut2.m_day || ut1.m_hour != ut2.m_hour || ut1.m_minute != ut2.m_minute || ut1.m_second != ut2.m_second); } const bool datetime::operator<(const datetime& other) const { const datetime ut1 = utility::datetimeUtils::localTimeToUniversalTime(*this); const datetime ut2 = utility::datetimeUtils::localTimeToUniversalTime(other); return ((ut1.m_year < ut2.m_year) || ((ut1.m_year == ut2.m_year) && ((ut1.m_month < ut2.m_month) || ((ut1.m_month == ut2.m_month) && ((ut1.m_day < ut2.m_day) || ((ut1.m_day == ut2.m_day) && ((ut1.m_hour < ut2.m_hour) || ((ut1.m_hour == ut2.m_hour) && ((ut1.m_minute < ut2.m_minute) || ((ut1.m_minute == ut2.m_minute) && ((ut1.m_second < ut2.m_second)))))))))))); } const bool datetime::operator<=(const datetime& other) const { const datetime ut1 = utility::datetimeUtils::localTimeToUniversalTime(*this); const datetime ut2 = utility::datetimeUtils::localTimeToUniversalTime(other); return ((ut1.m_year < ut2.m_year) || ((ut1.m_year == ut2.m_year) && ((ut1.m_month < ut2.m_month) || ((ut1.m_month == ut2.m_month) && ((ut1.m_day < ut2.m_day) || ((ut1.m_day == ut2.m_day) && ((ut1.m_hour < ut2.m_hour) || ((ut1.m_hour == ut2.m_hour) && ((ut1.m_minute < ut2.m_minute) || ((ut1.m_minute == ut2.m_minute) && ((ut1.m_second <= ut2.m_second)))))))))))); } const bool datetime::operator>(const datetime& other) const { const datetime ut1 = utility::datetimeUtils::localTimeToUniversalTime(*this); const datetime ut2 = utility::datetimeUtils::localTimeToUniversalTime(other); return ((ut1.m_year > ut2.m_year) || ((ut1.m_year == ut2.m_year) && ((ut1.m_month > ut2.m_month) || ((ut1.m_month == ut2.m_month) && ((ut1.m_day > ut2.m_day) || ((ut1.m_day == ut2.m_day) && ((ut1.m_hour > ut2.m_hour) || ((ut1.m_hour == ut2.m_hour) && ((ut1.m_minute > ut2.m_minute) || ((ut1.m_minute == ut2.m_minute) && (ut1.m_second > ut2.m_second))))))))))); } const bool datetime::operator>=(const datetime& other) const { const datetime ut1 = utility::datetimeUtils::localTimeToUniversalTime(*this); const datetime ut2 = utility::datetimeUtils::localTimeToUniversalTime(other); return ((ut1.m_year > ut2.m_year) || ((ut1.m_year == ut2.m_year) && ((ut1.m_month > ut2.m_month) || ((ut1.m_month == ut2.m_month) && ((ut1.m_day > ut2.m_day) || ((ut1.m_day == ut2.m_day) && ((ut1.m_hour > ut2.m_hour) || ((ut1.m_hour == ut2.m_hour) && ((ut1.m_minute > ut2.m_minute) || ((ut1.m_minute == ut2.m_minute) && (ut1.m_second >= ut2.m_second))))))))))); } } // vmime