summaryrefslogtreecommitdiffstats
path: root/calendar/kde/kdelibs/kdecore/date/kcalendarsystem.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'calendar/kde/kdelibs/kdecore/date/kcalendarsystem.cpp')
-rw-r--r--calendar/kde/kdelibs/kdecore/date/kcalendarsystem.cpp693
1 files changed, 693 insertions, 0 deletions
diff --git a/calendar/kde/kdelibs/kdecore/date/kcalendarsystem.cpp b/calendar/kde/kdelibs/kdecore/date/kcalendarsystem.cpp
new file mode 100644
index 0000000..4f8bc3f
--- /dev/null
+++ b/calendar/kde/kdelibs/kdecore/date/kcalendarsystem.cpp
@@ -0,0 +1,693 @@
+/*
+ Copyright (c) 2002 Carlos Moro <cfmoro@correo.uniovi.es>
+ Copyright (c) 2002 Hans Petter Bieker <bieker@kde.org>
+ Copyright (c) 2007 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 "kcalendarsystem.h"
+
+#include "kglobal.h"
+
+#include <QtCore/QDateTime>
+
+#include "kcalendarsystemgregorian.h"
+#include "kcalendarsystemhebrew.h"
+#include "kcalendarsystemhijri.h"
+#include "kcalendarsystemjalali.h"
+#include "kcalendarsystemindic.h"
+
+KCalendarSystem *KCalendarSystem::create( const QString &calendarType, const KLocale *locale )
+{
+ if ( calendarType == "hebrew" ) {
+ return new KCalendarSystemHebrew( locale );
+ }
+
+ if ( calendarType == "hijri" ) {
+ return new KCalendarSystemHijri( locale );
+ }
+
+ if ( calendarType == "gregorian" ) {
+ return new KCalendarSystemGregorian( locale );
+ }
+
+ if ( calendarType == "jalali" ) {
+ return new KCalendarSystemJalali( locale );
+ }
+ if ( calendarType == "indic" ) {
+ return new KCalendarSystemIndic( locale );
+ }
+
+ // ### HPB: Should it really be a default here?
+ return new KCalendarSystemGregorian( locale );
+}
+
+QStringList KCalendarSystem::calendarSystems()
+{
+ QStringList lst;
+
+ lst.append( "hebrew" );
+ lst.append( "hijri" );
+ lst.append( "gregorian" );
+ lst.append( "jalali" );
+ lst.append( "indic" );
+
+ return lst;
+}
+
+QString KCalendarSystem::calendarLabel( const QString &calendarType )
+{
+ if ( calendarType == "gregorian" ) {
+ return ki18nc( "@item Calendar system", "Gregorian" ).toString( KGlobal::locale() );
+ }
+
+ if ( calendarType == "hebrew" ) {
+ return ki18nc( "@item Calendar system", "Hebrew" ).toString( KGlobal::locale() );
+ }
+
+ if ( calendarType == "hijri" ) {
+ return ki18nc("@item Calendar system", "Hijri").toString( KGlobal::locale());
+ }
+
+ if ( calendarType == "jalali" ) {
+ return ki18nc( "@item Calendar system", "Jalali" ).toString( KGlobal::locale() );
+ }
+ if ( calendarType == "indic" ) {
+ return ki18nc( "@item Calendar system", "Indic" ).toString( KGlobal::locale() );
+ }
+ return ki18nc( "@item Calendar system", "Invalid Calendar Type" ).toString( KGlobal::locale() );
+}
+
+
+class KCalendarSystemPrivate
+{
+public:
+ KCalendarSystemPrivate( KCalendarSystem *q ): q( q )
+ {
+ }
+
+ ~KCalendarSystemPrivate()
+ {
+ }
+
+ KCalendarSystem *q;
+
+ bool setAnyDate( QDate &date, int year, int month, int day ) const;
+
+ int stringToInteger( const QString &sNum, int &iLength );
+
+ const KLocale *locale;
+};
+
+// Allows us to set dates outside publically valid range, USE WITH CARE!!!!
+bool KCalendarSystemPrivate::setAnyDate( QDate &date, int year, int month, int day ) const
+{
+ int jd;
+ q->dateToJulianDay( year, month, day, jd );
+ date = QDate::fromJulianDay( jd );
+ return true;
+}
+
+int KCalendarSystemPrivate::stringToInteger( const QString &sNum, int &iLength )
+{
+ int iPos = 0;
+ int result = 0;
+
+ for ( ; sNum.length() > iPos && sNum.at( iPos ).isDigit(); iPos++ ) {
+ result *= 10;
+ result += sNum.at( iPos ).digitValue();
+ }
+ iLength = iPos;
+
+ return result;
+}
+
+KCalendarSystem::KCalendarSystem( const KLocale *locale ) : d( new KCalendarSystemPrivate( this ) )
+{
+ d->locale = locale;
+}
+
+KCalendarSystem::~KCalendarSystem()
+{
+ delete d;
+}
+
+// This method MUST be re-implemented in any new Calendar System
+QDate KCalendarSystem::epoch() const
+{
+ // Earliest valid QDate
+ return QDate::fromJulianDay( 1 );
+}
+
+QDate KCalendarSystem::earliestValidDate() const
+{
+ return epoch();
+}
+
+// This method MUST be re-implemented in any new Calendar System
+QDate KCalendarSystem::latestValidDate() const
+{
+ // Default to Gregorian 9999-12-31
+ return QDate::fromJulianDay( 5373484 );
+}
+
+// This method MUST be re-implemented in any new Calendar System
+bool KCalendarSystem::isValid( int y, int month, int day ) const
+{
+ // Default to true Gregorian
+
+ if ( y < year( earliestValidDate() ) || y > year( latestValidDate() ) ) {
+ return false;
+ }
+
+ if ( month < 1 || month > 12 ) {
+ return false;
+ }
+
+ if ( month == 2 ) {
+ if ( isLeapYear( y ) ) {
+ return ( day >= 1 && day <= 29 );
+ } else {
+ return ( day >= 1 && day <= 28 );
+ }
+ }
+
+ if ( month == 4 || month == 6 || month == 9 || month == 11 ) {
+ return ( day >= 1 && day <= 30 );
+ }
+
+ return ( day >= 1 && day <= 31 );
+}
+
+bool KCalendarSystem::isValid( const QDate &date ) const
+{
+ if ( date.isNull() || date < earliestValidDate() || date > latestValidDate() ) {
+ return false;
+ }
+ return true;
+}
+
+bool KCalendarSystem::setDate( QDate &date, int year, int month, int day ) const
+{
+ if ( isValid( year, month, day ) ) {
+ int jd;
+ dateToJulianDay( year, month, day, jd );
+ date = QDate::fromJulianDay( jd );
+ return true;
+ }
+
+ return false;
+}
+
+// Deprecated
+bool KCalendarSystem::setYMD( QDate &date, int year, int month, int day ) const
+{
+ return setDate( date, year, month, day );
+}
+
+int KCalendarSystem::year( const QDate &date ) const
+{
+ if ( isValid( date ) ) {
+ int year, month, day;
+
+ julianDayToDate( date.toJulianDay(), year, month, day );
+
+ return year;
+ }
+
+ return 0; // How do you denote invalid year when we support -ve years?
+}
+
+int KCalendarSystem::month( const QDate &date ) const
+{
+ if ( isValid( date ) ) {
+ int year, month, day;
+
+ julianDayToDate( date.toJulianDay(), year, month, day );
+
+ return month;
+ }
+
+ return 0;
+}
+
+int KCalendarSystem::day( const QDate &date ) const
+{
+ if ( isValid( date ) ) {
+ int year, month, day;
+
+ julianDayToDate( date.toJulianDay(), year, month, day );
+
+ return day;
+ }
+
+ return 0;
+}
+
+QDate KCalendarSystem::addYears( const QDate &date, int numYears ) const
+{
+ if ( isValid( date ) ) {
+
+ int originalYear, originalMonth, originalDay;
+ int newYear, newMonth, newDay;
+ QDate firstOfNewMonth, newDate;
+
+ julianDayToDate( date.toJulianDay(), originalYear, originalMonth, originalDay );
+
+ newYear = originalYear + numYears;
+ newMonth = originalMonth;
+
+ //Adjust day number if new month has fewer days than old month
+ if ( setDate( firstOfNewMonth, newYear, newMonth, 1 ) ) {
+ int daysInNewMonth = daysInMonth( firstOfNewMonth );
+ newDay = ( daysInNewMonth < originalDay ) ? daysInNewMonth : originalDay;
+
+ if ( setDate( newDate, newYear, newMonth, newDay ) ) {
+ return newDate;
+ }
+ }
+
+ }
+
+ //Is QDate's way of saying is invalid
+ return QDate::fromJulianDay( 0 );
+}
+
+QDate KCalendarSystem::addMonths( const QDate &date, int numMonths ) const
+{
+ if ( isValid( date ) ) {
+
+ int originalYear, originalMonth, originalDay;
+ int newYear, newMonth, newDay;
+ int monthsInOriginalYear, daysInNewMonth;
+ QDate firstOfNewMonth, newDate;
+
+ julianDayToDate( date.toJulianDay(), originalYear, originalMonth, originalDay );
+
+ monthsInOriginalYear = monthsInYear( date );
+
+ newYear = originalYear + ( ( originalMonth + numMonths ) / monthsInOriginalYear );
+ newMonth = ( originalMonth + numMonths ) % monthsInOriginalYear;
+
+ if ( newMonth == 0 ) {
+ newYear = newYear - 1;
+ newMonth = monthsInOriginalYear;
+ }
+ if ( newMonth < 0 ) {
+ newYear = newYear - 1;
+ newMonth = newMonth + monthsInOriginalYear;
+ }
+
+ //Adjust day number if new month has fewer days than old month
+ if ( setDate( firstOfNewMonth, newYear, newMonth, 1 ) ) {
+ daysInNewMonth = daysInMonth( firstOfNewMonth );
+ newDay = ( daysInNewMonth < originalDay ) ? daysInNewMonth : originalDay;
+
+ if ( setDate( newDate, newYear, newMonth, newDay ) ) {
+ return newDate;
+ }
+ }
+
+ }
+
+ //Is QDate's way of saying is invalid
+ return QDate::fromJulianDay( 0 );
+}
+
+QDate KCalendarSystem::addDays( const QDate &date, int numDays ) const
+{
+ // QDate only holds a uint and has no boundary checking in addDays(), so we need to check
+ if ( isValid( date ) && (long) date.toJulianDay() + (long) numDays > 0 ) {
+ // QDate adds straight to jd
+ QDate temp = date.addDays( numDays );
+ if ( isValid( temp ) ) {
+ return temp;
+ }
+ }
+
+ //Is QDate's way of saying is invalid
+ return QDate::fromJulianDay( 0 );
+}
+
+int KCalendarSystem::monthsInYear( const QDate &date ) const
+{
+ // Last day of this year = first day of next year minus 1 day
+ // Use setAnyDate() to allow correct calculation in last valid year
+
+ if ( isValid( date ) ) {
+ QDate firstDayOfNextYear;
+ d->setAnyDate( firstDayOfNextYear, year( date ) + 1, 1, 1 );
+ QDate lastDayOfThisYear = addDays( firstDayOfNextYear, -1 );
+ return month( lastDayOfThisYear );
+ }
+
+ return -1;
+}
+
+int KCalendarSystem::weeksInYear( const QDate &date ) const
+{
+ if ( isValid( date ) ) {
+ return weeksInYear( year( date ) );
+ }
+ return -1;
+}
+
+// ISO compliant week numbering, not traditional number, rename in KDE5 to isoWeeksInYear()
+int KCalendarSystem::weeksInYear( int year ) const
+{
+ // Last day of this year = first day of next year minus 1 day
+ // Use setAnyDate() to allow correct calculation in last valid year
+
+ if ( isValid( year, 1, 1 ) ) {
+ QDate firstDayOfNextYear;
+ d->setAnyDate( firstDayOfNextYear, year + 1, 1, 1 );
+ QDate lastDayOfThisYear = addDays( firstDayOfNextYear, -1 );
+
+ int lastWeekInThisYear = weekNumber( lastDayOfThisYear );
+
+ // If the last day of the year is in the first week of next year use the week before
+ if ( lastWeekInThisYear == 1 ) {
+ lastDayOfThisYear = lastDayOfThisYear.addDays( -7 );
+ lastWeekInThisYear = weekNumber( lastDayOfThisYear );
+ }
+
+ return lastWeekInThisYear;
+ }
+
+ return -1;
+}
+
+int KCalendarSystem::daysInYear( const QDate &date ) const
+{
+ // Days in year = jd of first day of next year minus jd of first day of this year
+ // Use setAnyDate() to allow correct calculation in last valid year
+
+ if ( isValid( date ) ) {
+ QDate firstDayOfThisYear, firstDayOfNextYear;
+
+ setDate( firstDayOfThisYear, year( date ), 1, 1 );
+ d->setAnyDate( firstDayOfNextYear, year( date ) + 1, 1, 1 );
+
+ return ( firstDayOfNextYear.toJulianDay() - firstDayOfThisYear.toJulianDay() );
+ }
+
+ return -1;
+}
+
+int KCalendarSystem::daysInMonth( const QDate &date ) const
+{
+ // Days In Month = jd of first day of next month minus jd of first day of this month
+ // Use setAnyDate() to allow correct calculation in last valid year
+
+ if ( isValid( date ) ) {
+ QDate firstDayOfThisMonth, firstDayOfNextMonth;
+
+ int thisYear = year( date );
+ int thisMonth = month( date );
+
+ setDate( firstDayOfThisMonth, thisYear, thisMonth, 1 );
+
+ //check if next month falls in next year
+ if ( thisMonth < monthsInYear( date ) ) {
+ setDate( firstDayOfNextMonth, thisYear, thisMonth + 1, 1 );
+ } else {
+ d->setAnyDate( firstDayOfNextMonth, thisYear + 1, 1, 1 );
+ }
+
+ return ( firstDayOfNextMonth.toJulianDay() - firstDayOfThisMonth.toJulianDay() );
+ }
+
+ return -1;
+}
+
+int KCalendarSystem::daysInWeek( const QDate &date ) const
+{
+ Q_UNUSED( date );
+ return 7;
+}
+
+int KCalendarSystem::dayOfYear( const QDate &date ) const
+{
+ //Take the jd of the given date, and subtract the jd of the first day of that year
+
+ if ( isValid( date ) ) {
+ QDate firstDayOfYear;
+
+ if ( setDate( firstDayOfYear, year( date ), 1, 1 ) ) {
+ return ( date.toJulianDay() - firstDayOfYear.toJulianDay() + 1 );
+ }
+ }
+
+ return -1;
+}
+
+int KCalendarSystem::dayOfWeek( const QDate &date ) const
+{
+ // Makes assumption that Julian Day 0 was day 1 of week
+ // This is true for Julian/Gregorian calendar with jd 0 being Monday
+ // We add 1 for ISO compliant numbering for 7 day week
+ // Assumes we've never skipped weekdays
+ if ( isValid( date ) ) {
+ return ( ( date.toJulianDay() % daysInWeek( date ) ) + 1 );
+ }
+
+ return -1;
+}
+
+// ISO compliant week numbering, not traditional number, rename in KDE5 to isoWeekNumber()
+// JPL still need to fully clean up here
+int KCalendarSystem::weekNumber( const QDate &date, int *yearNum ) const
+{
+ if ( isValid( date ) ) {
+ QDate firstDayWeek1, lastDayOfYear;
+ int y = year( date );
+ int week;
+ int weekDay1, dayOfWeek1InYear;
+
+ // let's guess 1st day of 1st week
+ setDate( firstDayWeek1, y, 1, 1 );
+ weekDay1 = dayOfWeek( firstDayWeek1 );
+
+ // iso 8601: week 1 is the first containing thursday and week starts on monday
+ if ( weekDay1 > 4 /*Thursday*/ ) {
+ firstDayWeek1 = addDays( firstDayWeek1 , daysInWeek( date ) - weekDay1 + 1 ); // next monday
+ }
+
+ dayOfWeek1InYear = dayOfYear( firstDayWeek1 );
+
+ // our date in prev year's week
+ if ( dayOfYear( date ) < dayOfWeek1InYear ) {
+ if ( yearNum ) {
+ *yearNum = y - 1;
+ }
+ return weeksInYear( y - 1 );
+ }
+
+ // let's check if its last week belongs to next year
+ d->setAnyDate( lastDayOfYear, y + 1, 1, 1 );
+ lastDayOfYear = addDays( lastDayOfYear, -1 );
+ // if our date is in last week && 1st week in next year has thursday
+ if ( ( dayOfYear( date ) >= daysInYear( date ) - dayOfWeek( lastDayOfYear ) + 1 )
+ && dayOfWeek( lastDayOfYear ) < 4 ) {
+ if ( yearNum ) {
+ * yearNum = y + 1;
+ }
+ week = 1;
+ } else {
+ // To calculate properly the number of weeks from day a to x let's make a day 1 of week
+ if( weekDay1 < 5 ) {
+ firstDayWeek1 = addDays( firstDayWeek1, -( weekDay1 - 1 ) );
+ }
+
+ week = firstDayWeek1.daysTo( date ) / daysInWeek( date ) + 1;
+ }
+
+ return week;
+ }
+
+ return -1;
+}
+
+// This method MUST be reimplemented in any derived Calendar Systems
+bool KCalendarSystem::isLeapYear( int year ) const
+{
+ // Default to pure Gregorian
+
+ if ( year % 4 == 0 ) {
+ if ( year % 100 != 0 ) {
+ return true;
+ } else if ( year % 400 == 0 ) {
+ return true;
+ }
+ }
+ return false;
+}
+
+bool KCalendarSystem::isLeapYear( const QDate &date ) const
+{
+ return isLeapYear( year( date ) );
+}
+
+QString KCalendarSystem::monthName( const QDate &date, MonthNameFormat format ) const
+{
+ if ( isValid( date ) ) {
+ return monthName( month( date ), year( date ), format );
+ }
+
+ return QString();
+}
+
+QString KCalendarSystem::weekDayName( const QDate &date, WeekDayNameFormat format ) const
+{
+ if ( isValid( date ) ) {
+ return weekDayName( dayOfWeek( date ), format );
+ }
+
+ return QString();
+}
+
+QString KCalendarSystem::yearString( const QDate &date, StringFormat format ) const
+{
+ if ( isValid( date ) ) {
+ QString result;
+
+ result.setNum( year( date ) );
+ if ( format == ShortFormat && result.length() == 4 ) {
+ result = result.right( 2 );
+ }
+
+ return result;
+ }
+
+ return QString();
+}
+
+QString KCalendarSystem::monthString( const QDate &date, StringFormat format ) const
+{
+ if ( isValid( date ) ) {
+ QString result;
+
+ result.setNum( month( date ) );
+ if ( format == LongFormat && result.length() == 1 ) {
+ result.prepend( QLatin1Char( '0' ) );
+ }
+
+ return result;
+ }
+
+ return QString();
+}
+
+QString KCalendarSystem::dayString( const QDate &date, StringFormat format ) const
+{
+ if ( isValid( date ) ) {
+ QString result;
+
+ result.setNum( day( date ) );
+ if ( format == LongFormat && result.length() == 1 ) {
+ result.prepend( QLatin1Char( '0' ) );
+ }
+
+ return result;
+ }
+
+ return QString();
+}
+
+int KCalendarSystem::yearStringToInteger( const QString &yearString, int &iLength ) const
+{
+ return d->stringToInteger( yearString, iLength );
+}
+
+int KCalendarSystem::monthStringToInteger( const QString &monthString, int &iLength ) const
+{
+ return d->stringToInteger( monthString, iLength );
+}
+
+int KCalendarSystem::dayStringToInteger( const QString &dayString, int &iLength ) const
+{
+ return d->stringToInteger( dayString, iLength );
+}
+
+QString KCalendarSystem::formatDate( const QDate &date, KLocale::DateFormat format ) const
+{
+ return locale()->formatDate( date, format );
+}
+
+QDate KCalendarSystem::readDate( const QString &str, bool *ok ) const
+{
+ return locale()->readDate( str, ok );
+}
+
+QDate KCalendarSystem::readDate( const QString &intstr, const QString &fmt, bool *ok ) const
+{
+ return locale()->readDate( intstr, fmt, ok );
+}
+
+QDate KCalendarSystem::readDate( const QString &str, KLocale::ReadDateFlags flags, bool *ok ) const
+{
+ return locale()->readDate( str, flags, ok );
+}
+
+int KCalendarSystem::weekStartDay() const
+{
+ return locale()->weekStartDay();
+}
+
+// Fake version using QDate, each Calendar System MUST implement the correct version for themselves
+// The implementation MUST NOT do validity checking on date ranges, all calls to this function MUST
+// instead be wrapped in validity checks, as sometimes we want this to work outside the public valid
+// range, i.e. to allow us to internally set dates of 1/1/10000 which are not publically valid but
+// are required for internal maths
+bool KCalendarSystem::julianDayToDate( int jd, int &year, int &month, int &day ) const
+{
+ QDate date = QDate::fromJulianDay( jd );
+
+ if ( date.isValid() ) {
+ year = date.year();
+ month = date.month();
+ day = date.day();
+ }
+
+ return date.isValid();
+}
+
+// Fake version using QDate, each Calendar System MUST implement the correct version for themselves
+// The implementation MUST NOT do validity checking on date ranges, all calls to this function MUST
+// instead be wrapped in validity checks, as sometimes we want this to work outside the public valid
+// range, i.e. to allow us to internally set dates of 1/1/10000 which are not publically valid but
+// are required for internal maths
+bool KCalendarSystem::dateToJulianDay( int year, int month, int day, int &jd ) const
+{
+ QDate date;
+
+ if ( date.setDate( year, month, day ) ) {
+ jd = date.toJulianDay();
+ return true;
+ }
+
+ return false;
+}
+
+const KLocale * KCalendarSystem::locale() const
+{
+ if ( d->locale ) {
+ return d->locale;
+ }
+
+ return KGlobal::locale();
+}