|
|
|
/*
|
|
|
|
*
|
|
|
|
* 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 <tqstringlist.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 TQString &_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 TQString& 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;
|
|
|
|
|
|
|
|
TQString input = input_;
|
|
|
|
|
|
|
|
// First find the 'T' separator, if any.
|
|
|
|
int tPos = input.tqfind('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.tqcontains('-');
|
|
|
|
if (0 == dashes) {
|
|
|
|
input += "-01-01";
|
|
|
|
} else if (1 == dashes) {
|
|
|
|
input += "-01";
|
|
|
|
}
|
|
|
|
tPos = input.length();
|
|
|
|
input += "T12:00:00";
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now parse the date part.
|
|
|
|
|
|
|
|
TQString dateString = input.left(tPos).stripWhiteSpace();
|
|
|
|
|
|
|
|
TQString timeString = input.mid(tPos + 1).stripWhiteSpace();
|
|
|
|
|
|
|
|
TQStringList l = TQStringList::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 ((QChar)'Z' == timeString.at(timeString.length() - 1)) {
|
|
|
|
timeString.remove(timeString.length() - 1, 1);
|
|
|
|
}
|
|
|
|
|
|
|
|
// +zone or -zone suffix (offset from UTC).
|
|
|
|
|
|
|
|
int plusPos = timeString.tqfindRev('+');
|
|
|
|
|
|
|
|
if (-1 != plusPos) {
|
|
|
|
TQString offsetString = timeString.mid(plusPos + 1);
|
|
|
|
|
|
|
|
offset = offsetString.left(2).toUInt() * 60 + offsetString.right(2).toUInt();
|
|
|
|
|
|
|
|
timeString = timeString.left(plusPos);
|
|
|
|
} else {
|
|
|
|
int minusPos = timeString.tqfindRev('-');
|
|
|
|
|
|
|
|
if (-1 != minusPos) {
|
|
|
|
TQString 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.tqfindRev('.');
|
|
|
|
|
|
|
|
if (-1 != dotPos) {
|
|
|
|
timeString = timeString.left(dotPos);
|
|
|
|
}
|
|
|
|
|
|
|
|
// Now parse the time part.
|
|
|
|
|
|
|
|
l = TQStringList::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"
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
TQCString KRFCDate::rfc2822DateString(time_t utcTime, int utcOffset)
|
|
|
|
{
|
|
|
|
utcTime += utcOffset * 60;
|
|
|
|
tm *tM = gmtime(&utcTime);
|
|
|
|
char sgn = (utcOffset < 0) ? '-' : '+';
|
|
|
|
int z = (utcOffset < 0) ? -utcOffset : utcOffset;
|
|
|
|
TQCString 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;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
TQCString KRFCDate::rfc2822DateString(time_t utcTime)
|
|
|
|
{
|
|
|
|
return rfc2822DateString(utcTime, localUTCOffset());
|
|
|
|
}
|