diff options
Diffstat (limited to 'src/dateTime.cpp')
-rw-r--r-- | src/dateTime.cpp | 702 |
1 files changed, 702 insertions, 0 deletions
diff --git a/src/dateTime.cpp b/src/dateTime.cpp new file mode 100644 index 00000000..ceaec6f1 --- /dev/null +++ b/src/dateTime.cpp @@ -0,0 +1,702 @@ +// +// VMime library (http://vmime.sourceforge.net) +// Copyright (C) 2002-2004 Vincent Richard <[email protected]> +// +// 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., 675 Mass Ave, Cambridge, MA 02139, USA. +// + +#include <iomanip> + +#include "dateTime.hpp" +#include "platformDependant.hpp" +#include "parserHelpers.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 && isspace(*p)) ++p; + + if (p < pend) + { + if (isalpha(*p)) + { + // Ignore week day + while (p < pend && isalpha(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + if (p < pend && *p == ',') ++p; + while (p < pend && isspace(*p)) ++p; + } + + while (p < pend && !isdigit(*p)) ++p; + + if (p < pend && isdigit(*p)) + { + // Month day + comp_t day = 0; + + do + { + day = day * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_day = (day >= 1 && day <= 31) ? day : 1; + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_day = 1; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p < pend && isalpha(*p)) + { + // Month + char_t month[4] = { 0 }; + int monthLength = 0; + + do + { + month[monthLength++] = *p; + ++p; + } + while (monthLength < 3 && p < pend && isalpha(*p)); + + while (p < pend && 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 && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_month = JANUARY; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p < pend && isdigit(*p)) + { + // Year + comp_t year = 0; + + do + { + year = year * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + if (year < 70) m_year = year + 2000; + else if (year < 1000) m_year = year + 1900; + else m_year = year; + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + else + { + m_year = 1970; + + // Skip everything to the next field + while (p < pend && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p < pend && isdigit(*p)) + { + // Hour + comp_t hour = 0; + + do + { + hour = hour * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_hour = (hour >= 0 && hour <= 23) ? hour : 0; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && *p == ':') + { + ++p; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && isdigit(*p)) + { + // Minute + comp_t minute = 0; + + do + { + minute = minute * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_minute = (minute >= 0 && minute <= 59) ? minute : 0; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && *p == ':') + { + ++p; + + while (p < pend && isspace(*p)) ++p; + + if (p < pend && isdigit(*p)) + { + // Second + comp_t second = 0; + + do + { + second = second * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + m_second = (second >= 0 && second <= 59) ? second : 0; + + while (p < pend && !isspace(*p)) ++p; + while (p < pend && 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 && !isspace(*p)) ++p; + while (p < pend && isspace(*p)) ++p; + } + + if (p + 1 < pend && (*p == '+' || *p == '-') && isdigit(*(p + 1))) + { + const char_t sign = *p; + ++p; + + // Zone offset (in hour/minutes) + comp_t offset = 0; + + do + { + offset = offset * 10 + (*p - '0'); + ++p; + } + while (p < pend && isdigit(*p)); + + const comp_t hourOff = offset / 100; + const comp_t 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 && 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 <char_t, int> 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(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; + } + + 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 comp_t z = ((m_zone < 0) ? -m_zone : m_zone); + const comp_t zh = z / 60; + const comp_t zm = z % 60; + + std::ostringstream oss; + oss << dayNames[dayOfWeek(m_year, m_month, m_day)] << ", " + << m_day << " " << monthNames[month() - 1] << " " << 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 comp_t year, const comp_t month, const comp_t 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 comp_t year, const comp_t month, const comp_t day, + const comp_t hour, const comp_t minute, const comp_t second, + const comp_t 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 string& date) +{ + parse(date); +} + + +datetime::~datetime() +{ +} + + +void datetime::copyFrom(const datetime& d) +{ + 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& d) +{ + copyFrom(d); + return (*this); +} + + +datetime& datetime::operator=(const string& s) +{ + parse(s); + return (*this); +} + + +void datetime::getTime(comp_t& hour, comp_t& minute, comp_t& second, comp_t& zone) const +{ + hour = m_hour; + minute = m_minute; + second = m_second; + zone = m_zone; +} + + +void datetime::getTime(comp_t& hour, comp_t& minute, comp_t& second) const +{ + hour = m_hour; + minute = m_minute; + second = m_second; +} + + +void datetime::getDate(comp_t& year, comp_t& month, comp_t& day) const +{ + year = m_year; + month = m_month; + day = m_day; +} + + +void datetime::setTime(const comp_t hour, const comp_t minute, + const comp_t second, const comp_t zone) +{ + m_hour = hour; + m_minute = minute; + m_second = second; + m_zone = zone; +} + + +void datetime::setDate(const comp_t year, const comp_t month, const comp_t day) +{ + m_year = year; + m_month = month; + m_day = day; +} + + +const datetime::comp_t datetime::dayOfWeek(const comp_t year, const comp_t month, const comp_t day) +{ + comp_t y = year; + comp_t m = month; + + // From RFC-3339 - Appendix B. Day of the Week + + // Adjust months so February is the last one + m -= 2; + + if (m < 1) + { + m += 12; + --y; + } + + // Split by century + const comp_t cent = y / 100; + y %= 100; + + return (((26 * m - 2) / 10 + day + y + (y >> 2) + (cent >> 2) + 5 * cent) % 7); +} + + +const datetime datetime::now() +{ + return (platformDependant::getHandler()->getCurrentLocalTime()); +} + + +} // vmime |