pimappsupport/chinesecalendaralg/pluginsrc/chinesecalendar.cpp
author Dremov Kirill (Nokia-D-MSW/Tampere) <kirill.dremov@nokia.com>
Fri, 12 Mar 2010 15:42:35 +0200
branchRCL_3
changeset 12 38571fd2a704
permissions -rw-r--r--
Revision: 201007 Kit: 201008

// Copyright (c) 2000-2009 Nokia Corporation and/or its subsidiary(-ies).
// All rights reserved.
// This component and the accompanying materials are made available
// under the terms of "Eclipse Public License v1.0"
// which accompanies this distribution, and is available
// at the URL "http://www.eclipse.org/legal/epl-v10.html".
//
// Initial Contributors:
// Nokia Corporation - initial contribution.
//
// Contributors:
//
// Description:
// Implementation of the TChineseCalendar class.
//

#include "chinesecalendar.h"
#include "arithmeticaldate.h"
#include "gregoriancalendar.h"

#if defined(_DEBUG)
_LIT(KCalconPanic,"Calcon");
#endif

const TUint KMonthInvalid=0;
const TUint K29DayMonth=1;
const TUint K30DayMonth=2;
const TUint K29DayLeapMonth=3;
const TUint K30DayLeapMonth=4;

const TUint KMonthMask=1<<15;
const TUint KLeapMonthShift=28;

const TInt KNoOfYearsInCycle = 60;

enum TCalconPanic
	{
	ECalconChineseFromFixedMonthInvalid=0,
	ECalconChineseToFixedLeapYearInvalid,
	ECalconGetDataYearOutOfRange,
	ECalconGetDataMonthOutOfRange,
	ECalconGetNewYearYearOutOfRange
	};

TUint TCalconData::GetData(TInt aCycle, TInt aYear, TInt aMonth) const
	{
	__ASSERT_DEBUG((aYear>=0 && aYear<=59), User::Panic(_L("Calcon"), ECalconGetDataYearOutOfRange));	//year range is 0..59
	__ASSERT_DEBUG((aMonth>=0 && aMonth<=12), User::Panic(_L("Calcon"), ECalconGetDataMonthOutOfRange));//month range is 0..12

	TInt y=(aCycle*KNoOfYearsInCycle)+aYear;
	y-=(KFirstYear-1);	//there are KFirstYear-1 entries missing from the head of the table
	TUint16 x=iCalConDataMonth[y];

	TUint mask=KMonthMask>>aMonth;
	
	TInt flag=K29DayMonth;
	if (x & mask)
		flag=K30DayMonth;

	TUint leapMonth=iCalConDataYear[y]>>KLeapMonthShift;
	leapMonth--;

	if ((TUint)aMonth==leapMonth)
		flag+=2;	//--> K29/30DayLeapMonth
	
	if ((aMonth==12) && (leapMonth==0))
		flag=KMonthInvalid;//month doesn't exist

	return flag;
	}

