summaryrefslogtreecommitdiffstats
path: root/kdecore/krfcdate.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'kdecore/krfcdate.cpp')
-rw-r--r--kdecore/krfcdate.cpp505
1 files changed, 505 insertions, 0 deletions
diff --git a/kdecore/krfcdate.cpp b/kdecore/krfcdate.cpp
new file mode 100644
index 000000000..4e214a81f
--- /dev/null
+++ b/kdecore/krfcdate.cpp
@@ -0,0 +1,505 @@
+/*
+ *
+ * This file is part of the KDE libraries
+ * Copyright (c) 2000-2002 Waldo Bastian <bastian@kde.org>
+ * 2002 Rik Hemsley <rik@kde.org>
+ *
+ * $Id$
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Library General Public
+ * License version 2 as published by the Free Software Foundation.
+ *
+ * This library 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
+ * Library General Public License for more details.
+ *
+ * You should have received a copy of the GNU Library General Public License
+ * along with this library; see the file COPYING.LIB. If not, write to
+ * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
+ * Boston, MA 02110-1301, USA.
+ **/
+
+#include <config.h>
+
+#include <sys/param.h>
+#include <ctype.h>
+#include <stdlib.h>
+
+#include <qstringlist.h>
+
+#include <krfcdate.h>
+
+static unsigned int ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second)
+{
+ if (sizeof(time_t) == 4)
+ {
+ if ((time_t)-1 < 0)
+ {
+ if (year >= 2038)
+ {
+ year = 2038;
+ mon = 0;
+ day = 1;
+ hour = 0;
+ minute = 0;
+ second = 0;
+ }
+ }
+ else
+ {
+ if (year >= 2115)
+ {
+ year = 2115;
+ mon = 0;
+ day = 1;
+ hour = 0;
+ minute = 0;
+ second = 0;
+ }
+ }
+ }
+
+ unsigned int ret = (day - 32075) /* days */
+ + 1461L * (year + 4800L + (mon - 14) / 12) / 4
+ + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12
+ - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4
+ - 2440588;
+ ret = 24*ret + hour; /* hours */
+ ret = 60*ret + minute; /* minutes */
+ ret = 60*ret + second; /* seconds */
+
+ return ret;
+}
+
+static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec";
+
+// we follow the recommendation of rfc2822 to consider all
+// obsolete time zones not listed here equivalent to "-0000"
+static const struct {
+ const char tzName[4];
+ int tzOffset;
+} known_zones[] = {
+ { "UT", 0 },
+ { "GMT", 0 },
+ { "EST", -300 },
+ { "EDT", -240 },
+ { "CST", -360 },
+ { "CDT", -300 },
+ { "MST", -420 },
+ { "MDT", -360 },
+ { "PST", -480 },
+ { "PDT", -420 },
+ { { 0,0,0,0 }, 0 }
+};
+
+time_t
+KRFCDate::parseDate(const QString &_date)
+{
+ if (_date.isEmpty())
+ return 0;
+
+ // This parse a date in the form:
+ // Wednesday, 09-Nov-99 23:12:40 GMT
+ // or
+ // Sat, 01-Jan-2000 08:00:00 GMT
+ // or
+ // Sat, 01 Jan 2000 08:00:00 GMT
+ // or
+ // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822)
+ //
+ // We ignore the weekday
+ //
+ time_t result = 0;
+ int offset = 0;
+ char *newPosStr;
+ const char *dateString = _date.latin1();
+ int day = 0;
+ char monthStr[4];
+ int month = -1;
+ int year = 0;
+ int hour = 0;
+ int minute = 0;
+ int second = 0;
+
+ // Strip leading space
+ while(*dateString && isspace(*dateString))
+ dateString++;
+
+ // Strip weekday
+ while(*dateString && !isdigit(*dateString) && !isspace(*dateString))
+ dateString++;
+
+ // Strip trailing space
+ while(*dateString && isspace(*dateString))
+ dateString++;
+
+ if (!*dateString)
+ return result; // Invalid date
+
+ if (isalpha(*dateString))
+ {
+ // ' Nov 5 1994 18:15:30 GMT'
+ // Strip leading space
+ while(*dateString && isspace(*dateString))
+ dateString++;
+
+ for(int i=0; i < 3;i++)
+ {
+ if (!*dateString || (*dateString == '-') || isspace(*dateString))
+ return result; // Invalid date
+ monthStr[i] = tolower(*dateString++);
+ }
+ monthStr[3] = '\0';
+
+ newPosStr = (char*)strstr(haystack, monthStr);
+
+ if (!newPosStr)
+ return result; // Invalid date
+
+ month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
+
+ if ((month < 0) || (month > 11))
+ return result; // Invalid date
+
+ while (*dateString && isalpha(*dateString))
+ dateString++; // Skip rest of month-name
+ }
+
+ // ' 09-Nov-99 23:12:40 GMT'
+ // ' 5 1994 18:15:30 GMT'
+ day = strtol(dateString, &newPosStr, 10);
+ dateString = newPosStr;
+
+ if ((day < 1) || (day > 31))
+ return result; // Invalid date;
+
+ if (!*dateString)
+ return result; // Invalid date
+
+ while(*dateString && (isspace(*dateString) || (*dateString == '-')))
+ dateString++;
+
+ if (month == -1)
+ {
+ for(int i=0; i < 3;i++)
+ {
+ if (!*dateString || (*dateString == '-') || isspace(*dateString))
+ return result; // Invalid date
+ monthStr[i] = tolower(*dateString++);
+ }
+ monthStr[3] = '\0';
+
+ newPosStr = (char*)strstr(haystack, monthStr);
+
+ if (!newPosStr)
+ return result; // Invalid date
+
+ month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, ..
+
+ if ((month < 0) || (month > 11))
+ return result; // Invalid date
+
+ while (*dateString && isalpha(*dateString))
+ dateString++; // Skip rest of month-name
+
+ }
+
+ // '-99 23:12:40 GMT'
+ while(*dateString && (isspace(*dateString) || (*dateString == '-')))
+ dateString++;
+
+ if (!*dateString || !isdigit(*dateString))
+ return result; // Invalid date
+
+ // '99 23:12:40 GMT'
+ year = strtol(dateString, &newPosStr, 10);
+ dateString = newPosStr;
+
+ // Y2K: Solve 2 digit years
+ if ((year >= 0) && (year < 50))
+ year += 2000;
+
+ if ((year >= 50) && (year < 100))
+ year += 1900; // Y2K
+
+ if ((year < 1900) || (year > 2500))
+ return result; // Invalid date
+
+ // Don't fail if the time is missing.
+ if (*dateString)
+ {
+ // ' 23:12:40 GMT'
+ if (!isspace(*dateString++))
+ return result; // Invalid date
+
+ hour = strtol(dateString, &newPosStr, 10);
+ dateString = newPosStr;
+
+ if ((hour < 0) || (hour > 23))
+ return result; // Invalid date
+
+ if (!*dateString)
+ return result; // Invalid date
+
+ // ':12:40 GMT'
+ if (*dateString++ != ':')
+ return result; // Invalid date
+
+ minute = strtol(dateString, &newPosStr, 10);
+ dateString = newPosStr;
+
+ if ((minute < 0) || (minute > 59))
+ return result; // Invalid date
+
+ if (!*dateString)
+ return result; // Invalid date
+
+ // ':40 GMT'
+ if (*dateString != ':' && !isspace(*dateString))
+ return result; // Invalid date
+
+ // seconds are optional in rfc822 + rfc2822
+ if (*dateString ==':') {
+ dateString++;
+
+ second = strtol(dateString, &newPosStr, 10);
+ dateString = newPosStr;
+
+ if ((second < 0) || (second > 59))
+ return result; // Invalid date
+ } else {
+ dateString++;
+ }
+
+ while(*dateString && isspace(*dateString))
+ dateString++;
+ }
+
+ // don't fail if the time zone is missing, some
+ // broken mail-/news-clients omit the time zone
+ if (*dateString) {
+ if ((strncasecmp(dateString, "gmt", 3) == 0) ||
+ (strncasecmp(dateString, "utc", 3) == 0))
+ {
+ dateString += 3;
+ while(*dateString && isspace(*dateString))
+ dateString++;
+ }
+
+ if ((*dateString == '+') || (*dateString == '-')) {
+ offset = strtol(dateString, &newPosStr, 10);
+ if (abs(offset) < 30)
+ {
+ dateString = newPosStr;
+
+ offset = offset * 100;
+
+ if (*dateString && *(dateString+1))
+ {
+ dateString++;
+ int minutes = strtol(dateString, &newPosStr, 10);
+ if (offset > 0)
+ offset += minutes;
+ else
+ offset -= minutes;
+ }
+ }
+
+ if ((offset < -9959) || (offset > 9959))
+ return result; // Invalid date
+
+ int sgn = (offset < 0)? -1:1;
+ offset = abs(offset);
+ offset = ((offset / 100)*60 + (offset % 100))*sgn;
+ } else {
+ for (int i=0; known_zones[i].tzName != 0; i++) {
+ if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) {
+ offset = known_zones[i].tzOffset;
+ break;
+ }
+ }
+ }
+ }
+
+ result = ymdhms_to_seconds(year, month+1, day, hour, minute, second);
+
+ // avoid negative time values
+ if ((offset > 0) && (offset > result))
+ offset = 0;
+
+ result -= offset*60;
+
+ // If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT
+ // This is so that parse error and valid epoch 0 return values won't
+ // be the same for sensitive applications...
+ if (result < 1) result = 1;
+
+ return result;
+}
+
+time_t
+KRFCDate::parseDateISO8601( const QString& input_ )
+{
+ if (input_.isEmpty())
+ return 0;
+
+ // These dates look like this:
+ // YYYY-MM-DDTHH:MM:SS
+ // But they may also have 0, 1 or 2 suffixes.
+ // Suffix 1: .secfrac (fraction of second)
+ // Suffix 2: Either 'Z' or +zone or -zone, where zone is HHMM
+
+ unsigned int year = 0;
+ unsigned int month = 0;
+ unsigned int mday = 0;
+ unsigned int hour = 0;
+ unsigned int min = 0;
+ unsigned int sec = 0;
+
+ int offset = 0;
+
+ QString input = input_;
+
+ // First find the 'T' separator, if any.
+ int tPos = input.find('T');
+
+ // If there is no time, no month or no day specified, fill those missing
+ // fields so that 'input' matches YYYY-MM-DDTHH:MM:SS
+ if (-1 == tPos) {
+ const int dashes = input.contains('-');
+ if (0 == dashes) {
+ input += "-01-01";
+ } else if (1 == dashes) {
+ input += "-01";
+ }
+ tPos = input.length();
+ input += "T12:00:00";
+ }
+
+ // Now parse the date part.
+
+ QString dateString = input.left(tPos).stripWhiteSpace();
+
+ QString timeString = input.mid(tPos + 1).stripWhiteSpace();
+
+ QStringList l = QStringList::split('-', dateString);
+
+ if (l.size() < 3)
+ return 0;
+
+ year = l[0].toUInt();
+ month = l[1].toUInt();
+ mday = l[2].toUInt();
+
+ // Z suffix means UTC.
+ if ('Z' == timeString.at(timeString.length() - 1)) {
+ timeString.remove(timeString.length() - 1, 1);
+ }
+
+ // +zone or -zone suffix (offset from UTC).
+
+ int plusPos = timeString.findRev('+');
+
+ if (-1 != plusPos) {
+ QString offsetString = timeString.mid(plusPos + 1);
+
+ offset = offsetString.left(2).toUInt() * 60 + offsetString.right(2).toUInt();
+
+ timeString = timeString.left(plusPos);
+ } else {
+ int minusPos = timeString.findRev('-');
+
+ if (-1 != minusPos) {
+ QString offsetString = timeString.mid(minusPos + 1);
+
+ offset = - int(offsetString.left(2).toUInt() * 60 + offsetString.right(2).toUInt());
+
+ timeString = timeString.left(minusPos);
+ }
+ }
+
+ // secfrac suffix.
+ int dotPos = timeString.findRev('.');
+
+ if (-1 != dotPos) {
+ timeString = timeString.left(dotPos);
+ }
+
+ // Now parse the time part.
+
+ l = QStringList::split(':', timeString);
+
+ if (l.size() < 3)
+ return 0;
+
+ hour = l[0].toUInt();
+ min = l[1].toUInt();
+ sec = l[2].toUInt();
+
+ time_t result = ymdhms_to_seconds(year, month, mday, hour, min, sec);
+
+ // avoid negative time values
+ if ((offset > 0) && (offset > result))
+ offset = 0;
+
+ result -= offset*60;
+
+ // If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT
+ // This is so that parse error and valid epoch 0 return values won't
+ // be the same for sensitive applications...
+ if (result < 1) result = 1;
+
+ return result;
+}
+
+
+int KRFCDate::localUTCOffset()
+{
+ time_t timeNow = time((time_t*) 0);
+
+ tm *tM = gmtime(&timeNow);
+ unsigned int timeUTC = ymdhms_to_seconds(tM->tm_year+1900, tM->tm_mon+1, tM->tm_mday,
+ tM->tm_hour, tM->tm_min, tM->tm_sec);
+
+ tM = localtime(&timeNow);
+ unsigned int timeLocal = ymdhms_to_seconds(tM->tm_year+1900, tM->tm_mon+1, tM->tm_mday,
+ tM->tm_hour, tM->tm_min, tM->tm_sec);
+
+ return ((int)(timeLocal-timeUTC))/60;
+}
+
+
+static const char * const day_names[] = {
+ "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
+};
+
+static const char * const month_names[] = {
+ "Jan", "Feb", "Mar", "Apr", "May", "Jun",
+ "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
+};
+
+
+QCString KRFCDate::rfc2822DateString(time_t utcTime, int utcOffset)
+{
+ utcTime += utcOffset * 60;
+ tm *tM = gmtime(&utcTime);
+ char sgn = (utcOffset < 0) ? '-' : '+';
+ int z = (utcOffset < 0) ? -utcOffset : utcOffset;
+ QCString dateStr;
+
+ dateStr.sprintf("%s, %02d %s %04d %02d:%02d:%02d %c%02d%02d",
+ day_names[tM->tm_wday], tM->tm_mday,
+ month_names[tM->tm_mon], tM->tm_year+1900,
+ tM->tm_hour, tM->tm_min, tM->tm_sec,
+ sgn, z/60%24, z%60);
+
+ return dateStr;
+}
+
+
+QCString KRFCDate::rfc2822DateString(time_t utcTime)
+{
+ return rfc2822DateString(utcTime, localUTCOffset());
+}