KDECore
kdatetimeparser.cpp
Go to the documentation of this file.
00001 /* 00002 Copyright 2009, 2010 John Layt <john@layt.net> 00003 00004 This library is free software; you can redistribute it and/or 00005 modify it under the terms of the GNU Library General Public 00006 License as published by the Free Software Foundation; either 00007 version 2 of the License, or (at your option) any later version. 00008 00009 This library is distributed in the hope that it will be useful, 00010 but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00012 Library General Public License for more details. 00013 00014 You should have received a copy of the GNU Library General Public License 00015 along with this library; see the file COPYING.LIB. If not, write to 00016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00017 Boston, MA 02110-1301, USA. 00018 */ 00019 00020 #include "kdatetimeparser_p.h" 00021 00022 #include "kcalendarsystemprivate_p.h" 00023 #include "kcalendarsystem.h" 00024 #include "kcalendarera_p.h" 00025 00026 #include "kdebug.h" 00027 00028 KDateTimeParser::KDateTimeParser() 00029 { 00030 } 00031 00032 KDateTimeParser::~KDateTimeParser() 00033 { 00034 } 00035 00036 // Parse a DateTime input string and return just the Date component 00037 QDate KDateTimeParser::parseDate(const QString &inputString, 00038 const QString &formatString, 00039 const KCalendarSystem *calendar, 00040 const KLocale *locale, 00041 KLocale::DigitSet digitSet, 00042 KLocale::DateTimeFormatStandard formatStandard) const 00043 { 00044 DateTimeComponents result; 00045 if (formatStandard == KLocale::UnicodeFormat) { 00046 result = parseDateUnicode(inputString, formatString, calendar, locale, digitSet); 00047 } else { 00048 result = parseDatePosix(inputString, formatString, calendar, locale, digitSet, formatStandard); 00049 } 00050 00051 QDate resultDate; 00052 00053 if (!result.error && 00054 formatString.simplified().length() <= result.formatPosition && 00055 inputString.simplified().length() <= result.inputPosition) { 00056 00057 // If there were no parsing errors, and we have reached the end of both the input and 00058 // format strings, then see if we have a valid date based on the components parsed 00059 00060 // If we haven't parsed a year component, then assume this year 00061 if (!result.parsedYear) { 00062 result.year = calendar->year(QDate::currentDate()); 00063 } 00064 00065 if ((!result.eraName.isEmpty() || result.yearInEra > -1) && result.month > 0 && result.day > 0) { 00066 // Have parsed Era components as well as month and day components 00067 calendar->setDate(resultDate, result.eraName, result.yearInEra, result.month, result.day); 00068 } else if (result.month > 0 && result.day > 0) { 00069 // Have parsed month and day components 00070 calendar->setDate(resultDate, result.year, result.month, result.day); 00071 } else if (result.dayInYear > 0) { 00072 // Have parsed Day In Year component 00073 calendar->setDate(resultDate, result.year, result.dayInYear); 00074 } else if (result.isoWeekNumber > 0 && result.dayOfIsoWeek > 0) { 00075 // Have parsed ISO Week components 00076 calendar->setDateIsoWeek(resultDate, result.year, result.isoWeekNumber, result.dayOfIsoWeek); 00077 } 00078 00079 } 00080 00081 return resultDate; 00082 } 00083 00084 DateTimeComponents KDateTimeParser::parseDatePosix(const QString &inputString, 00085 const QString &formatString, 00086 const KCalendarSystem *calendar, 00087 const KLocale *locale, 00088 KLocale::DigitSet digitSet, 00089 KLocale::DateTimeFormatStandard standard) const 00090 { 00091 QString str = inputString.simplified().toLower(); 00092 QString fmt = formatString.simplified(); 00093 int dd = -1; 00094 int mm = -1; 00095 int yy = 0; 00096 bool parsedYear = false; 00097 int ey = -1; 00098 QString ee; 00099 int dayInYear = -1; 00100 int isoWeekNumber = -1; 00101 int dayOfIsoWeek = -1; 00102 int strpos = 0; 00103 int fmtpos = 0; 00104 int readLength; // Temporary variable used when reading input 00105 bool error = false; 00106 00107 while (fmt.length() > fmtpos && str.length() > strpos && !error) { 00108 00109 QChar fmtChar = fmt.at(fmtpos++); 00110 00111 if (fmtChar != QLatin1Char('%')) { 00112 00113 if (fmtChar.isSpace() && str.at(strpos).isSpace()) { 00114 strpos++; 00115 } else if (fmtChar.toLower() == str.at(strpos)) { 00116 strpos++; 00117 } else { 00118 error = true; 00119 } 00120 00121 } else { 00122 int j; 00123 QString shortName, longName; 00124 QChar modifierChar; 00125 // remove space at the beginning 00126 if (str.length() > strpos && str.at(strpos).isSpace()) { 00127 strpos++; 00128 } 00129 00130 fmtChar = fmt.at(fmtpos++); 00131 if (fmtChar == QLatin1Char('E')) { 00132 modifierChar = fmtChar; 00133 fmtChar = fmt.at(fmtpos++); 00134 } 00135 00136 switch (fmtChar.unicode()) { 00137 case 'a': // Weekday Name Short 00138 case 'A': // Weekday Name Long 00139 error = true; 00140 j = 1; 00141 while (error && j <= calendar->d_ptr->maxDaysInWeek()) { 00142 shortName = calendar->weekDayName(j, KCalendarSystem::ShortDayName).toLower(); 00143 longName = calendar->weekDayName(j, KCalendarSystem::LongDayName).toLower(); 00144 if (str.mid(strpos, longName.length()) == longName) { 00145 strpos += longName.length(); 00146 error = false; 00147 } else if (str.mid(strpos, shortName.length()) == shortName) { 00148 strpos += shortName.length(); 00149 error = false; 00150 } 00151 ++j; 00152 } 00153 break; 00154 case 'b': // Month Name Short 00155 case 'h': // Month Name Short 00156 case 'B': // Month Name Long 00157 error = true; 00158 j = 1; 00159 while (error && j <= calendar->d_ptr->maxMonthsInYear()) { 00160 // This may be a problem in calendar systems with variable number of months 00161 // in the year and/or names of months that change depending on the year, e.g 00162 // Hebrew. We really need to know the correct year first, but we may not have 00163 // read it yet and will be using the current year instead 00164 int monthYear; 00165 if (parsedYear) { 00166 monthYear = yy; 00167 } else { 00168 monthYear = calendar->year(QDate::currentDate()); 00169 } 00170 if (calendar->locale()->dateMonthNamePossessive()) { 00171 shortName = calendar->monthName(j, monthYear, KCalendarSystem::ShortNamePossessive).toLower(); 00172 longName = calendar->monthName(j, monthYear, KCalendarSystem::LongNamePossessive).toLower(); 00173 } else { 00174 shortName = calendar->monthName(j, monthYear, KCalendarSystem::ShortName).toLower(); 00175 longName = calendar->monthName(j, monthYear, KCalendarSystem::LongName).toLower(); 00176 } 00177 if (str.mid(strpos, longName.length()) == longName) { 00178 mm = j; 00179 strpos += longName.length(); 00180 error = false; 00181 } else if (str.mid(strpos, shortName.length()) == shortName) { 00182 mm = j; 00183 strpos += shortName.length(); 00184 error = false; 00185 } 00186 ++j; 00187 } 00188 break; 00189 case 'd': // Day Number Long 00190 case 'e': // Day Number Short 00191 dd = calendar->dayStringToInteger(str.mid(strpos), readLength); 00192 strpos += readLength; 00193 error = readLength <= 0; 00194 break; 00195 case 'n': 00196 // PosixFormat %n is Newline 00197 // KdeFormat %n is Month Number Short 00198 if (standard == KLocale::KdeFormat) { 00199 mm = calendar->monthStringToInteger(str.mid(strpos), readLength); 00200 strpos += readLength; 00201 error = readLength <= 0; 00202 } 00203 // standard == KLocale::PosixFormat 00204 // all whitespace already 'eaten', no action required 00205 break; 00206 case 'm': // Month Number Long 00207 mm = calendar->monthStringToInteger(str.mid(strpos), readLength); 00208 strpos += readLength; 00209 error = readLength <= 0; 00210 break; 00211 case 'Y': // Year Number Long 00212 case 'y': // Year Number Short 00213 if (modifierChar == QLatin1Char('E')) { // Year In Era 00214 if (fmtChar == QLatin1Char('y')) { 00215 ey = calendar->yearStringToInteger(str.mid(strpos), readLength); 00216 strpos += readLength; 00217 error = readLength <= 0; 00218 } else { 00219 error = true; 00220 j = calendar->eraList()->count() - 1; // Start with the most recent 00221 while (error && j >= 0) { 00222 QString subFormat = calendar->eraList()->at(j).format(); 00223 QString subInput = str.mid(strpos); 00224 DateTimeComponents subResult = parseDatePosix(subInput, subFormat, calendar, locale, digitSet, standard); 00225 if (!subResult.error) { 00226 if (subResult.parsedYear) { 00227 yy = subResult.year; 00228 parsedYear = true; 00229 error = false; 00230 strpos += subResult.inputPosition; 00231 } else if (!subResult.eraName.isEmpty() && subResult.yearInEra >= 0) { 00232 ee = subResult.eraName; 00233 ey = subResult.yearInEra; 00234 error = false; 00235 strpos += subResult.inputPosition; 00236 } 00237 } 00238 --j; 00239 } 00240 } 00241 } else { 00242 yy = calendar->yearStringToInteger(str.mid(strpos), readLength); 00243 strpos += readLength; 00244 if (fmtChar == QLatin1Char('y')) { 00245 yy = calendar->applyShortYearWindow(yy); 00246 } 00247 error = readLength <= 0; 00248 if (!error) { 00249 parsedYear = true; 00250 } 00251 } 00252 break; 00253 case 'C': // Era 00254 error = true; 00255 if (modifierChar == QLatin1Char('E')) { 00256 j = calendar->eraList()->count() - 1; // Start with the most recent 00257 while (error && j >= 0) { 00258 shortName = calendar->d_ptr->m_eraList->at(j).name(KLocale::ShortName).toLower(); 00259 longName = calendar->eraList()->at(j).name(KLocale::LongName).toLower(); 00260 if (str.mid(strpos, longName.length()) == longName) { 00261 strpos += longName.length(); 00262 ee = longName; 00263 error = false; 00264 } else if (str.mid(strpos, shortName.length()) == shortName) { 00265 strpos += shortName.length(); 00266 ee = shortName; 00267 error = false; 00268 } 00269 --j; 00270 } 00271 } 00272 break; 00273 case 'j': // Day Of Year Number 00274 dayInYear = integerFromString(str.mid(strpos), 3, readLength); 00275 strpos += readLength; 00276 error = readLength <= 0; 00277 break; 00278 case 'V': // ISO Week Number 00279 isoWeekNumber = integerFromString(str.mid(strpos), 2, readLength); 00280 strpos += readLength; 00281 error = readLength <= 0; 00282 break; 00283 case 'u': // ISO Day Of Week 00284 dayOfIsoWeek = integerFromString(str.mid(strpos), 1, readLength); 00285 strpos += readLength; 00286 error = readLength <= 0; 00287 break; 00288 } 00289 } 00290 } 00291 00292 DateTimeComponents result; 00293 result.error = error; 00294 result.inputPosition = strpos; 00295 result.formatPosition = fmtpos; 00296 if (error) { 00297 result.day = -1; 00298 result.month = -1; 00299 result.year = 0; 00300 result.parsedYear = false; 00301 result.eraName.clear(); 00302 result.yearInEra = -1; 00303 result.dayInYear = -1; 00304 result.isoWeekNumber = -1; 00305 result.dayOfIsoWeek = -1; 00306 } else { 00307 result.day = dd; 00308 result.month = mm; 00309 result.year = yy; 00310 result.parsedYear = parsedYear; 00311 result.eraName = ee; 00312 result.yearInEra = ey; 00313 result.dayInYear = dayInYear; 00314 result.isoWeekNumber = isoWeekNumber; 00315 result.dayOfIsoWeek = dayOfIsoWeek; 00316 } 00317 return result; 00318 } 00319 00320 // Parse an input string to match a UNICODE DateTime format string and return any components found 00321 DateTimeComponents KDateTimeParser::parseDateUnicode(const QString &inputString, 00322 const QString &formatString, 00323 const KCalendarSystem *calendar, 00324 const KLocale *locale, 00325 KLocale::DigitSet digitSet) const 00326 { 00327 Q_UNUSED(calendar); 00328 Q_UNUSED(locale); 00329 Q_UNUSED(digitSet); 00330 QString str = inputString.simplified().toLower(); 00331 QString fmt = formatString.simplified(); 00332 int dd = -1; 00333 int mm = -1; 00334 int yy = 0; 00335 bool parsedYear = false; 00336 int ey = -1; 00337 QString ee; 00338 int dayInYear = -1; 00339 int isoWeekNumber = -1; 00340 int dayOfIsoWeek = -1; 00341 int strpos = 0; 00342 int fmtpos = 0; 00343 //int readLength; // Temporary variable used when reading input 00344 bool error = false; 00345 00346 DateTimeComponents result; 00347 result.error = error; 00348 result.inputPosition = strpos; 00349 result.formatPosition = fmtpos; 00350 if (error) { 00351 result.day = -1; 00352 result.month = -1; 00353 result.year = 0; 00354 result.parsedYear = false; 00355 result.eraName.clear(); 00356 result.yearInEra = -1; 00357 result.dayInYear = -1; 00358 result.isoWeekNumber = -1; 00359 result.dayOfIsoWeek = -1; 00360 } else { 00361 result.day = dd; 00362 result.month = mm; 00363 result.year = yy; 00364 result.parsedYear = parsedYear; 00365 result.eraName = ee; 00366 result.yearInEra = ey; 00367 result.dayInYear = dayInYear; 00368 result.isoWeekNumber = isoWeekNumber; 00369 result.dayOfIsoWeek = dayOfIsoWeek; 00370 } 00371 return result; 00372 } 00373 00374 // Peel a number off the front of a string which may have other trailing chars after the number 00375 // Stop either at either maxLength, eos, or first non-digit char 00376 int KDateTimeParser::integerFromString(const QString &string, int maxLength, int &readLength) const 00377 { 00378 int value = -1; 00379 int position = 0; 00380 readLength = 0; 00381 bool ok = false; 00382 00383 if (maxLength < 0) { 00384 maxLength = string.length(); 00385 } 00386 00387 while (position < string.length() && 00388 position < maxLength && 00389 string.at(position).isDigit()) { 00390 position++; 00391 } 00392 00393 if (position > 0) { 00394 value = string.left(position).toInt(&ok); 00395 if (ok) { 00396 readLength = position; 00397 } else { 00398 value = -1; 00399 } 00400 } 00401 00402 return value; 00403 }
KDE 4.7 API Reference