TUint TCalconData::GetNewYear(TInt aCycle, TInt aYear) const
	{
	__ASSERT_DEBUG((aYear>=0 && aYear<=59), User::Panic(_L("Calcon"), ECalconGetNewYearYearOutOfRange));
	TInt y=(aCycle*KNoOfYearsInCycle)+aYear;
	y-=(KFirstYear-1);
	return (iCalConDataYear[y] & 0x0fffffff);
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    ChineseToDateTime
// Arguments:   TDateTime &
//
// Comments:    This function converts the date held within
//				the TChineseCalendar class to a TDateTime format and 
//				places it in the TDateTime class provided.
//
// Return:      void
//------------------------------------------------------
void TChineseCalendar::ChineseToDateTime(TDateTime &aDT)
	{
	TArithmeticalDate gregDate;
	TGregorianCalendar greg(iJulianDay);

	greg.GregFromJulianDay(gregDate,iJulianDay);
	aDT.Set(0,EJanuary,0,0,0,0,0);

	aDT.SetDay(gregDate.iDay - KCalConvMonthOffsetByOne);
	aDT.SetMonth((TMonth)(gregDate.iMonth - KCalConvMonthOffsetByOne));
	aDT.SetYear(gregDate.iYear);
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    DateTimeToChinese
// Arguments:   TDateTime &
//
// Comments:    Sets the date held in the given TDateTime
//				class to the TChineseCalendar class
//
// Return:      void
//------------------------------------------------------
void TChineseCalendar::DateTimeToChinese(const TDateTime &aDT)
	{
	TArithmeticalDate gregDate;
	TGregorianCalendar greg;

	gregDate.iDay = aDT.Day() + KCalConvMonthOffsetByOne;
	gregDate.iMonth = (TInt)aDT.Month() + KCalConvMonthOffsetByOne;
	gregDate.iYear = aDT.Year();

	iJulianDay = greg.GregToJulianDay(gregDate);
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    SetDate
// Arguments:   TChineseDate&
//
// Comments:    this function sets the julian day value
//				in the base class from the given 
//				TChineseDate
//
// Return:      None
//------------------------------------------------------
TInt TChineseCalendar::SetDate(const TChineseDate &aDate)
	{
	TReal jD;
	TChineseDate ChinDate = aDate;
	
	if(!ValidDate(ChinDate))
		return KErrArgument;
	
	if (!ChineseToFixed(aDate,jD))
		return KErrArgument;

	if (jD<KFirstJulianDate || jD>KLastJulianDate)
		return KErrArgument;

	iJulianDay = (TInt)jD;
	return KErrNone;
	}
//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    GetDate
// Arguments:   TChineseDate &
//
// Comments:    This function Determines the chinese date and 
//				places it in the TChineseDate class provided
//				for the julian day value held internally in the 
//				Chinese class.
//
// Return:      None
//------------------------------------------------------
TInt TChineseCalendar::GetDate(TChineseDate &aDate)
	{
	return ChineseFromFixed(aDate,iJulianDay);
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    ChineseToFixed
// Arguments:   TChineseDate , TReal &
//				TChineseDate members start at 1 (not zero)
//
// Comments:    This function converts a chinese date to 
//				to a julian day value.
//
// Return:      TBool ETrue if date valid, else EFalse
//------------------------------------------------------
TBool TChineseCalendar::ChineseToFixed(const TChineseDate& aDate, TReal &aJulianDay) const
	{
	TInt cycle=aDate.iCycle-KFirstCycle;	//cycle starts from zero
	TInt year=aDate.iYear-1;				//year is 0..59

	TInt days=0;

	TInt targetMonth=aDate.iMonth;			//targetMonth is 1..12
	TBool leap=aDate.iLeapMonth;
	if (leap)
		{
		targetMonth++;
		if (iData.GetData(cycle, year, targetMonth-1)<3)
			return EFalse;	//not a leap month
		}

	TInt month=1;
	while (month<=targetMonth)
		{
		TInt daysFlag=iData.GetData(cycle, year, month-1);
		
//We need to handle case where targetMonth is a leap month
//Eg if Chinese month==6 targetMonth will be 6
//Eg but if Chinese month 5 is a leap month, month 6 will be 5(leap) so we need to take it into account
//BUT Eg if Chinese momth== 7(leap) we've already taken this into account.
		if (month==targetMonth)	
			if ((leap) || (daysFlag<3))
				break;

		switch (daysFlag)
			{
			case KMonthInvalid:
				return EFalse;
			case K29DayMonth:
				days+=29;
				break;
			case K30DayMonth:
				days+=30;
				break;
			case K29DayLeapMonth:
				__ASSERT_DEBUG(!leap, User::Panic(KCalconPanic,ECalconChineseToFixedLeapYearInvalid));
				leap=ETrue;
				targetMonth++;
				days+=29;
				break;
			case K30DayLeapMonth:
				__ASSERT_DEBUG(!leap, User::Panic(KCalconPanic,ECalconChineseToFixedLeapYearInvalid));
				leap=ETrue;
				targetMonth++;
				days+=30;
				break;
			}
		month++;
		}

//Check that if days==30, the requested month actually has 30 days
	TInt checkMonth = aDate.iMonth;
	if (leap)
		checkMonth++;	//this is the month requested by the user
	TUint daysFlag=iData.GetData(cycle, year, checkMonth-1);

	if ((aDate.iDay==30) && ((daysFlag==K29DayMonth) || (daysFlag==K29DayLeapMonth)))
		return EFalse;

	days+=aDate.iDay-1;
	
	days+=iData.GetNewYear(cycle, year);//add the New Year
	aJulianDay=days;
	return ETrue;
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    ChineseFromFixed
// Arguments:   TChineseDate &, TReal
//
// Comments:    this function converts a julian day value to
//				a chinese date in the form TChineseDate
//
// Return:      None
//------------------------------------------------------
TInt TChineseCalendar::ChineseFromFixed(TChineseDate &aDate, const TReal& aJulianDay) const
	{
	if ((aJulianDay<KFirstJulianDate)
		|| (aJulianDay>KLastJulianDate))
		return KErrArgument;

	TInt cycleIndex=0;

	while ((cycleIndex < KLastCycle-KFirstCycle) 
			&& (aJulianDay >= iData.GetNewYear(cycleIndex+1,0)))
		cycleIndex++;
	
	aDate.iCycle=cycleIndex + KFirstCycle;
	TInt chineseNewYear;
	TInt yearCount=0;
	if (cycleIndex==0)
		yearCount=KFirstYear-1;

	while (yearCount<60 && aJulianDay >= iData.GetNewYear(cycleIndex,yearCount))
		yearCount++;
		
	aDate.iYear=yearCount;
	chineseNewYear = iData.GetNewYear(cycleIndex,--yearCount);
	
	TInt addedNumberOfDays = 0;
	TInt previousAddedNumberOfDays = 0;
	TInt monthCount = 1;
	aDate.iMonth = 0;

	TInt monthNumber; // 0=No month exists, 1 = 29 day month, 2 = 30 day month, 3 = 29 day leap month, 4 = 30 day leap month

	while (aJulianDay >= (chineseNewYear + addedNumberOfDays))
		{
		previousAddedNumberOfDays = addedNumberOfDays;
		monthNumber = iData.GetData(cycleIndex,yearCount,monthCount-1);

		switch (monthNumber)
			{
		case KMonthInvalid:
			__ASSERT_DEBUG(0, User::Panic(_L("Calcon"),ECalconChineseFromFixedMonthInvalid));
			break;
		case K29DayMonth:
			addedNumberOfDays += 29;
			aDate.iMonth++;
			aDate.iLeapMonth = EFalse;
			break;
		case K30DayMonth:
			addedNumberOfDays += 30;
			aDate.iMonth++;
			aDate.iLeapMonth = EFalse;
			break;
		case K29DayLeapMonth:
			addedNumberOfDays += 29;
			aDate.iLeapMonth = ETrue;
			break;
		case K30DayLeapMonth:
			addedNumberOfDays += 30;
			aDate.iLeapMonth = ETrue;
			break;
			}
		monthCount++;
		}

	aDate.iDay = (TInt)aJulianDay - chineseNewYear-previousAddedNumberOfDays;
	aDate.iDay++;
	return KErrNone;
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    ValidDate
// Arguments:   TChineseDate &
//
// Comments:    This function Determines whether the given
//				date is a valid chinese date
//
// Return:      TBool - ETrue if date is valid, else EFalse
//------------------------------------------------------
TBool TChineseCalendar::ValidDate(const TChineseDate &aDate) const
	{
	//do some trivial checks to ensure that the date is in the range of the lookup table
	if (aDate.iYear==0 || aDate.iYear>KNoOfYearsInCycle)
		return EFalse;

	if (aDate.iCycle < KFirstCycle)
		return EFalse;

	if (aDate.iCycle==KFirstCycle && aDate.iYear < KFirstYear)
		return EFalse;
	
	if (aDate.iCycle > KLastCycle)
		return EFalse;

	if ( (aDate.iCycle==KLastCycle) && (aDate.iYear>KLastYear))
		return EFalse;

	if (aDate.iDay==0 || aDate.iDay>30)
		return EFalse;

	return ETrue;
	}

TReal TChineseCalendar::JulianDate() __SOFTFP
	{
	return iJulianDay;
	}