kdecore Library API Documentation

krfcdate.cpp

00001 /* 00002 * 00003 * This file is part of the KDE libraries 00004 * Copyright (c) 2000-2002 Waldo Bastian <bastian@kde.org> 00005 * 2002 Rik Hemsley <rik@kde.org> 00006 * 00007 * $Id: krfcdate.cpp,v 1.18 2003/12/29 19:11:30 adridg Exp $ 00008 * 00009 * This library is free software; you can redistribute it and/or 00010 * modify it under the terms of the GNU Library General Public 00011 * License version 2 as published by the Free Software Foundation. 00012 * 00013 * This library is distributed in the hope that it will be useful, 00014 * but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 * Library General Public License for more details. 00017 * 00018 * You should have received a copy of the GNU Library General Public License 00019 * along with this library; see the file COPYING.LIB. If not, write to 00020 * the Free Software Foundation, Inc., 59 Temple Place - Suite 330, 00021 * Boston, MA 02111-1307, USA. 00022 **/ 00023 00024 #include <config.h> 00025 00026 #include <sys/param.h> 00027 #include <ctype.h> 00028 #include <stdlib.h> 00029 00030 #include <qstringlist.h> 00031 00032 #include <krfcdate.h> 00033 00034 static unsigned int ymdhms_to_seconds(int year, int mon, int day, int hour, int minute, int second) 00035 { 00036 if (sizeof(time_t) == 4) 00037 { 00038 if ((time_t)-1 < 0) 00039 { 00040 if (year >= 2038) 00041 { 00042 year = 2038; 00043 mon = 0; 00044 day = 1; 00045 hour = 0; 00046 minute = 0; 00047 second = 0; 00048 } 00049 } 00050 else 00051 { 00052 if (year >= 2115) 00053 { 00054 year = 2115; 00055 mon = 0; 00056 day = 1; 00057 hour = 0; 00058 minute = 0; 00059 second = 0; 00060 } 00061 } 00062 } 00063 00064 unsigned int ret = (day - 32075) /* days */ 00065 + 1461L * (year + 4800L + (mon - 14) / 12) / 4 00066 + 367 * (mon - 2 - (mon - 14) / 12 * 12) / 12 00067 - 3 * ((year + 4900L + (mon - 14) / 12) / 100) / 4 00068 - 2440588; 00069 ret = 24*ret + hour; /* hours */ 00070 ret = 60*ret + minute; /* minutes */ 00071 ret = 60*ret + second; /* seconds */ 00072 00073 return ret; 00074 } 00075 00076 static const char haystack[37]="janfebmaraprmayjunjulaugsepoctnovdec"; 00077 00078 // we follow the recommendation of rfc2822 to consider all 00079 // obsolete time zones not listed here equivalent to "-0000" 00080 static const struct { 00081 const char tzName[4]; 00082 int tzOffset; 00083 } known_zones[] = { 00084 { "UT", 0 }, 00085 { "GMT", 0 }, 00086 { "EST", -300 }, 00087 { "EDT", -240 }, 00088 { "CST", -360 }, 00089 { "CDT", -300 }, 00090 { "MST", -420 }, 00091 { "MDT", -360 }, 00092 { "PST", -480 }, 00093 { "PDT", -420 }, 00094 { { 0,0,0,0 }, 0 } 00095 }; 00096 00097 time_t 00098 KRFCDate::parseDate(const QString &_date) 00099 { 00100 // This parse a date in the form: 00101 // Wednesday, 09-Nov-99 23:12:40 GMT 00102 // or 00103 // Sat, 01-Jan-2000 08:00:00 GMT 00104 // or 00105 // Sat, 01 Jan 2000 08:00:00 GMT 00106 // or 00107 // 01 Jan 99 22:00 +0100 (exceptions in rfc822/rfc2822) 00108 // 00109 // We ignore the weekday 00110 // 00111 time_t result = 0; 00112 int offset = 0; 00113 char *newPosStr; 00114 const char *dateString = _date.latin1(); 00115 int day = 0; 00116 char monthStr[4]; 00117 int month = -1; 00118 int year = 0; 00119 int hour = 0; 00120 int minute = 0; 00121 int second = 0; 00122 00123 // Strip leading space 00124 while(*dateString && isspace(*dateString)) 00125 dateString++; 00126 00127 // Strip weekday 00128 while(*dateString && !isdigit(*dateString) && !isspace(*dateString)) 00129 dateString++; 00130 00131 // Strip trailing space 00132 while(*dateString && isspace(*dateString)) 00133 dateString++; 00134 00135 if (!*dateString) 00136 return result; // Invalid date 00137 00138 if (isalpha(*dateString)) 00139 { 00140 // ' Nov 5 1994 18:15:30 GMT' 00141 // Strip leading space 00142 while(*dateString && isspace(*dateString)) 00143 dateString++; 00144 00145 for(int i=0; i < 3;i++) 00146 { 00147 if (!*dateString || (*dateString == '-') || isspace(*dateString)) 00148 return result; // Invalid date 00149 monthStr[i] = tolower(*dateString++); 00150 } 00151 monthStr[3] = '\0'; 00152 00153 newPosStr = (char*)strstr(haystack, monthStr); 00154 00155 if (!newPosStr) 00156 return result; // Invalid date 00157 00158 month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, .. 00159 00160 if ((month < 0) || (month > 11)) 00161 return result; // Invalid date 00162 00163 while (*dateString && isalpha(*dateString)) 00164 dateString++; // Skip rest of month-name 00165 } 00166 00167 // ' 09-Nov-99 23:12:40 GMT' 00168 // ' 5 1994 18:15:30 GMT' 00169 day = strtol(dateString, &newPosStr, 10); 00170 dateString = newPosStr; 00171 00172 if ((day < 1) || (day > 31)) 00173 return result; // Invalid date; 00174 00175 if (!*dateString) 00176 return result; // Invalid date 00177 00178 while(*dateString && (isspace(*dateString) || (*dateString == '-'))) 00179 dateString++; 00180 00181 if (month == -1) 00182 { 00183 for(int i=0; i < 3;i++) 00184 { 00185 if (!*dateString || (*dateString == '-') || isspace(*dateString)) 00186 return result; // Invalid date 00187 monthStr[i] = tolower(*dateString++); 00188 } 00189 monthStr[3] = '\0'; 00190 00191 newPosStr = (char*)strstr(haystack, monthStr); 00192 00193 if (!newPosStr) 00194 return result; // Invalid date 00195 00196 month = (newPosStr-haystack)/3; // Jan=00, Feb=01, Mar=02, .. 00197 00198 if ((month < 0) || (month > 11)) 00199 return result; // Invalid date 00200 00201 while (*dateString && isalpha(*dateString)) 00202 dateString++; // Skip rest of month-name 00203 00204 } 00205 00206 // '-99 23:12:40 GMT' 00207 while(*dateString && (isspace(*dateString) || (*dateString == '-'))) 00208 dateString++; 00209 00210 if (!*dateString || !isdigit(*dateString)) 00211 return result; // Invalid date 00212 00213 // '99 23:12:40 GMT' 00214 year = strtol(dateString, &newPosStr, 10); 00215 dateString = newPosStr; 00216 00217 // Y2K: Solve 2 digit years 00218 if ((year >= 0) && (year < 50)) 00219 year += 2000; 00220 00221 if ((year >= 50) && (year < 100)) 00222 year += 1900; // Y2K 00223 00224 if ((year < 1900) || (year > 2500)) 00225 return result; // Invalid date 00226 00227 // Don't fail if the time is missing. 00228 if (*dateString) 00229 { 00230 // ' 23:12:40 GMT' 00231 if (!isspace(*dateString++)) 00232 return result; // Invalid date 00233 00234 hour = strtol(dateString, &newPosStr, 10); 00235 dateString = newPosStr; 00236 00237 if ((hour < 0) || (hour > 23)) 00238 return result; // Invalid date 00239 00240 if (!*dateString) 00241 return result; // Invalid date 00242 00243 // ':12:40 GMT' 00244 if (*dateString++ != ':') 00245 return result; // Invalid date 00246 00247 minute = strtol(dateString, &newPosStr, 10); 00248 dateString = newPosStr; 00249 00250 if ((minute < 0) || (minute > 59)) 00251 return result; // Invalid date 00252 00253 if (!*dateString) 00254 return result; // Invalid date 00255 00256 // ':40 GMT' 00257 if (*dateString != ':' && !isspace(*dateString)) 00258 return result; // Invalid date 00259 00260 // seconds are optional in rfc822 + rfc2822 00261 if (*dateString ==':') { 00262 dateString++; 00263 00264 second = strtol(dateString, &newPosStr, 10); 00265 dateString = newPosStr; 00266 00267 if ((second < 0) || (second > 59)) 00268 return result; // Invalid date 00269 } else { 00270 dateString++; 00271 } 00272 00273 while(*dateString && isspace(*dateString)) 00274 dateString++; 00275 } 00276 00277 // don't fail if the time zone is missing, some 00278 // broken mail-/news-clients omit the time zone 00279 if (*dateString) { 00280 if ((strncasecmp(dateString, "gmt", 3) == 0) || 00281 (strncasecmp(dateString, "utc", 3) == 0)) 00282 { 00283 dateString += 3; 00284 while(*dateString && isspace(*dateString)) 00285 dateString++; 00286 } 00287 00288 if ((*dateString == '+') || (*dateString == '-')) { 00289 offset = strtol(dateString, &newPosStr, 10); 00290 if (abs(offset) < 30) 00291 { 00292 dateString = newPosStr; 00293 00294 offset = offset * 100; 00295 00296 if (*dateString && *(dateString+1)) 00297 { 00298 dateString++; 00299 int minutes = strtol(dateString, &newPosStr, 10); 00300 if (offset > 0) 00301 offset += minutes; 00302 else 00303 offset -= minutes; 00304 } 00305 } 00306 00307 if ((offset < -9959) || (offset > 9959)) 00308 return result; // Invalid date 00309 00310 int sgn = (offset < 0)? -1:1; 00311 offset = abs(offset); 00312 offset = ((offset / 100)*60 + (offset % 100))*sgn; 00313 } else { 00314 for (int i=0; known_zones[i].tzName != 0; i++) { 00315 if (0 == strncasecmp(dateString, known_zones[i].tzName, strlen(known_zones[i].tzName))) { 00316 offset = known_zones[i].tzOffset; 00317 break; 00318 } 00319 } 00320 } 00321 } 00322 00323 result = ymdhms_to_seconds(year, month+1, day, hour, minute, second); 00324 00325 // avoid negative time values 00326 if ((offset > 0) && (offset > result)) 00327 offset = 0; 00328 00329 result -= offset*60; 00330 00331 // If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT 00332 // This is so that parse error and valid epoch 0 return values won't 00333 // be the same for sensitive applications... 00334 if (result < 1) result = 1; 00335 00336 return result; 00337 } 00338 00339 time_t 00340 KRFCDate::parseDateISO8601( const QString& input) 00341 { 00342 // These dates look like this: 00343 // YYYY-MM-DDTHH:MM:SS 00344 // But they may also have 0, 1 or 2 suffixes. 00345 // Suffix 1: .secfrac (fraction of second) 00346 // Suffix 2: Either 'Z' or +zone or -zone, where zone is HHMM 00347 00348 unsigned int year = 0; 00349 unsigned int month = 0; 00350 unsigned int mday = 0; 00351 unsigned int hour = 0; 00352 unsigned int min = 0; 00353 unsigned int sec = 0; 00354 00355 int offset = 0; 00356 00357 // First find the 'T' separator. 00358 int tPos = input.find('T'); 00359 00360 if (-1 == tPos) 00361 return 0; 00362 00363 // Now parse the date part. 00364 00365 QString dateString = input.left(tPos).stripWhiteSpace(); 00366 00367 QString timeString = input.mid(tPos + 1).stripWhiteSpace(); 00368 00369 QStringList l = QStringList::split('-', dateString); 00370 00371 year = l[0].toUInt(); 00372 month = l[1].toUInt(); 00373 mday = l[2].toUInt(); 00374 00375 // Z suffix means UTC. 00376 if ('Z' == timeString.at(timeString.length() - 1)) { 00377 timeString.remove(timeString.length() - 1, 1); 00378 } 00379 00380 // +zone or -zone suffix (offset from UTC). 00381 00382 int plusPos = timeString.findRev('+'); 00383 00384 if (-1 != plusPos) { 00385 QString offsetString = timeString.mid(plusPos + 1); 00386 00387 offset = offsetString.left(2).toUInt() * 60 + offsetString.right(2).toUInt(); 00388 00389 timeString = timeString.left(plusPos); 00390 } else { 00391 int minusPos = timeString.findRev('-'); 00392 00393 if (-1 != minusPos) { 00394 QString offsetString = timeString.mid(minusPos + 1); 00395 00396 offset = - (offsetString.left(2).toUInt() * 60 + offsetString.right(2).toUInt()); 00397 00398 timeString = timeString.left(minusPos); 00399 } 00400 } 00401 00402 // secfrac suffix. 00403 int dotPos = timeString.findRev('.'); 00404 00405 if (-1 != dotPos) { 00406 timeString = timeString.left(dotPos); 00407 } 00408 00409 // Now parse the time part. 00410 00411 l = QStringList::split(':', timeString); 00412 00413 hour = l[0].toUInt(); 00414 min = l[1].toUInt(); 00415 sec = l[2].toUInt(); 00416 00417 time_t result = ymdhms_to_seconds(year, month, mday, hour, min, sec); 00418 00419 // avoid negative time values 00420 if ((offset > 0) && (offset > result)) 00421 offset = 0; 00422 00423 result -= offset*60; 00424 00425 // If epoch 0 return epoch +1 which is Thu, 01-Jan-70 00:00:01 GMT 00426 // This is so that parse error and valid epoch 0 return values won't 00427 // be the same for sensitive applications... 00428 if (result < 1) result = 1; 00429 00430 return result; 00431 } 00432 00433 00434 int KRFCDate::localUTCOffset() 00435 { 00436 time_t timeNow = time((time_t*) 0); 00437 00438 tm *tM = gmtime(&timeNow); 00439 unsigned int timeUTC = ymdhms_to_seconds(tM->tm_year+1900, tM->tm_mon+1, tM->tm_mday, 00440 tM->tm_hour, tM->tm_min, tM->tm_sec); 00441 00442 tM = localtime(&timeNow); 00443 unsigned int timeLocal = ymdhms_to_seconds(tM->tm_year+1900, tM->tm_mon+1, tM->tm_mday, 00444 tM->tm_hour, tM->tm_min, tM->tm_sec); 00445 00446 return ((int)(timeLocal-timeUTC))/60; 00447 } 00448 00449 00450 static const char * const day_names[] = { 00451 "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" 00452 }; 00453 00454 static const char * const month_names[] = { 00455 "Jan", "Feb", "Mar", "Apr", "May", "Jun", 00456 "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" 00457 }; 00458 00459 00460 QCString KRFCDate::rfc2822DateString(time_t utcTime, int utcOffset) 00461 { 00462 utcTime += utcOffset * 60; 00463 tm *tM = gmtime(&utcTime); 00464 char sgn = (utcOffset < 0) ? '-' : '+'; 00465 int z = (utcOffset < 0) ? -utcOffset : utcOffset; 00466 QCString dateStr; 00467 00468 dateStr.sprintf("%s, %02d %s %04d %02d:%02d:%02d %c%02d%02d", 00469 day_names[tM->tm_wday], tM->tm_mday, 00470 month_names[tM->tm_mon], tM->tm_year+1900, 00471 tM->tm_hour, tM->tm_min, tM->tm_sec, 00472 sgn, z/60%24, z%60); 00473 00474 return dateStr; 00475 } 00476 00477 00478 QCString KRFCDate::rfc2822DateString(time_t utcTime) 00479 { 00480 return rfc2822DateString(utcTime, localUTCOffset()); 00481 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.2.3.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Sun Oct 10 18:54:56 2004 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003