Bug 131342

Summary: KCalendarSystem::addMonths() does not always work for Hijri and Jalali calendars
Product: [Frameworks and Libraries] kdelibs Reporter: nq
Component: kdecoreAssignee: John Layt <jlayt>
Status: RESOLVED INTENTIONAL    
Severity: normal CC: jlayt
Priority: NOR    
Version: unspecified   
Target Milestone: ---   
Platform: Gentoo Packages   
OS: Linux   
Latest Commit: Version Fixed In:

Description nq 2006-07-25 11:32:11 UTC
Version:            (using KDE KDE 3.5.3)
Installed from:    Gentoo Packages
Compiler:          gcc-4.1.1 
OS:                Linux

addMonths(const QDate &date, int nmonths) in KCalendarSystemHijri and KCalendarSystemJalali does not return an incremented date if the day of initial date does not exist in the new month.

Example (with Hijri selected as calendar system):

QDate date;
KGlobal::locale()->calendar()->setYMD(date, 1427, 3, 30);
date = KGlobal::locale()->calendar()->addMonths(date, 1);
//date has not changed

Solution:

If day of initial date is greater than days in new month, then set day to last day in new month. 

change line 563 in kcalendarsystemhijri.cpp and line 300 in kcalendarsystemjalali.cpp from

setYMD( result, y, m, day(date));

to something like

setYMD(result, y, m, 1);
int d = day(date), nDaysInMonth = daysInMonth(result);
if(d > nDaysInMonth) setYMD(result, y, m, nDaysInMonth);
else setYMD(result, y, m, d);

or just copy  KCalendarSystemHebrew::addMonths()
Comment 1 nq 2006-07-25 12:32:56 UTC
KCalendarSystemHebrew::addMonths() does not work correctly either as it adds the number of days in the current month. 

Example:

QDate date;
KGlobal::locale()->calendar()->setYMD(date, 5766, 7, 30);
date = KGlobal::locale()->calendar()->addMonths(date, 1);
// date is now at month 9, day 1

Comment 2 nq 2006-07-28 12:00:06 UTC
Same problem with addYears().

Here is my generic functions for working solving the problems:

QDate KCalendarSystem::addMonths(const QDate &date, int nmonths) {
	QDate result = date;
	int m = month(date);
	int d =  day(date);
	if(nmonths > 0) {
		int nMonthsInYear = monthsInYear(result);
		while(nmonths > 0 && m + nmonths > nMonthsInYear) {
			setYMD(result, year(result) + 1, 1, 1);
			nmonths -= nMonthsInYear - m + 1;
			m = 1;
			nMonthsInYear = monthsInYear(result);
		}
		if(nmonths > 0) {
			setYMD(result, year(result), m + nmonths, 1);
		}
		if(d != 1) {
			int nDaysInMonth = daysInMonth(result);
			if(d > nDaysInMonth) result = addDays(result, nDaysInMonth - 1);
			else result = addDays(result, d - 1);
		}		
	} else if(nmonths < 0) {
		while(nmonths < 0 && m + nmonths < 1) {
			setYMD(result, year(result) - 1, 1, 1);
			int nMonthsInYear = monthsInYear(result);
			setYMD(result, year(result), nMonthsInYear, 1);
			nmonths += m;
			m = nMonthsInYear;
		}
		if(nmonths < 0) {
			setYMD(result, year(result), m + nmonths, 1);
		}
		if(d != 1) {
			int nDaysInMonth = daysInMonth(result);
			if(d > nDaysInMonth) result = addDays(result, nDaysInMonth - 1);
			else result = addDays(result, d - 1);
		}
	}
	return result;
}

QDate KCalendarSystem::addYears(const QDate &date, int nyears) {
	QDate result = date;
	int y = year(date) + nyears;
	int d = day(date);
	int m = month(date);
	setYMD(result, y, 1, 1);
	if(m > monthsInYear(result)) {
		setYMD(result, y + 1, 1, 1);
		addDays(result, -1);
	}	
	setYMD(result, y, m, 1);
	if(d > daysInMonth(result)) {
		d = daysInMonth(result);
	}
	setYMD(result, y, m, d);
	return result;
}
Comment 3 John Layt 2008-10-16 21:37:22 UTC
Assign to me to work on.  Check KDE4 which should be correct and back-port if possible.
Comment 4 John Layt 2009-03-15 19:41:35 UTC
Works OK in KDE4, will not be back-ported to KDE3 as there will probably not be anymore KDE3 releases.