#include #include #include "kdebug.h" #include "kcalendarsystemgregorian.h" #include "klocale.h" QDict *mLocaleDict = 0; void setLocaleDict( QDict * dict ) { mLocaleDict = dict; } QString i18n(const char *text) { if ( ! mLocaleDict ) return QString( text ); else { QString* ret = mLocaleDict->find(QString(text)) ; if ( ret == 0 ) { return QString( text ); } else { if ( (*ret).isEmpty() ) return QString( text ); else return (*ret); } } } QString i18n(const char *,const char *text) { return i18n( text ); } QString i18n(const char *text1, const char *textn, int num) { if ( num == 1 ) return i18n( text1 ); else { QString text = i18n( textn ); int pos = text.find( "%n" ); if ( pos >= 0 ) text.replace( pos, 2, QString::number( num ) ); return text; } } inline void put_it_in( QChar *buffer, uint& index, const QString &s ) { for ( uint l = 0; l < s.length(); l++ ) buffer[index++] = s.at( l ); } inline void put_it_in( QChar *buffer, uint& index, int number ) { buffer[index++] = number / 10 + '0'; buffer[index++] = number % 10 + '0'; } static int readInt(const QString &str, uint &pos) { if (!str.at(pos).isDigit()) return -1; int result = 0; for (; str.length() > pos && str.at(pos).isDigit(); pos++) { result *= 10; result += str.at(pos).digitValue(); } return result; } KLocale::KLocale() : mCalendarSystem( 0 ) { m_decimalSymbol = "."; m_positiveSign = ""; m_negativeSign = "-"; m_thousandsSeparator = ","; mWeekStartsMonday = true; mHourF24Format = true; mIntDateFormat = 0; mLanguage = 0; mDateFormat = "%a %Y %b %d"; mDateFormatShort = "%Y-%m-%d"; mTimeZoneList << i18n ("-11:00 US/Samoa") << i18n ("-10:00 US/Hawaii") << i18n ("-09:00 US/Alaska") << i18n ("-08:00 US/Pacific") << i18n ("-07:00 US/Mountain") << i18n ("-06:00 US/Central") << i18n ("-05:00 US/Eastern") << i18n ("-04:00 Brazil/West") << i18n ("-03:00 Brazil/East") << i18n ("-02:00 Brazil/DeNoronha") << i18n ("-01:00 Atlantic/Azores") << i18n (" 00:00 Europe/London(UTC)") << i18n ("+01:00 Europe/Oslo(CET)") << i18n ("+02:00 Europe/Helsinki") << i18n ("+03:00 Europe/Moscow") << i18n ("+04:00 Indian/Mauritius") << i18n ("+05:00 Indian/Maldives") << i18n ("+06:00 Indian/Chagos") << i18n ("+07:00 Asia/Bangkok") << i18n ("+08:00 Asia/Hongkong") << i18n ("+09:00 Asia/Tokyo") << i18n ("+10:00 Asia/Vladivostok") << i18n ("+11:00 Asia/Magadan") << i18n ("+12:00 Asia/Kamchatka") // << i18n (" xx:xx User defined offset") << i18n (" Local Time"); mSouthDaylight = false; mTimeZoneOffset = 0; daylightEnabled = false; } void KLocale::setDateFormat( QString s ) { mDateFormat = s; } void KLocale::setDateFormatShort( QString s ) { mDateFormatShort = s; } void KLocale::setHore24Format ( bool b ) { mHourF24Format = b; } void KLocale::setWeekStartMonday( bool b ) { mWeekStartsMonday = b; } int KLocale::getIntDateFormat( ) { return mIntDateFormat ; } void KLocale::setIntDateFormat( int i ) { mIntDateFormat = i; } void KLocale::setLanguage( int i ) { mLanguage = i; } QString KLocale::translate( const char *index ) const { return i18n( index ); } QString KLocale::translate( const char *, const char *fallback) const { return i18n( fallback ); } QString KLocale::formatTime(const QTime &pTime, bool includeSecs) const { const QString rst = timeFormat(); // only "pm/am" here can grow, the rest shrinks, but // I'm rather safe than sorry QChar *buffer = new QChar[rst.length() * 3 / 2 + 30]; uint index = 0; bool escape = false; int number = 0; for ( uint format_index = 0; format_index < rst.length(); format_index++ ) { if ( !escape ) { if ( rst.at( format_index ).unicode() == '%' ) escape = true; else buffer[index++] = rst.at( format_index ); } else { switch ( rst.at( format_index ).unicode() ) { case '%': buffer[index++] = '%'; break; case 'H': put_it_in( buffer, index, pTime.hour() ); break; case 'I': put_it_in( buffer, index, ( pTime.hour() + 11) % 12 + 1 ); break; case 'M': put_it_in( buffer, index, pTime.minute() ); break; case 'S': if (includeSecs) put_it_in( buffer, index, pTime.second() ); else { // we remove the seperator sign before the seconds and // assume that works everywhere --index; break; } break; case 'k': number = pTime.hour(); case 'l': // to share the code if ( rst.at( format_index ).unicode() == 'l' ) number = (pTime.hour() + 11) % 12 + 1; if ( number / 10 ) buffer[index++] = number / 10 + '0'; buffer[index++] = number % 10 + '0'; break; case 'p': { QString s; if ( pTime.hour() >= 12 ) put_it_in( buffer, index, i18n("pm") ); else put_it_in( buffer, index, i18n("am") ); break; } default: buffer[index++] = rst.at( format_index ); break; } escape = false; } } QString ret( buffer, index ); delete [] buffer; return ret; } QString KLocale::formatDate(const QDate &pDate, bool shortFormat) const { const QString rst = shortFormat?dateFormatShort():dateFormat(); // I'm rather safe than sorry QChar *buffer = new QChar[rst.length() * 3 / 2 + 50]; unsigned int index = 0; bool escape = false; int number = 0; for ( uint format_index = 0; format_index < rst.length(); ++format_index ) { if ( !escape ) { if ( rst.at( format_index ).unicode() == '%' ) escape = true; else buffer[index++] = rst.at( format_index ); } else { switch ( rst.at( format_index ).unicode() ) { case '%': buffer[index++] = '%'; break; case 'Y': put_it_in( buffer, index, pDate.year() / 100 ); case 'y': put_it_in( buffer, index, pDate.year() % 100 ); break; case 'n': number = pDate.month(); case 'e': // to share the code if ( rst.at( format_index ).unicode() == 'e' ) number = pDate.day(); if ( number / 10 ) buffer[index++] = number / 10 + '0'; buffer[index++] = number % 10 + '0'; break; case 'm': put_it_in( buffer, index, pDate.month() ); break; case 'b': put_it_in( buffer, index, monthName(pDate.month(), true) ); break; case 'B': put_it_in( buffer, index, monthName(pDate.month(), false) ); break; case 'd': put_it_in( buffer, index, pDate.day() ); break; case 'a': put_it_in( buffer, index, weekDayName(pDate.dayOfWeek(), true) ); break; case 'A': put_it_in( buffer, index, weekDayName(pDate.dayOfWeek(), false) ); break; default: buffer[index++] = rst.at( format_index ); break; } escape = false; } } QString ret( buffer, index ); delete [] buffer; return ret; } QString KLocale::formatDateTime(const QDateTime &pDateTime, bool shortFormat, bool includeSeconds) const { return QString( "%1 %2") .arg( formatDate( pDateTime.date(), shortFormat ) ) .arg( formatTime( pDateTime.time(), includeSeconds ) ); } QString KLocale::formatDateTime(const QDateTime &pDateTime) const { return formatDateTime(pDateTime, true); } QDate KLocale::readDate(const QString &intstr, bool* ok) const { QDate date; date = readDate(intstr, true, ok); if (date.isValid()) return date; return readDate(intstr, false, ok); } QDate KLocale::readDate(const QString &intstr, bool shortFormat, bool* ok) const { QString fmt = (shortFormat ? dateFormatShort() : dateFormat()).simplifyWhiteSpace(); return readDate( intstr, fmt, ok ); } QDate KLocale::readDate(const QString &intstr, const QString &fmt, bool* ok) const { //kdDebug(173) << "KLocale::readDate intstr=" << intstr << " fmt=" << fmt << endl; QString str = intstr.simplifyWhiteSpace().lower(); int day = -1, month = -1; // allow the year to be omitted if not in the format int year = QDate::currentDate().year(); uint strpos = 0; uint fmtpos = 0; while (fmt.length() > fmtpos || str.length() > strpos) { if ( !(fmt.length() > fmtpos && str.length() > strpos) ) goto error; QChar c = fmt.at(fmtpos++); if (c != '%') { if (c.isSpace()) strpos++; else if (c != str.at(strpos++)) goto error; continue; } // remove space at the begining if (str.length() > strpos && str.at(strpos).isSpace()) strpos++; c = fmt.at(fmtpos++); switch (c) { case 'a': case 'A': // this will just be ignored { // Cristian Tache: porting to Win: Block added because of "j" redefinition for (int j = 1; j < 8; j++) { QString s = weekDayName(j, c == 'a').lower(); int len = s.length(); if (str.mid(strpos, len) == s) strpos += len; } break; } case 'b': case 'B': { // Cristian Tache: porting to Win: Block added because of "j" redefinition for (int j = 1; j < 13; j++) { QString s = monthName(j, c == 'b').lower(); int len = s.length(); if (str.mid(strpos, len) == s) { month = j; strpos += len; } } break; } case 'd': case 'e': day = readInt(str, strpos); if (day < 1 || day > 31) goto error; break; case 'n': case 'm': month = readInt(str, strpos); if (month < 1 || month > 12) goto error; break; case 'Y': case 'y': year = readInt(str, strpos); if (year < 0) goto error; // Qt treats a year in the range 0-100 as 1900-1999. // It is nicer for the user if we treat 0-68 as 2000-2068 if (year < 69) year += 2000; else if (c == 'y') year += 1900; break; } } //kdDebug(173) << "KLocale::readDate day=" << day << " month=" << month << " year=" << year << endl; if ( year != -1 && month != -1 && day != -1 ) { if (ok) *ok = true; return QDate(year, month, day); } error: if (ok) *ok = false; return QDate(); // invalid date } QTime KLocale::readTime(const QString &intstr, bool *ok) const { QTime _time; _time = readTime(intstr, true, ok); if (_time.isValid()) return _time; return readTime(intstr, false, ok); } QTime KLocale::readTime(const QString &intstr, bool seconds, bool *ok) const { QString str = intstr.simplifyWhiteSpace().lower(); QString Format = timeFormat().simplifyWhiteSpace(); if (!seconds) Format.replace(QRegExp(QString::fromLatin1(".%S")), QString::null); int hour = -1, minute = -1, second = seconds ? -1 : 0; // don't require seconds bool g_12h = false; bool pm = false; uint strpos = 0; uint Formatpos = 0; while (Format.length() > Formatpos || str.length() > strpos) { if ( !(Format.length() > Formatpos && str.length() > strpos) ) goto error; QChar c = Format.at(Formatpos++); if (c != '%') { if (c.isSpace()) strpos++; else if (c != str.at(strpos++)) goto error; continue; } // remove space at the begining if (str.length() > strpos && str.at(strpos).isSpace()) strpos++; c = Format.at(Formatpos++); switch (c) { case 'p': { QString s; s = i18n("pm").lower(); int len = s.length(); if (str.mid(strpos, len) == s) { pm = true; strpos += len; } else { s = i18n("am").lower(); len = s.length(); if (str.mid(strpos, len) == s) { pm = false; strpos += len; } else goto error; } } break; case 'k': case 'H': g_12h = false; hour = readInt(str, strpos); if (hour < 0 || hour > 23) goto error; break; case 'l': case 'I': g_12h = true; hour = readInt(str, strpos); if (hour < 1 || hour > 12) goto error; break; case 'M': minute = readInt(str, strpos); if (minute < 0 || minute > 59) goto error; break; case 'S': second = readInt(str, strpos); if (second < 0 || second > 59) goto error; break; } } if (g_12h) { hour %= 12; if (pm) hour += 12; } if (ok) *ok = true; return QTime(hour, minute, second); error: if (ok) *ok = false; return QTime(-1, -1, -1); // return invalid date if it didn't work // This will be removed in the near future, since it gives a warning on stderr. // The presence of the bool* (since KDE-3.0) removes the need for an invalid QTime. } bool KLocale::use12Clock() const { return !mHourF24Format ;; } bool KLocale::weekStartsMonday() const { return mWeekStartsMonday; } int KLocale::weekStartDay() const { if ( mWeekStartsMonday ) return 1; return 7; } QString KLocale::weekDayName(int i,bool shortName) const { if ( shortName ) switch ( i ) { case 1: return i18n("Monday", "Mon"); case 2: return i18n("Tuesday", "Tue"); case 3: return i18n("Wednesday", "Wed"); case 4: return i18n("Thursday", "Thu"); case 5: return i18n("Friday", "Fri"); case 6: return i18n("Saturday", "Sat"); case 7: return i18n("Sunday", "Sun"); } else switch ( i ) { case 1: return i18n("Monday"); case 2: return i18n("Tuesday"); case 3: return i18n("Wednesday"); case 4: return i18n("Thursday"); case 5: return i18n("Friday"); case 6: return i18n("Saturday"); case 7: return i18n("Sunday"); } return QString::null; } QString KLocale::monthName(int i,bool shortName) const { if ( shortName ) switch ( i ) { case 1: return i18n("January", "Jan"); case 2: return i18n("February", "Feb"); case 3: return i18n("March", "Mar"); case 4: return i18n("April", "Apr"); case 5: return i18n("May short", "May"); case 6: return i18n("June", "Jun"); case 7: return i18n("July", "Jul"); case 8: return i18n("August", "Aug"); case 9: return i18n("September", "Sep"); case 10: return i18n("October", "Oct"); case 11: return i18n("November", "Nov"); case 12: return i18n("December", "Dec"); } else switch (i) { case 1: return i18n("January"); case 2: return i18n("February"); case 3: return i18n("March"); case 4: return i18n("April"); case 5: return i18n("May long", "May"); case 6: return i18n("June"); case 7: return i18n("July"); case 8: return i18n("August"); case 9: return i18n("September"); case 10: return i18n("October"); case 11: return i18n("November"); case 12: return i18n("December"); } return QString::null; } QString KLocale::country() const { return QString::null; } QString KLocale::dateFormat() const { if ( QApplication::desktop()->width() < 480 ) { if ( mIntDateFormat == 0 ) return "%a %d %b %Y"; else if ( mIntDateFormat == 1 ) return "%a %b %d %Y"; else if ( mIntDateFormat == 2 ) return "%a %Y %b %d"; } else { if ( mIntDateFormat == 0 ) return "%A %d %B %Y"; else if ( mIntDateFormat == 1 ) return "%A %B %d %Y"; else if ( mIntDateFormat == 2 ) return "%A %Y %B %d"; } return mDateFormat ; } QString KLocale::dateFormatShort() const { if ( mIntDateFormat == 0 ) return "%d.%m.%Y"; else if ( mIntDateFormat == 1 ) return "%m.%d.%Y"; else if ( mIntDateFormat == 2 ) return "%Y-%m-%d"; return mDateFormatShort ; } QString KLocale::timeFormat() const { if ( mHourF24Format) return "%H:%M:%S"; return "%I:%M:%S%p"; } void KLocale::insertCatalogue ( const QString & ) { } KCalendarSystem *KLocale::calendar() { if ( !mCalendarSystem ) { mCalendarSystem = new KCalendarSystemGregorian; } return mCalendarSystem; } int KLocale::timezoneOffset( QString timeZone ) { int ret = 1001; int index = mTimeZoneList.findIndex( timeZone ); if ( index < 24 ) ret = ( index-11 ) * 60 ; return ret; } QStringList KLocale::timeZoneList() const { return mTimeZoneList; } void KLocale::setTimezone( const QString &timeZone ) { mTimeZoneOffset = timezoneOffset( timeZone ); } void KLocale::setDaylightSaving( bool b, int start , int end ) { daylightEnabled = b; daylightStart = start; daylightEnd = end; mSouthDaylight = (end < start); // qDebug("klocale daylight %d %d %d ", b, start , end ); } int KLocale::localTimeOffset( const QDateTime &dt ) { bool addDaylight = false; if ( daylightEnabled ) { int d_end, d_start; int dayofyear = dt.date().dayOfYear(); int year = dt.date().year(); int add = 0; if ( QDate::leapYear(year) ) add = 1; QDate date ( year,1,1 ); if ( daylightEnd > 59 ) d_end = daylightEnd +add; else d_end = daylightEnd; if ( daylightStart > 59 ) d_start = daylightStart +add; else d_start = daylightStart; QDate s_date = date.addDays( d_start -1 ); QDate e_date = date.addDays( d_end -1 ); int dof = s_date.dayOfWeek(); if ( dof < 7 ) s_date = s_date.addDays( -dof ); dof = e_date.dayOfWeek(); if ( dof < 7 ) e_date = e_date.addDays( -dof ); QTime startTime ( 3,0,0 ); QDateTime startDt( s_date, startTime ); QDateTime endDt( e_date, startTime ); //qDebug("dayligt saving start %s end %s ",startDt.toString().latin1(),endDt.toString().latin1( )); if ( mSouthDaylight ) { if ( ! ( endDt < dt && dt < startDt) ) addDaylight = true; } else { if ( startDt < dt && dt < endDt ) addDaylight = true; } } int addMin = 0; if ( addDaylight ) addMin = 60; return mTimeZoneOffset + addMin; } // ****************************************************************** // added LR QString KLocale::formatNumber(double num, int precision) const { bool neg = num < 0; if (precision == -1) precision = 2; QString res = QString::number(neg?-num:num, 'f', precision); int pos = res.find('.'); if (pos == -1) pos = res.length(); else res.replace(pos, 1, decimalSymbol()); while (0 < (pos -= 3)) res.insert(pos, thousandsSeparator()); // thousand sep // How can we know where we should put the sign? res.prepend(neg?negativeSign():positiveSign()); return res; } QString KLocale::formatNumber(const QString &numStr) const { return formatNumber(numStr.toDouble()); } double KLocale::readNumber(const QString &_str, bool * ok) const { QString str = _str.stripWhiteSpace(); bool neg = str.find(negativeSign()) == 0; if (neg) str.remove( 0, negativeSign().length() ); /* will hold the scientific notation portion of the number. Example, with 2.34E+23, exponentialPart == "E+23" */ QString exponentialPart; int EPos; EPos = str.find('E', 0, false); if (EPos != -1) { exponentialPart = str.mid(EPos); str = str.left(EPos); } int pos = str.find(decimalSymbol()); QString major; QString minor; if ( pos == -1 ) major = str; else { major = str.left(pos); minor = str.mid(pos + decimalSymbol().length()); } // Remove thousand separators int thlen = thousandsSeparator().length(); int lastpos = 0; while ( ( pos = major.find( thousandsSeparator() ) ) > 0 ) { // e.g. 12,,345,,678,,922 Acceptable positions (from the end) are 5, 10, 15... i.e. (3+thlen)*N int fromEnd = major.length() - pos; if ( fromEnd % (3+thlen) != 0 // Needs to be a multiple, otherwise it's an error || pos - lastpos > 3 // More than 3 digits between two separators -> error || pos == 0 // Can't start with a separator || (lastpos>0 && pos-lastpos!=3)) // Must have exactly 3 digits between two separators { if (ok) *ok = false; return 0.0; } lastpos = pos; major.remove( pos, thlen ); } if (lastpos>0 && major.length()-lastpos!=3) // Must have exactly 3 digits after the last separator { if (ok) *ok = false; return 0.0; } QString tot; if (neg) tot = '-'; tot += major + '.' + minor + exponentialPart; return tot.toDouble(ok); } QString KLocale::decimalSymbol() const { return m_decimalSymbol; } QString KLocale::thousandsSeparator() const { return m_thousandsSeparator; } QString KLocale::positiveSign() const { return m_positiveSign; } QString KLocale::negativeSign() const { return m_negativeSign; }