KDELibs4Support

kdatetimeparser.cpp
1 /*
2  Copyright 2009, 2010 John Layt <[email protected]>
3 
4  This library is free software; you can redistribute it and/or
5  modify it under the terms of the GNU Library General Public
6  License as published by the Free Software Foundation; either
7  version 2 of the License, or (at your option) any later version.
8 
9  This library is distributed in the hope that it will be useful,
10  but WITHOUT ANY WARRANTY; without even the implied warranty of
11  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12  Library General Public License for more details.
13 
14  You should have received a copy of the GNU Library General Public License
15  along with this library; see the file COPYING.LIB. If not, write to
16  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
17  Boston, MA 02110-1301, USA.
18 */
19 
20 #include "kdatetimeparser_p.h"
21 
22 #include "kcalendarsystemprivate_p.h"
23 #include "kcalendarsystem.h"
24 #include "kcalendarera_p.h"
25 #include "kdebug.h"
26 
27 KDateTimeParser::KDateTimeParser()
28 {
29 }
30 
31 KDateTimeParser::~KDateTimeParser()
32 {
33 }
34 
35 // Parse a DateTime input string and return just the Date component
36 QDate KDateTimeParser::parseDate(const QString &inputString,
37  const QString &formatString,
38  const KCalendarSystem *calendar,
39  const KLocale *locale,
40  KLocale::DigitSet digitSet,
41  KLocale::DateTimeFormatStandard formatStandard) const
42 {
43  DateTimeComponents result;
44  if (formatStandard == KLocale::UnicodeFormat) {
45  result = parseDateUnicode(inputString, formatString, calendar, locale, digitSet);
46  } else {
47  result = parseDatePosix(inputString, formatString, calendar, locale, digitSet, formatStandard);
48  }
49 
50  QDate resultDate;
51 
52  if (!result.error &&
53  formatString.simplified().length() <= result.formatPosition &&
54  inputString.simplified().length() <= result.inputPosition) {
55 
56  // If there were no parsing errors, and we have reached the end of both the input and
57  // format strings, then see if we have a valid date based on the components parsed
58 
59  // If we haven't parsed a year component, then assume this year
60  if (!result.parsedYear) {
61  result.year = calendar->year(QDate::currentDate());
62  }
63 
64  if ((!result.eraName.isEmpty() || result.yearInEra > -1) && result.month > 0 && result.day > 0) {
65  // Have parsed Era components as well as month and day components
66  calendar->setDate(resultDate, result.eraName, result.yearInEra, result.month, result.day);
67  } else if (result.month > 0 && result.day > 0) {
68  // Have parsed month and day components
69  calendar->setDate(resultDate, result.year, result.month, result.day);
70  } else if (result.dayInYear > 0) {
71  // Have parsed Day In Year component
72  calendar->setDate(resultDate, result.year, result.dayInYear);
73  } else if (result.isoWeekNumber > 0 && result.dayOfIsoWeek > 0) {
74  // Have parsed ISO Week components
75  calendar->setDateIsoWeek(resultDate, result.year, result.isoWeekNumber, result.dayOfIsoWeek);
76  }
77 
78  }
79 
80  return resultDate;
81 }
82 
83 DateTimeComponents KDateTimeParser::parseDatePosix(const QString &inputString,
84  const QString &formatString,
85  const KCalendarSystem *calendar,
86  const KLocale *locale,
87  KLocale::DigitSet digitSet,
88  KLocale::DateTimeFormatStandard standard) const
89 {
90  QString str = inputString.simplified().toLower();
91  QString fmt = formatString.simplified();
92  int dd = -1;
93  int mm = -1;
94  int yy = 0;
95  bool parsedYear = false;
96  int ey = -1;
97  QString ee;
98  int dayInYear = -1;
99  int isoWeekNumber = -1;
100  int dayOfIsoWeek = -1;
101  int strpos = 0;
102  int fmtpos = 0;
103  int readLength; // Temporary variable used when reading input
104  bool error = false;
105 
106  while (fmt.length() > fmtpos && str.length() > strpos && !error) {
107 
108  QChar fmtChar = fmt.at(fmtpos++);
109 
110  if (fmtChar != QLatin1Char('%')) {
111 
112  if (fmtChar.isSpace() && str.at(strpos).isSpace()) {
113  strpos++;
114  } else if (fmtChar.toLower() == str.at(strpos)) {
115  strpos++;
116  } else {
117  error = true;
118  }
119 
120  } else {
121  int j;
122  QString shortName, longName;
123  QChar modifierChar;
124  // remove space at the beginning
125  if (str.length() > strpos && str.at(strpos).isSpace()) {
126  strpos++;
127  }
128 
129  fmtChar = fmt.at(fmtpos++);
130  if (fmtChar == QLatin1Char('E')) {
131  modifierChar = fmtChar;
132  fmtChar = fmt.at(fmtpos++);
133  }
134 
135  switch (fmtChar.unicode()) {
136  case 'a': // Weekday Name Short
137  case 'A': // Weekday Name Long
138  error = true;
139  j = 1;
140  while (error && j <= calendar->d_ptr->daysInWeek()) {
141  shortName = calendar->weekDayName(j, KCalendarSystem::ShortDayName).toLower();
142  longName = calendar->weekDayName(j, KCalendarSystem::LongDayName).toLower();
143  if (str.mid(strpos, longName.length()) == longName) {
144  strpos += longName.length();
145  error = false;
146  } else if (str.mid(strpos, shortName.length()) == shortName) {
147  strpos += shortName.length();
148  error = false;
149  }
150  ++j;
151  }
152  break;
153  case 'b': // Month Name Short
154  case 'h': // Month Name Short
155  case 'B': // Month Name Long
156  error = true;
157  j = 1;
158  while (error && j <= calendar->d_ptr->maxMonthsInYear()) {
159  // This may be a problem in calendar systems with variable number of months
160  // in the year and/or names of months that change depending on the year, e.g
161  // Hebrew. We really need to know the correct year first, but we may not have
162  // read it yet and will be using the current year instead
163  int monthYear;
164  if (parsedYear) {
165  monthYear = yy;
166  } else {
167  monthYear = calendar->year(QDate::currentDate());
168  }
169  if (calendar->locale()->dateMonthNamePossessive()) {
170  shortName = calendar->monthName(j, monthYear, KCalendarSystem::ShortNamePossessive).toLower();
171  longName = calendar->monthName(j, monthYear, KCalendarSystem::LongNamePossessive).toLower();
172  } else {
173  shortName = calendar->monthName(j, monthYear, KCalendarSystem::ShortName).toLower();
174  longName = calendar->monthName(j, monthYear, KCalendarSystem::LongName).toLower();
175  }
176  if (str.mid(strpos, longName.length()) == longName) {
177  mm = j;
178  strpos += longName.length();
179  error = false;
180  } else if (str.mid(strpos, shortName.length()) == shortName) {
181  mm = j;
182  strpos += shortName.length();
183  error = false;
184  }
185  ++j;
186  }
187  break;
188  case 'd': // Day Number Long
189  case 'e': // Day Number Short
190  dd = calendar->dayStringToInteger(str.mid(strpos), readLength);
191  strpos += readLength;
192  error = readLength <= 0;
193  break;
194  case 'n':
195  // PosixFormat %n is Newline
196  // KdeFormat %n is Month Number Short
197  if (standard == KLocale::KdeFormat) {
198  mm = calendar->monthStringToInteger(str.mid(strpos), readLength);
199  strpos += readLength;
200  error = readLength <= 0;
201  }
202  // standard == KLocale::PosixFormat
203  // all whitespace already 'eaten', no action required
204  break;
205  case 'm': // Month Number Long
206  mm = calendar->monthStringToInteger(str.mid(strpos), readLength);
207  strpos += readLength;
208  error = readLength <= 0;
209  break;
210  case 'Y': // Year Number Long
211  case 'y': // Year Number Short
212  if (modifierChar == QLatin1Char('E')) { // Year In Era
213  if (fmtChar == QLatin1Char('y')) {
214  ey = calendar->yearStringToInteger(str.mid(strpos), readLength);
215  strpos += readLength;
216  error = readLength <= 0;
217  } else {
218  error = true;
219  j = calendar->eraList()->count() - 1; // Start with the most recent
220  while (error && j >= 0) {
221  QString subFormat = calendar->eraList()->at(j).format();
222  QString subInput = str.mid(strpos);
223  DateTimeComponents subResult = parseDatePosix(subInput, subFormat, calendar, locale, digitSet, standard);
224  if (!subResult.error) {
225  if (subResult.parsedYear) {
226  yy = subResult.year;
227  parsedYear = true;
228  error = false;
229  strpos += subResult.inputPosition;
230  } else if (!subResult.eraName.isEmpty() && subResult.yearInEra >= 0) {
231  ee = subResult.eraName;
232  ey = subResult.yearInEra;
233  error = false;
234  strpos += subResult.inputPosition;
235  }
236  }
237  --j;
238  }
239  }
240  } else {
241  yy = calendar->yearStringToInteger(str.mid(strpos), readLength);
242  strpos += readLength;
243  if (fmtChar == QLatin1Char('y')) {
244  yy = calendar->applyShortYearWindow(yy);
245  }
246  error = readLength <= 0;
247  if (!error) {
248  parsedYear = true;
249  }
250  }
251  break;
252  case 'C': // Era
253  error = true;
254  if (modifierChar == QLatin1Char('E')) {
255  j = calendar->eraList()->count() - 1; // Start with the most recent
256  while (error && j >= 0) {
257  shortName = calendar->d_ptr->m_eraList->at(j).name(KLocale::ShortName).toLower();
258  longName = calendar->eraList()->at(j).name(KLocale::LongName).toLower();
259  if (str.mid(strpos, longName.length()) == longName) {
260  strpos += longName.length();
261  ee = longName;
262  error = false;
263  } else if (str.mid(strpos, shortName.length()) == shortName) {
264  strpos += shortName.length();
265  ee = shortName;
266  error = false;
267  }
268  --j;
269  }
270  }
271  break;
272  case 'j': // Day Of Year Number
273  dayInYear = integerFromString(str.mid(strpos), 3, readLength);
274  strpos += readLength;
275  error = readLength <= 0;
276  break;
277  case 'V': // ISO Week Number
278  isoWeekNumber = integerFromString(str.mid(strpos), 2, readLength);
279  strpos += readLength;
280  error = readLength <= 0;
281  break;
282  case 'u': // ISO Day Of Week
283  dayOfIsoWeek = integerFromString(str.mid(strpos), 1, readLength);
284  strpos += readLength;
285  error = readLength <= 0;
286  break;
287  }
288  }
289  }
290 
291  DateTimeComponents result;
292  result.error = error;
293  result.inputPosition = strpos;
294  result.formatPosition = fmtpos;
295  if (error) {
296  result.day = -1;
297  result.month = -1;
298  result.year = 0;
299  result.parsedYear = false;
300  result.eraName.clear();
301  result.yearInEra = -1;
302  result.dayInYear = -1;
303  result.isoWeekNumber = -1;
304  result.dayOfIsoWeek = -1;
305  } else {
306  result.day = dd;
307  result.month = mm;
308  result.year = yy;
309  result.parsedYear = parsedYear;
310  result.eraName = ee;
311  result.yearInEra = ey;
312  result.dayInYear = dayInYear;
313  result.isoWeekNumber = isoWeekNumber;
314  result.dayOfIsoWeek = dayOfIsoWeek;
315  }
316  return result;
317 }
318 
319 // Parse an input string to match a UNICODE DateTime format string and return any components found
320 DateTimeComponents KDateTimeParser::parseDateUnicode(const QString &inputString,
321  const QString &formatString,
322  const KCalendarSystem *calendar,
323  const KLocale *locale,
324  KLocale::DigitSet digitSet) const
325 {
326  Q_UNUSED(calendar);
327  Q_UNUSED(locale);
328  Q_UNUSED(digitSet);
329  Q_UNUSED(inputString);
330  Q_UNUSED(formatString);
331 
332  kWarning() << "KDateTimeParser::parseDateUnicode is not implemented";
333 
334  DateTimeComponents result;
335  result.error = true;
336  result.inputPosition = 0;
337  result.formatPosition = 0;
338  result.day = -1;
339  result.month = -1;
340  result.year = 0;
341  result.parsedYear = false;
342  result.eraName.clear();
343  result.yearInEra = -1;
344  result.dayInYear = -1;
345  result.isoWeekNumber = -1;
346  result.dayOfIsoWeek = -1;
347  return result;
348 }
349 
350 // Peel a number off the front of a string which may have other trailing chars after the number
351 // Stop either at either maxLength, eos, or first non-digit char
352 int KDateTimeParser::integerFromString(const QString &string, int maxLength, int &readLength) const
353 {
354  int value = -1;
355  int position = 0;
356  readLength = 0;
357  bool ok = false;
358 
359  if (maxLength < 0) {
360  maxLength = string.length();
361  }
362 
363  while (position < string.length() &&
364  position < maxLength &&
365  string.at(position).isDigit()) {
366  position++;
367  }
368 
369  if (position > 0) {
370  value = string.left(position).toInt(&ok);
371  if (ok) {
372  readLength = position;
373  } else {
374  value = -1;
375  }
376  }
377 
378  return value;
379 }
Long name possessive format, e.g.
bool setDateIsoWeek(QDate &date, int year, int isoWeekNumber, int dayOfIsoWeek) const
virtual int monthStringToInteger(const QString &sNum, int &iLength) const
QString simplified() const const
virtual QString weekDayName(int weekDay, WeekDayNameFormat format=LongDayName) const =0
Gets specific calendar type week day name.
int applyShortYearWindow(int inputYear) const
UNICODE Standard (Qt/Java/OSX/Windows)
Definition: klocale.h:697
KCalendarSystem abstract base class, provides support for local Calendar Systems in KDE...
void clear()
bool isSpace() const const
Long text format, e.g.
Definition: klocale.h:777
void error(QWidget *parent, const QString &text, const QString &caption=QString(), Options options=Notify)
const KLocale * locale() const
Returns the locale used for translations and formats for this calendar system instance.
Short name possessive format, e.g.
KDE Standard.
Definition: klocale.h:695
ushort unicode() const const
QString toLower() const const
QChar toLower() const const
virtual int yearStringToInteger(const QString &sNum, int &iLength) const
Short text format, e.g.
Definition: klocale.h:776
virtual int year(const QDate &date) const
Returns the year portion of a given date in the current calendar system.
bool dateMonthNamePossessive() const
Use this to determine whether in dates a possessive form of month name is preferred ("of January" rat...
Definition: klocale.cpp:145
Short name format, e.g.
QString mid(int position, int n) const const
virtual QString monthName(int month, int year, MonthNameFormat format=LongName) const =0
Gets specific calendar type month name for a given month number If an invalid month is specified...
KLocale provides support for language and country specific stuff.
Definition: klocale.h:75
DateTimeFormatStandard
Definition: klocale.h:694
QDate currentDate()
const QChar at(int position) const const
int length() const const
Short name format, e.g.
virtual int dayStringToInteger(const QString &sNum, int &iLength) const
DigitSet
Definition: klocale.h:176
Long name format, e.g.
int year() const const
virtual bool setDate(QDate &date, int year, int month, int day) const
Changes the date&#39;s year, month and day.
int month() const const
Long name format, e.g.
This file is part of the KDE documentation.
Documentation copyright © 1996-2020 The KDE developers.
Generated on Sat Jul 4 2020 22:58:57 by doxygen 1.8.11 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.