/* * * This file is part of the KDE libraries * Copyright (c) 2000-2002 Waldo Bastian * 2002 Rik Hemsley * * $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 #include #include #include #include #include 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.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. 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.findRev('+'); 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.findRev('-'); 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.findRev('.'); 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()); }