pimappsupport/chinesecalendaralg/originalsrc/Chinese.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.
// 
//

// User includes
#include "calconv.h"

// Constants
const TInt KChinStartEpoch = 758325;
const TInt KFifteenth = 15;
const TInt KDegreesPerMonth = 30;
const TInt KMonthsInYear = 12;
const TInt KFirstChinYear = -2636;
const TInt KYearsInChinCycle = 60;
const TInt KCycleOffset = 1;


//
// Construction/Destruction
//

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    TChineseCalendar
// Arguments:   None
//
// Comments:    Constructer
//
// Return:      None
//------------------------------------------------------
EXPORT_C TChineseCalendar::TChineseCalendar()
	{
	iStartEpoch = KChinStartEpoch;
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    CurrentMajorSolarTerm
// Arguments:   TReal
//
// Comments:    Returns the index of the last major solar term
//
// Return:      see comment
//------------------------------------------------------
TReal TChineseCalendar::CurrentMajorSolarTerm(TReal& aJulianDay) const
	{
	TReal sigma;
	TInt sigmaI;

	AdjustJDToNoon(aJulianDay);

	sigma = ChineseTimeZone(aJulianDay);
	sigma = UniversalFromLocal(aJulianDay,sigma);
	SolarLongitude(sigma,sigma);
	sigma /= 30.0;
	Floor(sigmaI,sigma);
	sigmaI += 2;
	Amod(sigma,sigmaI,12.0);
	Round(sigmaI,sigma);
	return sigmaI;
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    ChineseTimeZone
// Arguments:   TReal
//
// Comments:    Determines which time zone to use
//
// Return:      TReal - time zone
//------------------------------------------------------
TReal TChineseCalendar::ChineseTimeZone(const TReal& aJulianDay) const
	{
	TReal result;
	TGregorianCalendar greg(aJulianDay);
	TArithmeticalDate date;

	greg.GetDate(date);

	if(date.iYear < 1929)
		{
		result = 465.0 + (40.0 / 60.0);
		}
	else
		{
		result = 480.0;
		}
	return result;
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    ChineseDateNextSolarLongitudeL
// Arguments:   TReal , TReal
//
// Comments:    calculates the first date on or after the given
//				julian day when the solar longitude will be a 
//				multiple of aDegrees degrees. 
//
// Return:      TInt - see comment
//------------------------------------------------------
TInt TChineseCalendar::ChineseDateNextSolarLongitude(TReal& aJulianDay, const TReal& aDegrees) const
	{
	TReal chTimeZone;
	TReal chDateNxtSlrLong;
	TInt rtn;

	AdjustJDToNoon(aJulianDay);
	chTimeZone = ChineseTimeZone(aJulianDay);

	chDateNxtSlrLong = UniversalFromLocal(aJulianDay,chTimeZone);
	chDateNxtSlrLong = DateNextSolarLongitude(chDateNxtSlrLong,aDegrees);
	chDateNxtSlrLong = LocalFromUniversal(chDateNxtSlrLong,chTimeZone);

	AdjustJDFromNoon(chDateNxtSlrLong);
	Floor(rtn,chDateNxtSlrLong);

	return rtn;
	}


//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    MajorSolarTermOnOrAfter
// Arguments:   TReal
//
// Comments:    Determines the major solar term on or after 
//				the given julian day.
//
// Return:      TReal - see comment
//------------------------------------------------------
TReal TChineseCalendar::MajorSolarTermOnOrAfter(TReal& aJulianDay) const
	{
	TReal majorSolarTerm;
	majorSolarTerm = ChineseDateNextSolarLongitude(aJulianDay,KDegreesPerMonth);
	return majorSolarTerm;
	}

//------------------------------------------------------
// 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
//------------------------------------------------------
EXPORT_C void TChineseCalendar::GetDate(TChineseDate &aDate)
	{
	ChineseFromFixed(aDate,iJulianDay);
	}


//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    ChineseNewMoonBefore
// Arguments:   TReal
//
// Comments:    corrects for chinese month starting at noon
//				in Beijing
//
// Return:      TInt - new moon
//------------------------------------------------------
TInt TChineseCalendar::ChineseNewMoonBefore(TReal aJulianDay) const
	{
	TReal newMoon;
	TReal timeZone;
	TInt rtn;

	AdjustJDToNoon(aJulianDay);

	timeZone = ChineseTimeZone(aJulianDay);
	newMoon = UniversalFromLocal(aJulianDay,timeZone);
	newMoon = NewMoonBefore(newMoon);
	newMoon = LocalFromUniversal(newMoon,timeZone);

	AdjustJDFromNoon(newMoon);
	Floor(rtn,newMoon);

	return rtn;
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    ChineseNewMoonOnOrAfter
// Arguments:   TReal
//
// Comments:    corrects for chinese month starting at noon
//				in Beijing
//
// Return:      TInt - new moon
//------------------------------------------------------
TInt TChineseCalendar::ChineseNewMoonOnOrAfter(TReal aJulianDay) const
	{
	TReal newMoon;
	TReal timeZone;
	TInt rtn;

	AdjustJDToNoon(aJulianDay);

	timeZone = ChineseTimeZone(aJulianDay);
	newMoon = UniversalFromLocal(aJulianDay,timeZone);
	newMoon = NewMoonAtOrAfter(newMoon);
	newMoon = LocalFromUniversal(newMoon,timeZone);

	AdjustJDFromNoon(newMoon);
	Floor(rtn,newMoon);

	return rtn;
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    NoMajorSolarTerm
// Arguments:   TReal
//
// Comments:    Determines whether a given month lacks a 
//				major solar term.  The first month without
//				a major solar term is a leap month.
//
// Return:      TBool - ETrue if month has no major solar term
//------------------------------------------------------
TBool TChineseCalendar::NoMajorSolarTerm(TReal aJulianDay) const
	{
	TReal chinNewMoon;
	TReal currentMajSolTrm1;
	TReal currentMajSolTrm2;

	chinNewMoon = ChineseNewMoonOnOrAfter(aJulianDay + KCalConvDayOffsetByOne);
	currentMajSolTrm1 = CurrentMajorSolarTerm(chinNewMoon);
	currentMajSolTrm2 = CurrentMajorSolarTerm(aJulianDay);
	
	return(currentMajSolTrm1 == currentMajSolTrm2);
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    PriorLeapMonth
// Arguments:   TReal , TReal
//
// Comments:    this function is used in conjunction with
//				NoMajorSolarTerm() to ensure that only the 
//				first month missing a major solar term is 
//				a leap month
//
// Return:      TBool - 
//------------------------------------------------------
TBool TChineseCalendar::PriorLeapMonth(TReal& aMnthprime, TReal aMnth) const
	{
	TBool rtn,majorSolarTerm;
	TReal newMoonBefore;

	majorSolarTerm = NoMajorSolarTerm(aMnth);
	newMoonBefore = ChineseNewMoonBefore(aMnth);

	if((aMnth >= aMnthprime) && 
		(PriorLeapMonth(aMnthprime,newMoonBefore) || majorSolarTerm))
		{
		rtn = ETrue;
		}
	else
		{
		rtn = EFalse;
		}
	return rtn;
	}

//------------------------------------------------------
// Class:       TChineseCalendar
// Function:    ChineseNewYear
// Arguments:   TInt
//
// Comments:    Determines the chinese new year
//
// Return:      TReal - Julian day of the start of the given year
//------------------------------------------------------
TReal TChineseCalendar::ChineseNewYear(TInt aYear) const
	{
	TGregorianCalendar greg(iJulianDay);
	TArithmeticalDate date;
	TReal solarTerm1;
	TReal solarTerm2;
	TReal newMoon1;
	TReal newMoon2;
	TReal newMoon3;
	TReal rtn;
	TBool majorSolarTerm1;
	TBool majorSolarTerm2;
	TReal tempReal;
	TInt tempInt;

	// get solar term 1
	date.iDay = KFifteenth;
	date.iMonth = EDecember + KCalConvMonthOffsetByOne;
	date.iYear = aYear - KCalConvYearOffsetByOne;
	solarTerm1 = greg.GregToJulianDay(date);
	solarTerm1 = MajorSolarTermOnOrAfter(solarTerm1);

	// get solar term 2
	date.iDay = KFifteenth;
	date.iMonth = EDecember + KCalConvMonthOffsetByOne;
	date.iYear = aYear;
	solarTerm2 = greg.GregToJulianDay(date);
	solarTerm2 = MajorSolarTermOnOrAfter(solarTerm2);

	// get new moon 1
	newMoon1 = ChineseNewMoonOnOrAfter(solarTerm1 + KCalConvDayOffsetByOne);

	// get new moon 2
	newMoon2 = ChineseNewMoonOnOrAfter(newMoon1 + KCalConvDayOffsetByOne);

	// get new moon 3
	newMoon3 = ChineseNewMoonBefore(solarTerm2 + KCalConvDayOffsetByOne);

	// perform chinese new year calculation
	tempReal = (newMoon3 - newMoon1) / KMeanSynodicMonth;
	Round(tempInt,tempReal);
	majorSolarTerm1 = NoMajorSolarTerm(newMoon1);
	majorSolarTerm2 = NoMajorSolarTerm(newMoon2);
	if((tempInt == KMonthsInYear) && (majorSolarTerm1 || majorSolarTerm2))
		{
		rtn = ChineseNewMoonOnOrAfter(newMoon2 + KCalConvDayOffsetByOne);
		}
	else
		{
		rtn = newMoon2;
		}
	return rtn;
	}

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

//------------------------------------------------------
// 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
//------------------------------------------------------
void TChineseCalendar::ChineseFromFixed(TChineseDate &aDate, const TReal& aJulianDay) const
	{
	TGregorianCalendar greg(aJulianDay);
	TArithmeticalDate date;
	TReal solarTerm1;
	TReal solarTerm2;
	TReal lunarTerm1;
	TReal lunarTerm2;
	TReal lunarTerm3;
	TInt leapYear;
	TInt year;
	TInt elapsedYears;
	TBool majorSolarTerm;
	TReal tempReal;
	TInt tempInt;

	// get the Gregorian date
	greg.GetDate(date);
	year = date.iYear;

	// get solar term 1
	date.iDay = KFifteenth;
	date.iMonth = EDecember + KCalConvMonthOffsetByOne;
	date.iYear = year - KCalConvYearOffsetByOne;
	solarTerm1 = greg.GregToJulianDay(date);
	solarTerm1 = MajorSolarTermOnOrAfter(solarTerm1);

	// get solar term 2
	date.iDay = KFifteenth;
	date.iMonth = EDecember + KCalConvMonthOffsetByOne;
	date.iYear = year;
	solarTerm2 = greg.GregToJulianDay(date);
	solarTerm2 = MajorSolarTermOnOrAfter(solarTerm2);

	// get lunar term 1
	if((solarTerm1 <= aJulianDay) && (aJulianDay < solarTerm2))
		{
		lunarTerm1 = solarTerm1 + KCalConvDayOffsetByOne;
		}
	else
		{
		lunarTerm1 = solarTerm2 + KCalConvDayOffsetByOne;
		}
	lunarTerm1 = ChineseNewMoonOnOrAfter(lunarTerm1);

	// get lunar term 2
	if((solarTerm1 <= aJulianDay) && (aJulianDay < solarTerm2))
		{
		lunarTerm2 = solarTerm2 + KCalConvDayOffsetByOne;
		}
	else
		{
		date.iDay = KFifteenth;
		date.iMonth = EDecember + KCalConvMonthOffsetByOne;
		date.iYear = year + KCalConvYearOffsetByOne;
		lunarTerm2 = greg.GregToJulianDay(date);
		lunarTerm2 = MajorSolarTermOnOrAfter(lunarTerm2) + KCalConvDayOffsetByOne;
		}
	lunarTerm2 = ChineseNewMoonBefore(lunarTerm2);

	// get Lunar term 3
	lunarTerm3 = aJulianDay + KCalConvDayOffsetByOne;
	lunarTerm3 = ChineseNewMoonBefore(lunarTerm3);

	// get leap year
	tempReal = ((lunarTerm2 - lunarTerm1) / KMeanSynodicMonth);
	Round(tempInt,tempReal);
	if(tempInt == KMonthsInYear)
		{
		leapYear = ETrue;
		}
	else
		{
		leapYear = EFalse;
		}

	// get month
	tempReal = ((lunarTerm3 - lunarTerm1) / KMeanSynodicMonth);
	Round(tempInt,tempReal);
	if(leapYear && PriorLeapMonth(lunarTerm1,lunarTerm3))
		{
		tempInt--;
		}
	Amod(tempReal,tempInt,KMonthsInYear);
	Round(aDate.iMonth,tempReal);

	// get leap month
	majorSolarTerm = NoMajorSolarTerm(lunarTerm3);
	if((leapYear) && majorSolarTerm && (!PriorLeapMonth(lunarTerm1,ChineseNewMoonBefore(lunarTerm3))))
		{
		aDate.iLeapMonth = ETrue;
		}
	else
		{
		aDate.iLeapMonth = EFalse;
		}

	// get elapsed years
	elapsedYears = year - KFirstChinYear;

	date.iDay = KCalConvFirstDay;
	date.iMonth = EJuly + KCalConvMonthOffsetByOne;
	date.iYear = year;
	tempReal = greg.GregToJulianDay(date);
	if((aDate.iMonth < (ENovember + KCalConvMonthOffsetByOne)) || (aJulianDay > tempReal))
		{
		elapsedYears++;
		}

	// get cycle
	tempReal = (elapsedYears - KCalConvYearOffsetByOne) / KYearsInChinCycle;
	Floor(aDate.iCycle,tempReal);
	aDate.iCycle++;

	// get year
	Amod(tempReal,elapsedYears,KYearsInChinCycle);
	Round(aDate.iYear,tempReal);

	// get day
	Floor(tempInt,aJulianDay);
	aDate.iDay = tempInt;
	Floor(tempInt,lunarTerm3);
	aDate.iDay -= tempInt;
	aDate.iDay += KCalConvDayOffsetByOne;
	}

//------------------------------------------------------
// 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
	{
	TReal jD;
	TChineseDate chinDate;

	ChineseToFixed(aDate,jD);
	ChineseFromFixed(chinDate,jD);

	if(aDate.operator==(chinDate))
		return ETrue;
	else
		return EFalse;
	}

//------------------------------------------------------
// 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
//------------------------------------------------------
EXPORT_C void TChineseCalendar::ChineseToDateTime(TDateTime &aDT)
	{
	TArithmeticalDate gregDate;
	TGregorianCalendar greg(iJulianDay);

	greg.GregFromJulianDay(gregDate,iJulianDay);

	aDT.SetMicroSecond(0);
	aDT.SetSecond(0);
	aDT.SetMinute(0);
	aDT.SetHour(0);
	aDT.SetMonth((TMonth)(gregDate.iMonth - KCalConvMonthOffsetByOne));
	aDT.SetDay(gregDate.iDay - 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
//------------------------------------------------------
EXPORT_C 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:    ChineseToFixed
// Arguments:   TChineseDate , TReal &
//
// Comments:    This function converts a chinese date to 
//				to a julian day value.
//
// Return:      void
//------------------------------------------------------
void TChineseCalendar::ChineseToFixed(const TChineseDate& aDate, TReal &aJulianDay) const
	{
	TInt year;
	TReal newYear;
	TReal pi;
	TReal priorNewMoon;
	TReal yearReal;
	TChineseDate chinDate;

	// get year
	yearReal = (aDate.iCycle - KCycleOffset) * KYearsInChinCycle;
	Floor(year,yearReal);
	year += aDate.iYear - KCalConvYearOffsetByOne;
	year += KFirstChinYear;

	// get new year
	newYear = ChineseNewYear(year);

	// get papa
	pi = ChineseNewMoonOnOrAfter(newYear + ((aDate.iMonth - KCalConvMonthOffsetByOne) * 29));

	// get delta
	ChineseFromFixed(chinDate,pi);

	// get priorNewMoon
	if((aDate.iMonth == chinDate.iMonth) && (aDate.iLeapMonth == chinDate.iLeapMonth))
		{
		priorNewMoon = pi;
		}
	else
		{
		priorNewMoon = ChineseNewMoonOnOrAfter(pi + KCalConvDayOffsetByOne);
		}

	aJulianDay = priorNewMoon + aDate.iDay - KCalConvDayOffsetByOne;
	}