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