File indexing completed on 2023-09-24 04:04:47
0001 /* 0002 Copyright 2009-2010 John Layt <john@layt.net> 0003 Copyright 2005-2010 David Jarvie <djarvie@kde.org> 0004 0005 This library is free software; you can redistribute it and/or 0006 modify it under the terms of the GNU Library General Public 0007 License as published by the Free Software Foundation; either 0008 version 2 of the License, or (at your option) any later version. 0009 0010 This library is distributed in the hope that it will be useful, 0011 but WITHOUT ANY WARRANTY; without even the implied warranty of 0012 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0013 Library General Public License for more details. 0014 0015 You should have received a copy of the GNU Library General Public License 0016 along with this library; see the file COPYING.LIB. If not, write to 0017 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0018 Boston, MA 02110-1301, USA. 0019 */ 0020 0021 #include "kdatetimeformatter_p.h" 0022 0023 #include <QDebug> 0024 #include <QDate> 0025 #include <QString> 0026 #include <QStringList> 0027 #include <QChar> 0028 0029 #include "kdatetime.h" 0030 #include "ktimezone.h" 0031 #include "kcalendarsystem.h" 0032 #include "kdayperiod_p.h" 0033 #include "klocale_p.h" 0034 0035 KDateTimeFormatter::KDateTimeFormatter() 0036 : m_englishLocale(nullptr), 0037 m_englishCalendar(nullptr) 0038 { 0039 } 0040 0041 KDateTimeFormatter::~KDateTimeFormatter() 0042 { 0043 delete m_englishCalendar; 0044 delete m_englishLocale; 0045 } 0046 0047 QString KDateTimeFormatter::formatDate(const QDate &fromDate, 0048 const QString &toFormat, 0049 const KCalendarSystem *calendar, 0050 const KLocale *locale, 0051 KLocale::DigitSet digitSet, 0052 KLocale::DateTimeFormatStandard formatStandard) const 0053 { 0054 // If not valid input, don't waste our time 0055 if (!calendar->isValid(fromDate) || toFormat.isEmpty()) { 0056 return QString(); 0057 } 0058 0059 return formatDateTime(KDateTime(fromDate), toFormat, KLocale::TimeFormatOption(), calendar, locale, digitSet, formatStandard); 0060 } 0061 0062 QString KDateTimeFormatter::formatTime(const QTime &fromTime, 0063 const QString &toFormat, 0064 KLocale::TimeFormatOptions timeOptions, 0065 const KCalendarSystem *calendar, 0066 const KLocale *locale, 0067 KLocale::DigitSet digitSet, 0068 KLocale::DateTimeFormatStandard formatStandard) const 0069 { 0070 // If not valid input, don't waste our time 0071 if (fromTime.isValid() || toFormat.isEmpty()) { 0072 return QString(); 0073 } 0074 0075 return formatDateTime(KDateTime(QDate::currentDate(), fromTime), toFormat, timeOptions, calendar, locale, digitSet, formatStandard); 0076 } 0077 0078 // Format an input date to match a POSIX date format string 0079 QString KDateTimeFormatter::formatDateTime(const KDateTime &fromDateTime, 0080 const QString &toFormat, 0081 KLocale::TimeFormatOptions timeOptions, 0082 const KCalendarSystem *calendar, 0083 const KLocale *locale, 0084 KLocale::DigitSet digitSet, 0085 KLocale::DateTimeFormatStandard formatStandard) const 0086 { 0087 // If not valid input, don't waste our time 0088 if (!fromDateTime.isValid() || !calendar->isValid(fromDateTime.date()) || toFormat.isEmpty()) { 0089 return QString(); 0090 } 0091 0092 if (formatStandard == KLocale::UnicodeFormat) { 0093 return formatDateTimeUnicode(fromDateTime, toFormat, timeOptions, calendar, locale, digitSet); 0094 } else { 0095 return formatDateTimePosix(fromDateTime, toFormat, timeOptions, calendar, locale, digitSet, formatStandard); 0096 } 0097 } 0098 0099 // Format an input date to match a POSIX date format string 0100 QString KDateTimeFormatter::formatDateTimePosix(const KDateTime &fromDateTime, 0101 const QString &toFormat, 0102 KLocale::TimeFormatOptions timeOptions, 0103 const KCalendarSystem *calendar, 0104 const KLocale *locale, 0105 KLocale::DigitSet digitSet, 0106 KLocale::DateTimeFormatStandard formatStandard) const 0107 { 0108 //qDebug() << "formatDateTimePosix(" << fromDateTime << toFormat << ")"; 0109 // If not valid input, don't waste our time 0110 if (!fromDateTime.isValid() || toFormat.isEmpty()) { 0111 return QString(); 0112 } 0113 0114 QChar thisChar; // Current toFormat char being processed 0115 QString result; // Output string 0116 0117 int padWidth = 0; // The width to pad numbers to 0118 QChar padChar = QLatin1Char('0'); // The char to use when padding numbers 0119 QChar signChar; // The sign to use when formatting numbers 0120 QChar caseChar; // The case modifier to use 0121 0122 bool escape = false; // Are we processing an escape char (%) 0123 bool escapeWidth = false; // Are we processing an escape width 0124 bool escapePad = false; // Are we processing an escape pad char 0125 bool escapeMod = false; // Are we processing an escape modifier 0126 int escapeIndex = 0; // Position in string of current escape char (%) 0127 0128 QChar modifierChar = QChar(); 0129 bool invalidModifier = false; 0130 0131 // Pre-fetch the core date components as they get used a lot 0132 // and it is 1/3rd more efficient than 3 separatre calls 0133 int year, month, day; 0134 calendar->getDate(fromDateTime.date(), &year, &month, &day); 0135 0136 for (int formatIndex = 0; formatIndex < toFormat.length(); ++formatIndex) { 0137 0138 thisChar = toFormat.at(formatIndex); 0139 0140 if (!escape) { 0141 0142 if (thisChar == QLatin1Char('%')) { 0143 escape = true; 0144 escapeIndex = formatIndex; 0145 } else { 0146 result.append(toFormat.at(formatIndex)); 0147 } 0148 0149 } else if (!escapeMod && !escapeWidth && thisChar == QLatin1Char('-')) { // no padding 0150 0151 padChar = QChar(); 0152 escapePad = true; 0153 0154 } else if (!escapeMod && !escapeWidth && thisChar == QLatin1Char('_')) { // space padding 0155 0156 padChar = QLatin1Char(' '); 0157 escapePad = true; 0158 0159 } else if (!escapeMod && !escapeWidth && thisChar == QLatin1Char('0')) { // 0 padding 0160 0161 padChar = QLatin1Char('0'); 0162 escapePad = true; 0163 0164 } else if (!escapeMod && !escapeWidth && (thisChar == QLatin1Char('^') || thisChar == QLatin1Char('#'))) { // Change case 0165 0166 caseChar = thisChar; 0167 0168 } else if (!escapeMod && 0169 ((!escapeWidth && thisChar >= QLatin1Char('1') && thisChar <= QLatin1Char('9')) || 0170 (escapeWidth && thisChar >= QLatin1Char('0') && thisChar <= QLatin1Char('9')))) { // Change width 0171 0172 if (escapeWidth) { 0173 padWidth = padWidth * 10; 0174 } 0175 padWidth = padWidth + QString(thisChar).toInt(); 0176 escapeWidth = true; 0177 0178 } else if (!escapeMod && (thisChar == QLatin1Char('E') || thisChar == QLatin1Char('O') || thisChar == QLatin1Char(':'))) { // Set modifier 0179 0180 escapeMod = true; 0181 modifierChar = thisChar; 0182 if (thisChar == QLatin1Char(':')) { 0183 invalidModifier = true; 0184 } 0185 0186 } else { 0187 0188 bool invalidComponent = false; 0189 QString componentString; 0190 int componentInteger = 0; 0191 int minWidth = 0; 0192 int isoWeekYear = year; 0193 QDate yearDate; 0194 KDateTime::SpecType timeSpecType; 0195 0196 //Default settings unless overridden by pad and case flags and width: are 0 pad to 0 width no sign 0197 //Names will override 0 pad with no pad unless flagged 0198 //Numbers will override with correct width unless flagged 0199 QChar thisChar = toFormat.at(formatIndex).unicode(); 0200 switch (thisChar.unicode()) { 0201 case '%': //Literal % 0202 if (modifierChar != QLatin1Char(':')) { // E and O mods are ignored if not used, but : is treated as literal 0203 componentString = QLatin1Char('%'); 0204 if (!escapePad) { 0205 padChar = QChar(); 0206 } 0207 } 0208 break; 0209 case 't': //Tab 0210 if (modifierChar != QLatin1Char(':')) { 0211 componentString = QString::fromLatin1("\t"); 0212 if (!escapePad) { 0213 padChar = QChar(); 0214 } 0215 } 0216 break; 0217 case 'Y': 0218 if (modifierChar == QLatin1Char('E')) { //Era Year, default no pad to 0 places no sign 0219 if (!escapePad) { 0220 padChar = QLatin1Char(' '); 0221 } 0222 componentString = calendar->eraYear(fromDateTime.date()); 0223 } else if (modifierChar != QLatin1Char(':')) { //Long year numeric, default 0 pad to 4 places with sign 0224 componentInteger = qAbs(year); 0225 minWidth = 4; 0226 if (year < 0) { 0227 signChar = QLatin1Char('-'); 0228 } 0229 } 0230 break; 0231 case 'C': 0232 if (modifierChar == QLatin1Char('E')) { //Era name, default no pad to 0 places no sign 0233 if (!escapePad) { 0234 padChar = QLatin1Char(' '); 0235 } 0236 componentString = calendar->eraName(fromDateTime.date()); 0237 } else if (modifierChar != QLatin1Char(':')) { //Century numeric, default 0 pad to 2 places with sign 0238 componentInteger = qAbs(year) / 100; 0239 minWidth = 2; 0240 if (year < 0) { 0241 signChar = QLatin1Char('-'); 0242 } 0243 } 0244 break; 0245 case 'y': 0246 if (modifierChar == QLatin1Char('E')) { //Year in Era number, default 0 pad to 1 places no sign 0247 componentInteger = calendar->yearInEra(fromDateTime.date()); 0248 minWidth = 1; 0249 } else if (modifierChar != QLatin1Char(':')) { //Short year numeric, default 0 pad to 2 places with sign 0250 componentInteger = qAbs(year) % 100; 0251 minWidth = 2; 0252 if (year < 0) { 0253 signChar = QLatin1Char('-'); 0254 } 0255 } 0256 break; 0257 case 'm': // Month numeric 0258 componentInteger = month; 0259 if (modifierChar == QLatin1Char(':')) { //Short month numeric, default no pad to 1 places no sign 0260 minWidth = 1; 0261 if (!escapePad) { 0262 padChar = QChar(); 0263 } 0264 invalidModifier = false; 0265 } else { //Long month numeric, default 0 pad to 2 places no sign 0266 componentInteger = month; 0267 minWidth = 2; 0268 } 0269 break; 0270 case 'n': 0271 //PosixFormat %n is newline 0272 //KdeFormat %n is short month numeric 0273 if (modifierChar != QLatin1Char(':')) { 0274 if (formatStandard == KLocale::KdeFormat) { 0275 //Copy what %e does, no padding by default 0276 //Short month numeric, default no pad to 1 places no sign 0277 componentInteger = month; 0278 minWidth = 1; 0279 if (!escapePad) { 0280 padChar = QChar(); 0281 } 0282 } else { // formatStandard == KLocale::PosixFormat 0283 componentString = QLatin1Char('\n'); 0284 } 0285 } 0286 break; 0287 case 'd': //Long day numeric, default 0 pad to 2 places no sign 0288 if (modifierChar != QLatin1Char(':')) { 0289 componentInteger = day; 0290 minWidth = 2; 0291 } 0292 break; 0293 case 'e': //Short day numeric, default no sign 0294 //PosixFormat %e is space pad to 2 places 0295 //KdeFormat %e is no pad to 1 place 0296 if (modifierChar != QLatin1Char(':')) { 0297 componentInteger = day; 0298 if (formatStandard == KLocale::KdeFormat) { 0299 minWidth = 1; 0300 if (!escapePad) { 0301 padChar = QChar(); 0302 } 0303 } else { // formatStandard == KLocale::PosixFormat 0304 minWidth = 2; 0305 if (!escapePad) { 0306 padChar = QLatin1Char(' '); 0307 } 0308 } 0309 } 0310 break; 0311 case 'B': //Long month name, default space pad to 0 places no sign 0312 if (locale->dateMonthNamePossessive()) { 0313 if (modifierChar == QLatin1Char(':')) { 0314 invalidModifier = false; 0315 initEnglish(calendar, locale); 0316 componentString = m_englishCalendar->monthName(month, year, KCalendarSystem::LongNamePossessive); 0317 } else { 0318 componentString = calendar->monthName(month, year, KCalendarSystem::LongNamePossessive); 0319 } 0320 } else { 0321 if (modifierChar == QLatin1Char(':')) { 0322 invalidModifier = false; 0323 initEnglish(calendar, locale); 0324 componentString = m_englishCalendar->monthName(month, year, KCalendarSystem::LongName); 0325 } else { 0326 componentString = calendar->monthName(month, year, KCalendarSystem::LongName); 0327 } 0328 } 0329 if (!escapePad) { 0330 padChar = QLatin1Char(' '); 0331 } 0332 break; 0333 case 'h': //Short month name, default space pad to 0 places no sign 0334 case 'b': //Short month name, default space pad to 0 places no sign 0335 if (locale->dateMonthNamePossessive()) { 0336 if (modifierChar == QLatin1Char(':')) { 0337 invalidModifier = false; 0338 initEnglish(calendar, locale); 0339 componentString = m_englishCalendar->monthName(month, year, KCalendarSystem::ShortNamePossessive); 0340 } else { 0341 componentString = calendar->monthName(month, year, KCalendarSystem::ShortNamePossessive); 0342 } 0343 } else { 0344 if (modifierChar == QLatin1Char(':')) { 0345 invalidModifier = false; 0346 initEnglish(calendar, locale); 0347 componentString = m_englishCalendar->monthName(month, year, KCalendarSystem::ShortName); 0348 } else { 0349 componentString = calendar->monthName(month, year, KCalendarSystem::ShortName); 0350 } 0351 } 0352 if (!escapePad) { 0353 padChar = QLatin1Char(' '); 0354 } 0355 break; 0356 case 'A': //Long weekday name, default space pad to 0 places no sign 0357 if (modifierChar == QLatin1Char(':')) { 0358 invalidModifier = false; 0359 initEnglish(calendar, locale); 0360 componentString = m_englishCalendar->weekDayName(fromDateTime.date(), KCalendarSystem::LongDayName); 0361 } else { 0362 componentString = calendar->weekDayName(fromDateTime.date(), KCalendarSystem::LongDayName); 0363 } 0364 if (!escapePad) { 0365 padChar = QLatin1Char(' '); 0366 } 0367 break; 0368 case 'a': //Short weekday name, default space pad to 0 places no sign 0369 if (modifierChar == QLatin1Char(':')) { 0370 invalidModifier = false; 0371 initEnglish(calendar, locale); 0372 componentString = m_englishCalendar->weekDayName(fromDateTime.date(), KCalendarSystem::ShortDayName); 0373 } else { 0374 componentString = calendar->weekDayName(fromDateTime.date(), KCalendarSystem::ShortDayName); 0375 } 0376 if (!escapePad) { 0377 padChar = QLatin1Char(' '); 0378 } 0379 break; 0380 case 'j': //Long day of year numeric, default 0 pad to 3 places no sign 0381 if (modifierChar != QLatin1Char(':')) { 0382 componentInteger = calendar->dayOfYear(fromDateTime.date()); 0383 minWidth = 3; 0384 } 0385 break; 0386 case 'V': //Long ISO week of year numeric, default 0 pad to 2 places no sign 0387 if (modifierChar != QLatin1Char(':')) { 0388 componentInteger = calendar->week(fromDateTime.date(), KLocale::IsoWeekNumber); 0389 minWidth = 2; 0390 } 0391 break; 0392 case 'G': //Long year of ISO week of year numeric, default 0 pad to 4 places with sign 0393 if (modifierChar != QLatin1Char(':')) { 0394 calendar->week(fromDateTime.date(), KLocale::IsoWeekNumber, &isoWeekYear); 0395 calendar->setDate(yearDate, isoWeekYear, 1, 1); 0396 componentInteger = qAbs(isoWeekYear); 0397 minWidth = 4; 0398 if (isoWeekYear < 0) { 0399 signChar = QLatin1Char('-'); 0400 } 0401 } 0402 break; 0403 case 'g': //Short year of ISO week of year numeric, default 0 pad to 2 places with sign 0404 if (modifierChar != QLatin1Char(':')) { 0405 calendar->week(fromDateTime.date(), KLocale::IsoWeekNumber, &isoWeekYear); 0406 calendar->setDate(yearDate, isoWeekYear, 1, 1); 0407 componentInteger = qAbs(isoWeekYear) % 100; 0408 minWidth = 2; 0409 if (isoWeekYear < 0) { 0410 signChar = QLatin1Char('-'); 0411 } 0412 } 0413 break; 0414 case 'u': 0415 if (modifierChar == QLatin1Char(':')) { // TZ UTC offset hours 0416 invalidModifier = false; 0417 KDateTime::SpecType timeSpecType = fromDateTime.timeType(); 0418 if (timeSpecType == KDateTime::UTC || timeSpecType == KDateTime::TimeZone || 0419 timeSpecType == KDateTime::OffsetFromUTC) { 0420 componentInteger = fromDateTime.utcOffset() / 3600; 0421 if (componentInteger >= 0) { 0422 signChar = QLatin1Char('+'); 0423 } else { 0424 componentInteger = -componentInteger; 0425 signChar = QLatin1Char('-'); 0426 } 0427 minWidth = 2; 0428 } 0429 } else { // Short day of week numeric 0430 componentInteger = calendar->dayOfWeek(fromDateTime.date()); 0431 minWidth = 1; 0432 } 0433 break; 0434 case 'D': // US short date format, ignore any overrides 0435 if (modifierChar != QLatin1Char(':')) { 0436 componentString = formatDateTimePosix(fromDateTime, QString::fromLatin1("%m/%d/%y"), timeOptions, calendar, locale, digitSet, formatStandard); 0437 padWidth = 0; 0438 padChar = QChar(); 0439 caseChar = QChar(); 0440 } 0441 break; 0442 case 'F': // Full or ISO short date format, ignore any overrides 0443 if (modifierChar != QLatin1Char(':')) { 0444 componentString = formatDateTimePosix(fromDateTime, QString::fromLatin1("%Y-%m-%d"), timeOptions, calendar, locale, digitSet, formatStandard); 0445 padWidth = 0; 0446 padChar = QChar(); 0447 caseChar = QChar(); 0448 } 0449 break; 0450 case 'x': // Locale short date format, ignore any overrides 0451 if (modifierChar != QLatin1Char(':')) { 0452 componentString = formatDateTimePosix(fromDateTime, locale->dateFormatShort(), timeOptions, calendar, locale, digitSet, formatStandard); 0453 padWidth = 0; 0454 padChar = QChar(); 0455 caseChar = QChar(); 0456 } 0457 break; 0458 case 'H': // Long 24 hour 0459 case 'k': // Short 24 hour 0460 if (modifierChar != QLatin1Char(':')) { 0461 componentInteger = fromDateTime.time().hour(); 0462 minWidth = 1; 0463 if (!escapePad) { 0464 padChar = QChar(); 0465 } 0466 } 0467 break; 0468 case 'I': // Long 12 hour 0469 case 'l': // Short 12 hour 0470 if (modifierChar != QLatin1Char(':')) { 0471 if ((timeOptions & KLocale::TimeDuration) == KLocale::TimeDuration) { 0472 componentInteger = fromDateTime.time().hour(); 0473 } else { 0474 componentInteger = locale->d->dayPeriodForTime(fromDateTime.time()).hourInPeriod(fromDateTime.time()); 0475 } 0476 if (thisChar == QLatin1Char('I')) { 0477 minWidth = 2; 0478 } else { 0479 minWidth = 1; 0480 if (!escapePad) { 0481 padChar = QChar(); 0482 } 0483 } 0484 } 0485 break; 0486 case 'M': // Long minutes 0487 if (modifierChar != QLatin1Char(':')) { 0488 componentInteger = fromDateTime.time().minute(); 0489 minWidth = 2; 0490 } 0491 break; 0492 case 'S': // Long seconds 0493 invalidModifier = false; 0494 if ((timeOptions & KLocale::TimeWithoutSeconds) == KLocale::TimeWithoutSeconds) { 0495 //TODO strip the preceding/following punctuation 0496 } else { 0497 componentInteger = fromDateTime.time().second(); 0498 if (modifierChar == QLatin1Char(':')) { // Only if not 00 seconds 0499 if (componentInteger > 0 || fromDateTime.time().msec() > 0) { 0500 result.append(QLatin1Char(':')); 0501 minWidth = 2; 0502 } 0503 } else { 0504 minWidth = 2; 0505 } 0506 } 0507 break; 0508 case 's': 0509 if (modifierChar == QLatin1Char(':')) { // Milliseconds 0510 invalidModifier = false; 0511 componentInteger = fromDateTime.time().msec(); 0512 minWidth = 3; 0513 } else { // Whole seconds since Unix Epoch 0514 KDateTime unixEpoch; 0515 unixEpoch.setTime_t(0); 0516 componentInteger = unixEpoch.secsTo(fromDateTime); 0517 } 0518 break; 0519 case 'p': // AM/PM symbol 0520 case 'P': // AM/PM symbol in lowercase 0521 if ((timeOptions & KLocale::TimeWithoutAmPm) == KLocale::TimeWithoutAmPm) { 0522 //TODO strip the preceding/following punctuation 0523 } else { 0524 if (modifierChar == QLatin1Char(':')) { 0525 invalidModifier = false; 0526 initEnglish(calendar, locale); 0527 componentString = m_englishLocale->d->dayPeriodForTime(fromDateTime.time()).periodName(KLocale::ShortName); 0528 } else { 0529 componentString = locale->d->dayPeriodForTime(fromDateTime.time()).periodName(KLocale::ShortName); 0530 } 0531 if (thisChar == QLatin1Char('P')) { 0532 componentString = componentString.toLower(); 0533 } 0534 } 0535 break; 0536 case 'z': // TZ UTC Offset 0537 invalidModifier = false; 0538 timeSpecType = fromDateTime.timeType(); 0539 if (timeSpecType == KDateTime::UTC || timeSpecType == KDateTime::TimeZone || 0540 timeSpecType == KDateTime::OffsetFromUTC) { 0541 if (modifierChar == QLatin1Char(':')) { // TZ UTC offset hours & minutes with colon 0542 int offsetInSeconds = fromDateTime.utcOffset(); 0543 if (offsetInSeconds >= 0) { 0544 signChar = QLatin1Char('+'); 0545 } else { 0546 offsetInSeconds = -offsetInSeconds; 0547 signChar = QLatin1Char('-'); 0548 } 0549 int offsetHours = offsetInSeconds / 3600; 0550 int offsetMinutes = (offsetInSeconds / 60) % 60; 0551 //int offsetSeconds = offsetInSeconds % 60; 0552 QString hourComponent = stringFromInteger(offsetHours, 2, QLatin1Char('0'), signChar, digitSet, locale); 0553 QString minuteComponent = stringFromInteger(offsetMinutes, 2, QLatin1Char('0'), QChar(), digitSet, locale); 0554 componentString = hourComponent + QLatin1Char(':') + minuteComponent; 0555 minWidth = 0; 0556 padChar = QChar(); 0557 padWidth = 0; 0558 } else { // TZ UTC offset hours & minutes 0559 componentInteger = fromDateTime.utcOffset() / 60; 0560 if (componentInteger >= 0) { 0561 signChar = QLatin1Char('+'); 0562 } else { 0563 componentInteger = -componentInteger; 0564 signChar = QLatin1Char('-'); 0565 } 0566 minWidth = 4; 0567 } 0568 } 0569 break; 0570 case 'Z': // TZ Name 0571 invalidModifier = false; 0572 timeSpecType = fromDateTime.timeType(); 0573 if (timeSpecType == KDateTime::UTC || timeSpecType == KDateTime::TimeZone) { 0574 KTimeZone tz = fromDateTime.timeZone(); 0575 if (tz.isValid()) { 0576 if (modifierChar == QLatin1Char(':')) { // TZ full name 0577 componentString = QString::fromLatin1(tz.abbreviation(fromDateTime.toUtc().dateTime())); 0578 } else { // TZ abbreviated name 0579 componentString = tz.name(); 0580 } 0581 } 0582 } 0583 break; 0584 default: //No valid format code, treat as literal 0585 invalidComponent = true; 0586 break; 0587 } 0588 0589 if (invalidComponent || invalidModifier) { // If escape sequence invalid treat as literal 0590 componentString = toFormat.mid(escapeIndex, formatIndex); 0591 } else if (componentString.isEmpty()) { //i.e. is a number component 0592 padWidth = qMax(minWidth, padWidth); 0593 componentString = stringFromInteger(componentInteger, padWidth, padChar, signChar, digitSet, locale); 0594 } else { //i.e. is a string component 0595 if (padChar != QChar() && padWidth != 0) { 0596 componentString = componentString.rightJustified(padWidth, padChar); 0597 } 0598 0599 if (caseChar == QLatin1Char('^')) { 0600 componentString = componentString.toUpper(); 0601 } else if (caseChar == QLatin1Char('#')) { 0602 componentString = componentString.toUpper(); // JPL ??? 0603 } 0604 } 0605 0606 result.append(componentString); 0607 0608 escape = false; 0609 escapePad = false; 0610 padChar = QLatin1Char('0'); 0611 escapeMod = false; 0612 invalidModifier = false; 0613 invalidComponent = false; 0614 modifierChar = QChar(); 0615 caseChar = QChar(); 0616 escapeWidth = false; 0617 padWidth = 0; 0618 signChar = QChar(); 0619 } 0620 } 0621 //qDebug() << " return = " << result; 0622 //qDebug() << ""; 0623 return result; 0624 } 0625 0626 void KDateTimeFormatter::initEnglish(const KCalendarSystem *calendar, const KLocale *locale) const 0627 { 0628 if (!m_englishCalendar || m_englishCalendar->calendarSystem() != calendar->calendarSystem()) { 0629 // Set up an English locale and calendar for use with ':' modifier which forces English names 0630 if (!m_englishLocale) { 0631 m_englishLocale = new KLocale(*locale); 0632 m_englishLocale->setLanguage(QStringList() << QString::fromLatin1("en_US")); 0633 } 0634 delete m_englishCalendar; 0635 m_englishCalendar = KCalendarSystem::create(calendar->calendarSystem(), m_englishLocale); 0636 } 0637 } 0638 0639 // Reimplement if special string handling required 0640 // Format an input date to match a UNICODE date format string 0641 // Original QDate::fmtDateTime() code taken from Qt 4.7 under LGPL, now heavily modifed 0642 // Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 0643 QString KDateTimeFormatter::formatDateTimeUnicode(const KDateTime &fromDateTime, 0644 const QString &toFormat, 0645 KLocale::TimeFormatOptions timeOptions, 0646 const KCalendarSystem *calendar, 0647 const KLocale *locale, 0648 KLocale::DigitSet digitSet) const 0649 { 0650 const QLatin1Char quote('\''); 0651 0652 QString result; 0653 QString format; 0654 QChar status(QLatin1Char('0')); 0655 0656 for (int i = 0; i < toFormat.length(); ++i) { 0657 if (toFormat.at(i) == quote) { 0658 if (status == quote) { 0659 if (i > 0 && toFormat.at(i - 1) == quote) { 0660 result += QLatin1Char('\''); 0661 } 0662 status = QLatin1Char('0'); 0663 } else { 0664 if (!format.isEmpty()) { 0665 result += getUnicodeString(fromDateTime, format, timeOptions, calendar, locale, digitSet); 0666 format.clear(); 0667 } 0668 status = quote; 0669 } 0670 } else if (status == quote) { 0671 result += toFormat.at(i); 0672 } else if (toFormat.at(i) == status) { 0673 if (toFormat.at(i) == QLatin1Char('P') || 0674 toFormat.at(i) == QLatin1Char('p')) { 0675 status = QLatin1Char('0'); 0676 } 0677 format += toFormat.at(i); 0678 } else { 0679 result += getUnicodeString(fromDateTime, format, timeOptions, calendar, locale, digitSet); 0680 format.clear(); 0681 if ((toFormat.at(i) == QLatin1Char('d')) || 0682 (toFormat.at(i) == QLatin1Char('M')) || 0683 (toFormat.at(i) == QLatin1Char('y'))) { 0684 status = toFormat.at(i); 0685 format += toFormat.at(i); 0686 } else { 0687 result += toFormat.at(i); 0688 status = QLatin1Char('0'); 0689 } 0690 } 0691 } 0692 0693 result += getUnicodeString(fromDateTime, format, timeOptions, calendar, locale, digitSet); 0694 0695 return result; 0696 } 0697 0698 // Original QDate::getFmtString() code taken from Qt 4.7 under LGPL, now heavily modifed 0699 // Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). 0700 // Replaces tokens by their value. See QDateTime::toString() for a list of valid tokens 0701 QString KDateTimeFormatter::getUnicodeString(const KDateTime &fromDateTime, 0702 const QString &toFormat, 0703 KLocale::TimeFormatOptions timeOptions, 0704 const KCalendarSystem *calendar, 0705 const KLocale *locale, 0706 KLocale::DigitSet digitSet) const 0707 { 0708 if (toFormat.isEmpty()) { 0709 return QString(); 0710 } 0711 0712 QString result = toFormat; 0713 int removed = 0; 0714 0715 if (toFormat.startsWith(QLatin1String("dddd"))) { 0716 result = calendar->weekDayName(fromDateTime.date(), KCalendarSystem::LongDayName); 0717 removed = 4; 0718 } else if (toFormat.startsWith(QLatin1String("ddd"))) { 0719 result = calendar->weekDayName(fromDateTime.date(), KCalendarSystem::ShortDayName); 0720 removed = 3; 0721 } else if (toFormat.startsWith(QLatin1String("dd"))) { 0722 result = QString::number(calendar->day(fromDateTime.date())).rightJustified(2, QLatin1Char('0'), true); 0723 removed = 2; 0724 } else if (toFormat.at(0) == QLatin1Char('d')) { 0725 result = QString::number(calendar->day(fromDateTime.date())); 0726 removed = 1; 0727 } else if (toFormat.startsWith(QLatin1String("MMMM"))) { 0728 result = calendar->monthName(calendar->month(fromDateTime.date()), calendar->year(fromDateTime.date()), KCalendarSystem::LongName); 0729 removed = 4; 0730 } else if (toFormat.startsWith(QLatin1String("MMM"))) { 0731 result = calendar->monthName(calendar->month(fromDateTime.date()), calendar->year(fromDateTime.date()), KCalendarSystem::ShortName); 0732 removed = 3; 0733 } else if (toFormat.startsWith(QLatin1String("MM"))) { 0734 result = QString::number(calendar->month(fromDateTime.date())).rightJustified(2, QLatin1Char('0'), true); 0735 removed = 2; 0736 } else if (toFormat.at(0) == QLatin1Char('M')) { 0737 result = QString::number(calendar->month(fromDateTime.date())); 0738 removed = 1; 0739 } else if (toFormat.startsWith(QLatin1String("yyyy"))) { 0740 const int year = calendar->year(fromDateTime.date()); 0741 result = QString::number(qAbs(year)).rightJustified(4, QLatin1Char('0')); 0742 if (year > 0) { 0743 removed = 4; 0744 } else { 0745 result.prepend(QLatin1Char('-')); 0746 removed = 5; 0747 } 0748 } else if (toFormat.startsWith(QLatin1String("yy"))) { 0749 const int year = calendar->year(fromDateTime.date()); 0750 result = QString::number(year).right(2).rightJustified(2, QLatin1Char('0')); 0751 if (year > 0) { 0752 removed = 2; 0753 } else { 0754 if (result.startsWith('0')) { 0755 result = result.right(1); 0756 } 0757 result.prepend(QLatin1Char('-')); 0758 removed = 3; 0759 } 0760 } 0761 0762 if (removed == 0 || removed >= toFormat.size()) { 0763 return result; 0764 } 0765 0766 return result + getUnicodeString(fromDateTime, toFormat.mid(removed), timeOptions, calendar, locale, digitSet); 0767 } 0768 0769 // Reimplement if special integer to string handling required, e.g. Hebrew. 0770 // Utility to convert an integer into the correct display string form 0771 QString KDateTimeFormatter::stringFromInteger(int number, int padWidth, QChar padChar, QChar signChar, 0772 KLocale::DigitSet digitSet, const KLocale *locale) const 0773 { 0774 if (padChar == QChar() && signChar == QChar()) { 0775 //qDebug() << " stringFromInteger(" << number << padWidth << "null" << "null" << ")"; 0776 } else if (padChar == QChar()) { 0777 //qDebug() << " stringFromInteger(" << number << padWidth << "null" << signChar << ")"; 0778 } else if (signChar == QChar()) { 0779 //qDebug() << " stringFromInteger(" << number << padWidth << padChar << "null" << ")"; 0780 } else if (signChar == QChar()) { 0781 //qDebug() << " stringFromInteger(" << number << padWidth << padChar << signChar << ")"; 0782 } 0783 QString result; 0784 if (padChar == QChar() || padWidth == 0) { // If null pad char or 0 width don't bother padding 0785 //qDebug() << " no pad"; 0786 if (signChar == QChar()) { 0787 result = locale->convertDigits(QString::number(number), digitSet); 0788 } else { 0789 result = locale->convertDigits(QString::number(number).prepend(signChar), digitSet); 0790 } 0791 } else if (signChar != QChar()) { // If sign required 0792 if (padChar == QLatin1Char('0')) { // If zero-padded, zero considered part of the number, so pad the number then prepend the sign 0793 //qDebug() << " zero pad with sign"; 0794 result = locale->convertDigits(QString::number(number).rightJustified(padWidth, padChar).prepend(signChar), digitSet); 0795 } else { // If space-padded space not considered part of the number, so prepend the sign and then pad the number 0796 //qDebug() << " space pad with sign"; 0797 result = locale->convertDigits(QString::number(number).prepend(signChar).rightJustified(padWidth, padChar), digitSet); 0798 } 0799 } else { // No sign required so just pad 0800 //qDebug() << " pad no sign"; 0801 result = locale->convertDigits(QString::number(number).rightJustified(padWidth, padChar), digitSet); 0802 } 0803 //qDebug() << " result = " << result; 0804 return result; 0805 }