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 { 00138 case 'a': // Weekday Name Short 00139 case 'A': // Weekday Name Long 00140 error = true; 00141 j = 1; 00142 while ( error && j <= calendar->d_ptr->maxDaysInWeek() ) { 00143 shortName = calendar->weekDayName( j, KCalendarSystem::ShortDayName ).toLower(); 00144 longName = calendar->weekDayName( j, KCalendarSystem::LongDayName ).toLower(); 00145 if ( str.mid( strpos, longName.length() ) == longName ) { 00146 strpos += longName.length(); 00147 error = false; 00148 } else if ( str.mid( strpos, shortName.length() ) == shortName ) { 00149 strpos += shortName.length(); 00150 error = false; 00151 } 00152 ++j; 00153 } 00154 break; 00155 case 'b': // Month Name Short 00156 case 'h': // Month Name Short 00157 case 'B': // Month Name Long 00158 error = true; 00159 j = 1; 00160 while ( error && j <= calendar->d_ptr->maxMonthsInYear() ) { 00161 // This may be a problem in calendar systems with variable number of months 00162 // in the year and/or names of months that change depending on the year, e.g 00163 // Hebrew. We really need to know the correct year first, but we may not have 00164 // read it yet and will be using the current year instead 00165 int monthYear; 00166 if ( parsedYear ) { 00167 monthYear = yy; 00168 } else { 00169 monthYear = calendar->year( QDate::currentDate() ); 00170 } 00171 if ( calendar->locale()->dateMonthNamePossessive() ) { 00172 shortName = calendar->monthName( j, yy, KCalendarSystem::ShortNamePossessive ).toLower(); 00173 longName = calendar->monthName( j, yy, KCalendarSystem::LongNamePossessive ).toLower(); 00174 } else { 00175 shortName = calendar->monthName( j, yy, KCalendarSystem::ShortName ).toLower(); 00176 longName = calendar->monthName( j, yy, KCalendarSystem::LongName ).toLower(); 00177 } 00178 if ( str.mid( strpos, longName.length() ) == longName ) { 00179 mm = j; 00180 strpos += longName.length(); 00181 error = false; 00182 } else if ( str.mid( strpos, shortName.length() ) == shortName ) { 00183 mm = j; 00184 strpos += shortName.length(); 00185 error = false; 00186 } 00187 ++j; 00188 } 00189 break; 00190 case 'd': // Day Number Long 00191 case 'e': // Day Number Short 00192 dd = calendar->dayStringToInteger( str.mid( strpos ), readLength ); 00193 strpos += readLength; 00194 error = readLength <= 0; 00195 break; 00196 case 'n': 00197 // PosixFormat %n is Newline 00198 // KdeFormat %n is Month Number Short 00199 if ( standard == KLocale::KdeFormat ) { 00200 mm = calendar->monthStringToInteger( str.mid( strpos ), readLength ); 00201 strpos += readLength; 00202 error = readLength <= 0; 00203 } 00204 // standard == KLocale::PosixFormat 00205 // all whitespace already 'eaten', no action required 00206 break; 00207 case 'm': // Month Number Long 00208 mm = calendar->monthStringToInteger( str.mid( strpos ), readLength ); 00209 strpos += readLength; 00210 error = readLength <= 0; 00211 break; 00212 case 'Y': // Year Number Long 00213 case 'y': // Year Number Short 00214 if ( modifierChar == QLatin1Char('E') ) { // Year In Era 00215 if ( fmtChar == QLatin1Char('y') ) { 00216 ey = calendar->yearStringToInteger( str.mid( strpos ), readLength ); 00217 strpos += readLength; 00218 error = readLength <= 0; 00219 } else { 00220 error = true; 00221 j = calendar->eraList()->count() -1; // Start with the most recent 00222 while ( error && j >= 0 ) { 00223 QString subFormat = calendar->eraList()->at( j ).format(); 00224 QString subInput = str.mid( strpos ); 00225 DateTimeComponents subResult = parseDatePosix( subInput, subFormat, calendar, locale, digitSet, standard ); 00226 if ( !subResult.error ) { 00227 if ( subResult.parsedYear ) { 00228 yy = subResult.year; 00229 parsedYear = true; 00230 error = false; 00231 strpos += subResult.inputPosition; 00232 } else if ( !subResult.eraName.isEmpty() && subResult.yearInEra >= 0 ) { 00233 ee = subResult.eraName; 00234 ey = subResult.yearInEra; 00235 error = false; 00236 strpos += subResult.inputPosition; 00237 } 00238 } 00239 --j; 00240 } 00241 } 00242 } else { 00243 yy = calendar->yearStringToInteger( str.mid( strpos ), readLength ); 00244 strpos += readLength; 00245 if ( fmtChar == QLatin1Char('y') ) { 00246 yy = calendar->applyShortYearWindow( yy ); 00247 } 00248 error = readLength <= 0; 00249 if ( !error ) { 00250 parsedYear = true; 00251 } 00252 } 00253 break; 00254 case 'C': // Era 00255 error = true; 00256 if ( modifierChar == QLatin1Char('E') ) { 00257 j = calendar->eraList()->count() -1; // Start with the most recent 00258 while ( error && j >= 0 ) { 00259 shortName = calendar->d_ptr->m_eraList->at( j ).name( KLocale::ShortName ).toLower(); 00260 longName = calendar->eraList()->at( j ).name( KLocale::LongName ).toLower(); 00261 if ( str.mid( strpos, longName.length() ) == longName ) { 00262 strpos += longName.length(); 00263 ee = longName; 00264 error = false; 00265 } else if ( str.mid( strpos, shortName.length() ) == shortName ) { 00266 strpos += shortName.length(); 00267 ee = shortName; 00268 error = false; 00269 } 00270 --j; 00271 } 00272 } 00273 break; 00274 case 'j': // Day Of Year Number 00275 dayInYear = integerFromString( str.mid( strpos ), 3, readLength ); 00276 strpos += readLength; 00277 error = readLength <= 0; 00278 break; 00279 case 'V': // ISO Week Number 00280 isoWeekNumber = integerFromString( str.mid( strpos ), 2, readLength ); 00281 strpos += readLength; 00282 error = readLength <= 0; 00283 break; 00284 case 'u': // ISO Day Of Week 00285 dayOfIsoWeek = integerFromString( str.mid( strpos ), 1, readLength ); 00286 strpos += readLength; 00287 error = readLength <= 0; 00288 break; 00289 } 00290 } 00291 } 00292 00293 DateTimeComponents result; 00294 result.error = error; 00295 result.inputPosition = strpos; 00296 result.formatPosition = fmtpos; 00297 if ( error ) { 00298 result.day = -1; 00299 result.month = -1; 00300 result.year = 0; 00301 result.parsedYear = false; 00302 result.eraName = QString(); 00303 result.yearInEra = -1; 00304 result.dayInYear = -1; 00305 result.isoWeekNumber = -1; 00306 result.dayOfIsoWeek = -1; 00307 } else { 00308 result.day = dd; 00309 result.month = mm; 00310 result.year = yy; 00311 result.parsedYear = parsedYear; 00312 result.eraName = ee; 00313 result.yearInEra = ey; 00314 result.dayInYear = dayInYear; 00315 result.isoWeekNumber = isoWeekNumber; 00316 result.dayOfIsoWeek = dayOfIsoWeek; 00317 } 00318 return result; 00319 } 00320 00321 // Parse an input string to match a UNICODE DateTime format string and return any components found 00322 DateTimeComponents KDateTimeParser::parseDateUnicode( const QString &inputString, 00323 const QString &formatString, 00324 const KCalendarSystem *calendar, 00325 const KLocale *locale, 00326 KLocale::DigitSet digitSet ) const 00327 { 00328 QString str = inputString.simplified().toLower(); 00329 QString fmt = formatString.simplified(); 00330 int dd = -1; 00331 int mm = -1; 00332 int yy = 0; 00333 bool parsedYear = false; 00334 int ey = -1; 00335 QString ee; 00336 int dayInYear = -1; 00337 int isoWeekNumber = -1; 00338 int dayOfIsoWeek = -1; 00339 int strpos = 0; 00340 int fmtpos = 0; 00341 int readLength; // Temporary variable used when reading input 00342 bool error = false; 00343 00344 DateTimeComponents result; 00345 result.error = error; 00346 result.inputPosition = strpos; 00347 result.formatPosition = fmtpos; 00348 if ( error ) { 00349 result.day = -1; 00350 result.month = -1; 00351 result.year = 0; 00352 result.parsedYear = false; 00353 result.eraName = QString(); 00354 result.yearInEra = -1; 00355 result.dayInYear = -1; 00356 result.isoWeekNumber = -1; 00357 result.dayOfIsoWeek = -1; 00358 } else { 00359 result.day = dd; 00360 result.month = mm; 00361 result.year = yy; 00362 result.parsedYear = parsedYear; 00363 result.eraName = ee; 00364 result.yearInEra = ey; 00365 result.dayInYear = dayInYear; 00366 result.isoWeekNumber = isoWeekNumber; 00367 result.dayOfIsoWeek = dayOfIsoWeek; 00368 } 00369 return result; 00370 } 00371 00372 // Peel a number off the front of a string which may have other trailing chars after the number 00373 // Stop either at either maxLength, eos, or first non-digit char 00374 int KDateTimeParser::integerFromString( const QString &string, int maxLength, int &readLength ) const 00375 { 00376 int value = -1; 00377 int position = 0; 00378 readLength = 0; 00379 bool ok = false; 00380 00381 if ( maxLength < 0 ) { 00382 maxLength = string.length(); 00383 } 00384 00385 while ( position < string.length() && 00386 position < maxLength && 00387 string.at( position ).isDigit() ) { 00388 position++; 00389 } 00390 00391 if ( position > 0 ) { 00392 value = string.left( position ).toInt( &ok ); 00393 if ( ok ) { 00394 readLength = position; 00395 } else { 00396 value = -1; 00397 } 00398 } 00399 00400 return value; 00401 }
KDE 4.6 API Reference