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.16 2002/10/07 01:07:45 maragato 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;
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 }
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 kdelibs Version 3.1.4.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Sun Feb 27 22:14:47 2005 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001