kdelibs/kdecore/date/kdatetimeparser.cpp
Ivailo Monev 798a73eb0b kdecore: de-virtualize KDateTimeFormatter, KDateTimeParser and KDayPeriod
the classes are private and the methods are no reimplemented

Signed-off-by: Ivailo Monev <xakepa10@gmail.com>
2023-07-21 10:55:09 +03:00

393 lines
15 KiB
C++

/*
Copyright 2009, 2010 John Layt <john@layt.net>
This library is free software; you can redistribute it and/or
modify it under the terms of the GNU Library General Public
License as published by the Free Software Foundation; either
version 2 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
Library General Public License for more details.
You should have received a copy of the GNU Library General Public License
along with this library; see the file COPYING.LIB. If not, write to
the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
Boston, MA 02110-1301, USA.
*/
#include "kdatetimeparser_p.h"
#include "kcalendarsystemprivate_p.h"
#include "kcalendarsystem.h"
#include "kcalendarera_p.h"
#include "kdebug.h"
KDateTimeParser::KDateTimeParser()
{
}
// Parse a DateTime input string and return just the Date component
QDate KDateTimeParser::parseDate(const QString &inputString,
const QString &formatString,
const KCalendarSystem *calendar,
const KLocale *locale,
KLocale::DigitSet digitSet,
KLocale::DateTimeFormatStandard formatStandard) const
{
DateTimeComponents result;
if (formatStandard == KLocale::UnicodeFormat) {
result = parseDateUnicode(inputString, formatString);
} else {
result = parseDatePosix(inputString, formatString, calendar, locale, digitSet, formatStandard);
}
QDate resultDate;
if (!result.error &&
formatString.simplified().length() <= result.formatPosition &&
inputString.simplified().length() <= result.inputPosition) {
// If there were no parsing errors, and we have reached the end of both the input and
// format strings, then see if we have a valid date based on the components parsed
// If we haven't parsed a year component, then assume this year
if (!result.parsedYear) {
result.year = calendar->year(QDate::currentDate());
}
if ((!result.eraName.isEmpty() || result.yearInEra > -1) && result.month > 0 && result.day > 0) {
// Have parsed Era components as well as month and day components
calendar->setDate(resultDate, result.eraName, result.yearInEra, result.month, result.day);
} else if (result.month > 0 && result.day > 0) {
// Have parsed month and day components
calendar->setDate(resultDate, result.year, result.month, result.day);
} else if (result.dayInYear > 0) {
// Have parsed Day In Year component
calendar->setDate(resultDate, result.year, result.dayInYear);
} else if (result.isoWeekNumber > 0 && result.dayOfIsoWeek > 0) {
// Have parsed ISO Week components
calendar->setDateIsoWeek(resultDate, result.year, result.isoWeekNumber, result.dayOfIsoWeek);
}
}
return resultDate;
}
DateTimeComponents KDateTimeParser::parseDatePosix(const QString &inputString,
const QString &formatString,
const KCalendarSystem *calendar,
const KLocale *locale,
KLocale::DigitSet digitSet,
KLocale::DateTimeFormatStandard standard) const
{
QString str = inputString.simplified().toLower();
QString fmt = formatString.simplified();
int dd = -1;
int mm = -1;
int yy = 0;
bool parsedYear = false;
int ey = -1;
QString ee;
int dayInYear = -1;
int isoWeekNumber = -1;
int dayOfIsoWeek = -1;
int strpos = 0;
int fmtpos = 0;
int readLength; // Temporary variable used when reading input
bool error = false;
while (fmt.length() > fmtpos && str.length() > strpos && !error) {
QChar fmtChar = fmt.at(fmtpos++);
if (fmtChar != QLatin1Char('%')) {
if (fmtChar.isSpace() && str.at(strpos).isSpace()) {
strpos++;
} else if (fmtChar.toLower() == str.at(strpos)) {
strpos++;
} else {
error = true;
}
} else {
int j;
QString shortName, longName;
QChar modifierChar;
// remove space at the beginning
if (str.length() > strpos && str.at(strpos).isSpace()) {
strpos++;
}
fmtChar = fmt.at(fmtpos++);
if (fmtChar == QLatin1Char('E')) {
modifierChar = fmtChar;
fmtChar = fmt.at(fmtpos++);
}
switch (fmtChar.unicode()) {
case 'a': // Weekday Name Short
case 'A': // Weekday Name Long
error = true;
j = 1;
while (error && j <= calendar->d_ptr->maxDaysInWeek()) {
shortName = calendar->weekDayName(j, KCalendarSystem::ShortDayName).toLower();
longName = calendar->weekDayName(j, KCalendarSystem::LongDayName).toLower();
if (str.mid(strpos, longName.length()) == longName) {
strpos += longName.length();
error = false;
} else if (str.mid(strpos, shortName.length()) == shortName) {
strpos += shortName.length();
error = false;
}
++j;
}
break;
case 'b': // Month Name Short
case 'h': // Month Name Short
case 'B': // Month Name Long
error = true;
j = 1;
while (error && j <= calendar->d_ptr->maxMonthsInYear()) {
// This may be a problem in calendar systems with variable number of months
// in the year and/or names of months that change depending on the year, e.g
// Hebrew. We really need to know the correct year first, but we may not have
// read it yet and will be using the current year instead
int monthYear;
if (parsedYear) {
monthYear = yy;
} else {
monthYear = calendar->year(QDate::currentDate());
}
if (calendar->locale()->dateMonthNamePossessive()) {
shortName = calendar->monthName(j, monthYear, KCalendarSystem::ShortNamePossessive).toLower();
longName = calendar->monthName(j, monthYear, KCalendarSystem::LongNamePossessive).toLower();
} else {
shortName = calendar->monthName(j, monthYear, KCalendarSystem::ShortName).toLower();
longName = calendar->monthName(j, monthYear, KCalendarSystem::LongName).toLower();
}
if (str.mid(strpos, longName.length()) == longName) {
mm = j;
strpos += longName.length();
error = false;
} else if (str.mid(strpos, shortName.length()) == shortName) {
mm = j;
strpos += shortName.length();
error = false;
}
++j;
}
break;
case 'd': // Day Number Long
case 'e': // Day Number Short
dd = calendar->dayStringToInteger(str.mid(strpos), readLength);
strpos += readLength;
error = readLength <= 0;
break;
case 'n':
// PosixFormat %n is Newline
// KdeFormat %n is Month Number Short
if (standard == KLocale::KdeFormat) {
mm = calendar->monthStringToInteger(str.mid(strpos), readLength);
strpos += readLength;
error = readLength <= 0;
}
// standard == KLocale::PosixFormat
// all whitespace already 'eaten', no action required
break;
case 'm': // Month Number Long
mm = calendar->monthStringToInteger(str.mid(strpos), readLength);
strpos += readLength;
error = readLength <= 0;
break;
case 'Y': // Year Number Long
case 'y': // Year Number Short
if (modifierChar == QLatin1Char('E')) { // Year In Era
if (fmtChar == QLatin1Char('y')) {
ey = calendar->yearStringToInteger(str.mid(strpos), readLength);
strpos += readLength;
error = readLength <= 0;
} else {
error = true;
j = calendar->eraList()->count() - 1; // Start with the most recent
while (error && j >= 0) {
QString subFormat = calendar->eraList()->at(j).format();
QString subInput = str.mid(strpos);
DateTimeComponents subResult = parseDatePosix(subInput, subFormat, calendar, locale, digitSet, standard);
if (!subResult.error) {
if (subResult.parsedYear) {
yy = subResult.year;
parsedYear = true;
error = false;
strpos += subResult.inputPosition;
} else if (!subResult.eraName.isEmpty() && subResult.yearInEra >= 0) {
ee = subResult.eraName;
ey = subResult.yearInEra;
error = false;
strpos += subResult.inputPosition;
}
}
--j;
}
}
} else {
yy = calendar->yearStringToInteger(str.mid(strpos), readLength);
strpos += readLength;
if (fmtChar == QLatin1Char('y')) {
yy = calendar->applyShortYearWindow(yy);
}
error = readLength <= 0;
if (!error) {
parsedYear = true;
}
}
break;
case 'C': // Era
error = true;
if (modifierChar == QLatin1Char('E')) {
j = calendar->eraList()->count() - 1; // Start with the most recent
while (error && j >= 0) {
shortName = calendar->d_ptr->m_eraList->at(j).name(KLocale::ShortName).toLower();
longName = calendar->eraList()->at(j).name(KLocale::LongName).toLower();
if (str.mid(strpos, longName.length()) == longName) {
strpos += longName.length();
ee = longName;
error = false;
} else if (str.mid(strpos, shortName.length()) == shortName) {
strpos += shortName.length();
ee = shortName;
error = false;
}
--j;
}
}
break;
case 'j': // Day Of Year Number
dayInYear = integerFromString(str.mid(strpos), 3, readLength);
strpos += readLength;
error = readLength <= 0;
break;
case 'V': // ISO Week Number
isoWeekNumber = integerFromString(str.mid(strpos), 2, readLength);
strpos += readLength;
error = readLength <= 0;
break;
case 'u': // ISO Day Of Week
dayOfIsoWeek = integerFromString(str.mid(strpos), 1, readLength);
strpos += readLength;
error = readLength <= 0;
break;
}
}
}
DateTimeComponents result;
result.error = error;
result.inputPosition = strpos;
result.formatPosition = fmtpos;
if (error) {
result.day = -1;
result.month = -1;
result.year = 0;
result.parsedYear = false;
result.eraName.clear();
result.yearInEra = -1;
result.dayInYear = -1;
result.isoWeekNumber = -1;
result.dayOfIsoWeek = -1;
} else {
result.day = dd;
result.month = mm;
result.year = yy;
result.parsedYear = parsedYear;
result.eraName = ee;
result.yearInEra = ey;
result.dayInYear = dayInYear;
result.isoWeekNumber = isoWeekNumber;
result.dayOfIsoWeek = dayOfIsoWeek;
}
return result;
}
// Parse an input string to match a UNICODE DateTime format string and return any components found
DateTimeComponents KDateTimeParser::parseDateUnicode(const QString &inputString,
const QString &formatString) const
{
QString str = inputString.simplified().toLower();
QString fmt = formatString.simplified();
int dd = -1;
int mm = -1;
int yy = 0;
bool parsedYear = false;
int ey = -1;
QString ee;
int dayInYear = -1;
int isoWeekNumber = -1;
int dayOfIsoWeek = -1;
int strpos = 0;
int fmtpos = 0;
//int readLength; // Temporary variable used when reading input
bool error = false;
DateTimeComponents result;
result.error = error;
result.inputPosition = strpos;
result.formatPosition = fmtpos;
if (error) {
result.day = -1;
result.month = -1;
result.year = 0;
result.parsedYear = false;
result.eraName.clear();
result.yearInEra = -1;
result.dayInYear = -1;
result.isoWeekNumber = -1;
result.dayOfIsoWeek = -1;
} else {
result.day = dd;
result.month = mm;
result.year = yy;
result.parsedYear = parsedYear;
result.eraName = ee;
result.yearInEra = ey;
result.dayInYear = dayInYear;
result.isoWeekNumber = isoWeekNumber;
result.dayOfIsoWeek = dayOfIsoWeek;
}
return result;
}
// Peel a number off the front of a string which may have other trailing chars after the number
// Stop either at either maxLength, eos, or first non-digit char
int KDateTimeParser::integerFromString(const QString &string, int maxLength, int &readLength) const
{
int value = -1;
int position = 0;
readLength = 0;
bool ok = false;
if (maxLength < 0) {
maxLength = string.length();
}
while (position < string.length() &&
position < maxLength &&
string.at(position).isDigit()) {
position++;
}
if (position > 0) {
value = string.left(position).toInt(&ok);
if (ok) {
readLength = position;
} else {
value = -1;
}
}
return value;
}