diff -r 000000000000 -r 2e3d3ce01487 tzservices/tzserver/Client/Source/vtzrules.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tzservices/tzserver/Client/Source/vtzrules.cpp Tue Feb 02 10:12:00 2010 +0200 @@ -0,0 +1,1546 @@ +// Copyright (c) 2005-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: +// + +#include +#include +#include + +#include "tzrules.h" + +const TInt KDaysInTheWeek = 7; +const TInt KTzRulesGranularity = 4; + +// +// TVTzRule +// + +/** +Constructor for a time zone rule. + +@publishedAll +@released + +@param aFrom The first date at which the rule applies. +@param aTo The last date at which the rule applies. +@param aOldOffset The UTC offset in minutes which applies before the DST change. +@param aNewOffset The UTC offset in minutes which applies after the DST change. +@param aMonth The month in which the DST change occurs. +@param aDayRule The rule defining on which day the DST change takes place. +@param aDayOfMonth The number of the day within the month, offset from zero. Used in conjunction with + aDayRule to define the day when DST changes. +@param aDayOfWeek The number of the day within the week, the numerical equivalent of a TDay value. + Used in conjunction with aDayRule to define the day where DST changes. +@param aTimeReference Defines whether aTimeOfChange is a local (wall-clock) time or a UTC time. +@param aTimeOfChange The time of the DST change in minutes from midnight. +*/ +EXPORT_C TTzRule::TTzRule(TTimeWithReference aFrom, TTimeWithReference aTo, TInt16 aOldOffset, TInt16 aNewOffset, TMonth aMonth, TTzRuleDay aDayRule, + TUint8 aDayOfMonth, TUint8 aDayOfWeek, TTzTimeReference aTimeReference, TUint16 aTimeOfChange) : + iFrom(aFrom), + iTo(aTo), + iOldLocalTimeOffset(aOldOffset), + iNewLocalTimeOffset(aNewOffset), + iMonth(aMonth), + iDayRule(aDayRule), + iDayOfMonth(aDayOfMonth), + iDayOfWeek(aDayOfWeek), + iTimeReference(aTimeReference), + iTimeOfChange(aTimeOfChange) + { + } + +/** +Constructor for a time zone rule. + +@publishedAll +@released + +@param aFromYear The first year in which the rule applies. +@param aToYear The last year in which the rule applies. +@param aOldOffset The UTC offset in minutes which applies before the DST change. +@param aNewOffset The UTC offset in minutes which applies after the DST change. +@param aMonth The month in which the DST change occurs. +@param aDayRule The rule defining on which day the DST change takes place. +@param aDayOfMonth The number of the day within the month, offset from zero. Used in conjunction with + aDayRule to define the day where DST changes. +@param aDayOfWeek The number of the day within the week, the numerical equivalent of a TDay value. + Used in conjunction with aDayRule to define the day where DST changes. +@param aTimeReference Defines whether aTimeOfChange is a local (wall-clock) time or a UTC time. +@param aTimeOfChange The time of the DST change in minutes from midnight. +*/ +EXPORT_C TTzRule::TTzRule(TInt16 aFromYear, TInt16 aToYear, TInt16 aOldOffset, TInt16 aNewOffset, TMonth aMonth, TTzRuleDay aDayRule, + TUint8 aDayOfMonth, TUint8 aDayOfWeek, TTzTimeReference aTimeReference, TUint16 aTimeOfChange) : + iFrom(TTimeWithReference(TDateTime(aFromYear,EJanuary,0,0,0,0,0))), + iTo(TTimeWithReference(TDateTime(aToYear,EDecember,30,23,59,59,0))), + iOldLocalTimeOffset(aOldOffset), + iNewLocalTimeOffset(aNewOffset), + iMonth(aMonth), + iDayRule(aDayRule), + iDayOfMonth(aDayOfMonth), + iDayOfWeek(aDayOfWeek), + iTimeReference(aTimeReference), + iTimeOfChange(aTimeOfChange) + { + } + +/** +Default constructor for a time zone rule. + +All member variables are set to zero values. + +@publishedAll +@released +*/ +EXPORT_C TTzRule::TTzRule() : + iOldLocalTimeOffset(0), + iNewLocalTimeOffset(0), + iMonth(EJanuary), + iDayRule(ETzFixedDate), + iDayOfMonth(0), + iDayOfWeek(0), + iTimeReference(ETzWallTimeReference), + iTimeOfChange(0) + { + } + +/** +Copy constructor for a time zone rule. + +@publishedAll +@released +*/ +EXPORT_C TTzRule::TTzRule(const TTzRule& aRule) : + iFrom(aRule.iFrom), + iTo(aRule.iTo), + iOldLocalTimeOffset(aRule.iOldLocalTimeOffset), + iNewLocalTimeOffset(aRule.iNewLocalTimeOffset), + iMonth(aRule.iMonth), + iDayRule(aRule.iDayRule), + iDayOfMonth(aRule.iDayOfMonth), + iDayOfWeek(aRule.iDayOfWeek), + iTimeReference(aRule.iTimeReference), + iTimeOfChange(aRule.iTimeOfChange) + { + } + +/** +Externalises a time zone rule to a write stream. + +@param aStream Stream to which the object should be externalised. +@internalComponent +@released +*/ +void TTzRule::ExternalizeL(RWriteStream& aStream) const + { + aStream << iFrom.iTime.Int64(); + aStream << static_cast(iFrom.iTimeReference); + aStream << iTo.iTime.Int64(); + aStream << static_cast(iTo.iTimeReference); + aStream << iOldLocalTimeOffset; + aStream << iNewLocalTimeOffset; + aStream << static_cast(iMonth); + aStream << static_cast(iDayRule); + aStream << iDayOfMonth; + aStream << iDayOfWeek; + aStream << static_cast(iTimeReference); + aStream << iTimeOfChange; + } + +/** +Internalizes a time zone rule from a read stream. + +@param aStream Stream from which the object should be internalised. +@internalComponent +@released +*/ +void TTzRule::InternalizeL(RReadStream& aStream) + { + TInt64 time; + aStream >> time; + iFrom.iTime = TTime(time); + iFrom.iTimeReference = static_cast(aStream.ReadInt32L()); + aStream >> time; + iTo.iTime = TTime(time); + iTo.iTimeReference = static_cast(aStream.ReadInt32L()); + aStream >> iOldLocalTimeOffset; + aStream >> iNewLocalTimeOffset; + iMonth = static_cast(aStream.ReadInt32L()); + iDayRule = static_cast(aStream.ReadInt32L()); + aStream >> iDayOfMonth; + aStream >> iDayOfWeek; + iTimeReference = static_cast(aStream.ReadInt32L()); + aStream >> iTimeOfChange; + } + + +/** +Check if a time zone rule is applicable during a time range. + +@return ETrue if the rule applies during the supplied time range, EFalse otherwise. +@param aStart Start of time range, inclusively. ie. For a rule to be applicable, this time + can be equal to or earlier than the rule's end time. This time should use the same + time reference used when TTzRule is constructed. +@param aEnd End of time range, exclusively. ie. In order for a rule to be applicable, this + time must be later than the rule's start time. This time should use the same + time reference used when TTzRule is constructed. +@internalComponent +@released +*/ +TBool TTzRule::RuleApplies(const TTime& aStart, const TTime& aEnd) const + { + // If the end of the rule is in the time range or the beginning of the rule is in the time range + return ( (iTo.iTime >= aStart) && (iFrom.iTime < aEnd) ); + } + +/** +Resolves the date rule to the precise date and time for the given year, and +returns it in a TVTzActualisedRule + +For day rules ETzDayAfterDate & ETzDayBeforeDate, the reference date entered +is inclusive in the calculation. ie. If the reference date fits the criteria, +the reference date will be returned. + +eg. Actualise(Friday, ETzDayAfterDate, Fri Jun 22, 2007) returns Jun 22, 2007. + +Deprecated. Use TVTzActualisedRule TTzRule::ActualiseL instead. + +@return The time zone rule with the precise date and time for the given year. +@param aYear Year to actualise time rules +@publishedAll +@deprecated +@see TVTzActualisedRule TTzRule::ActualiseL(TInt aYear) const +*/ +EXPORT_C TVTzActualisedRule TTzRule::Actualise(TInt aYear) const + { + TVTzActualisedRule rule; + TRAP_IGNORE(rule = ActualiseL(aYear)); + return rule; + } + +/** +Resolves the date rule to the precise date and time for the given year, and +returns it in a TVTzActualisedRule + +For day rules ETzDayAfterDate & ETzDayBeforeDate, the reference date entered +is inclusive in the calculation. ie. If the reference date fits the criteria, +the reference date will be returned. + +eg. Actualise(Friday, ETzDayAfterDate, Fri Jun 22, 2007) returns Jun 22, 2007. + +@return The time zone rule with the precise date and time for the given year. +@param aYear Year to actualise time rules +@leave KErrCorrupt if the current day rule is not one of the defined values in TTzRuleDay +@publishedAll +@released +*/ +EXPORT_C TVTzActualisedRule TTzRule::ActualiseL(TInt aYear) const + { + TInt dayOfMonth = iDayOfMonth; + TInt daysDifference = 0; + TDay dayOfWeek(EMonday); + + TDateTime actualDateTime(aYear, + (TMonth)iMonth, + dayOfMonth, + (iTimeOfChange / 60), + (iTimeOfChange % 60), + 0, + 0); + TTime actualTime(actualDateTime); + switch(iDayRule) + { + case ETzFixedDate: + // do nothing + break; + + case ETzDayAfterDate: + // e.g. "Sunday after the 15th" + + // find day in week for given date + dayOfWeek = actualTime.DayNoInWeek(); + + // find difference in days to the rule date + daysDifference = iDayOfWeek - dayOfWeek; + // make positive difference + if (daysDifference < 0) + { + daysDifference += KDaysInTheWeek; // sunday is last day of month + } + + // set actual day + dayOfMonth += daysDifference; + actualDateTime.SetDay(dayOfMonth); + break; + + case ETzDayBeforeDate: + // e.g. "Sunday before the 15th" + + // find day in week for given date + dayOfWeek = actualTime.DayNoInWeek(); + + // find difference in days to the rule date + daysDifference = dayOfWeek - iDayOfWeek; + // make positive difference + if (daysDifference < 0) + { + daysDifference += KDaysInTheWeek; // sunday is last day of month + } + + // set actual day + dayOfMonth -= daysDifference; + actualDateTime.SetDay(dayOfMonth); + break; + + case ETzDayInLastWeekOfMonth: + // e.g. "last Sunday in the month" + + // initialise the day to the last day in the month, + dayOfMonth = actualTime.DaysInMonth() - 1; // days offset from 0 + actualDateTime.SetDay(dayOfMonth); + + // find day in week for given date + actualTime = actualDateTime; + dayOfWeek = actualTime.DayNoInWeek(); + + // find difference in days to the rule date + daysDifference = dayOfWeek - iDayOfWeek; + // make positive difference + if (daysDifference < 0) + { + daysDifference += KDaysInTheWeek; // sunday is last day of month + } + + // set actual day + dayOfMonth -= daysDifference; + actualDateTime.SetDay(dayOfMonth); + break; + + default: + User::Leave(KErrCorrupt); // If data is corrupt method leaves + break; + } + + actualTime = actualDateTime; + + TVTzActualisedRule tActRule(actualTime, + iNewLocalTimeOffset, + static_cast(iTimeReference)); + return tActRule; + } + +// +// CTzRules +// + +/** +Creates a new time zone rules object. +@return Pointer to the time zone rules. +@publishedAll +@released +*/ +EXPORT_C CTzRules* CTzRules::NewL() + { + CTzRules* self = new(ELeave) CTzRules(); + return self; + } + +/** +Creates a new time zone rules object. +@param aStartYear The first year in which these time zone rules apply. +@param aEndYear The last year in which these time zone rules apply. +@return Pointer to the time zone rules. +@publishedAll +@released +*/ +EXPORT_C CTzRules* CTzRules::NewL(TInt aStartYear, TInt aEndYear) + { + CTzRules* self = new(ELeave) CTzRules(aStartYear,aEndYear); + return self; + } + +/** +Creates a new time zone rules object from a stream. +@return Pointer to the time zone rules. +@param aStream Stream with the time zone rules to be used to create a CTzRules object. +@publishedAll +@released +*/ +EXPORT_C CTzRules* CTzRules::NewL(RReadStream& aStream) + { + CTzRules* self = new(ELeave) CTzRules(); + CleanupStack::PushL(self); + self->InternalizeL(aStream); + CleanupStack::Pop(self); + return self; + } + +CTzRules::CTzRules(TInt aStartYear, TInt aEndYear) : + iStartYear((TInt16)aStartYear), + iEndYear((TInt16)aEndYear), + iRules(KTzRulesGranularity) + { + } + +CTzRules::CTzRules() : + iRules(KTzRulesGranularity) + { + } + +/** +Destructor. +@publishedAll +@released +*/ +EXPORT_C CTzRules::~CTzRules() + { + delete iActualisedRulesCache; + iRules.Reset(); + } + +/** +Get the object size when it is internalize and externalised. + +@return the size of thisobject +@internalComponent +@released +*/ +EXPORT_C TInt CTzRules::SizeOfObject() const + { + return iRules.Count() * sizeof (TTzRule) + 4*sizeof (TInt16); + //As to 4 size of TInt16, they are for iStartYear, iEndYear, iInitialStdTimeOffset and the count of array of TTzRule + } +/** +Internalizes time zone rules from a read stream. + +@param aStream Stream from which the object should be internalised. +@publishedAll +@released +*/ +EXPORT_C void CTzRules::InternalizeL(RReadStream& aStream) + { + iRules.Reset(); + + // read start and end year covered by rules + iStartYear = aStream.ReadInt16L(); + iEndYear = aStream.ReadInt16L(); + + // read initial std time offset + iInitialStdTimeOffset = aStream.ReadInt16L(); + + // read number of rules + const TInt16 KCount = aStream.ReadInt16L(); + + // read rules + TTzRule rule; + for (TInt i = 0; i < KCount; ++i) + { + rule.InternalizeL(aStream); + User::LeaveIfError(iRules.Append(rule)); + } + + // The cache has been invalidated so clear it + delete iActualisedRulesCache; + iActualisedRulesCache = NULL; + } + +/** +Externalises time zone rules to a write stream. + +@param aStream Stream to which the object should be externalised. +@leave KErrArgument if the output stream size is invalid +@publishedAll +@released +*/ +EXPORT_C void CTzRules::ExternalizeL(RWriteStream& aStream) const + { + // Ensure the size of the stream is valid. + TInt size = aStream.Sink()->SizeL(); + const TInt KMaxSize = KMaxTInt / 2; + if (size < 0 || size >= KMaxSize) + { + User::Leave(KErrArgument); + } + + // write range of years covered by rules + aStream.WriteInt16L(iStartYear); + aStream.WriteInt16L(iEndYear); + + // write initial std time offset + aStream.WriteInt16L(iInitialStdTimeOffset); + + // write number of rules + const TInt16 KCount = (TInt16)iRules.Count(); + aStream.WriteInt16L(KCount); + + // write rules + for (TInt i = 0; i < KCount; i++) + { + iRules[i].ExternalizeL(aStream); + } + } + +/** +Gets the first year in which the time zone rules apply. +@return The year. +@publishedAll +@released +*/ +EXPORT_C TInt CTzRules::StartYear() const + { + return iStartYear; + } + +/** +Gets the last year in which the time zone rules apply. +@return The year. +@publishedAll +@released +*/ +EXPORT_C TInt CTzRules::EndYear() const + { + return iEndYear; + } + +/** +Sets the first year in which the time zone rules apply. +@param aYear The year. +@publishedAll +@released +*/ +EXPORT_C void CTzRules::SetStartYear(TInt aYear) + { + iStartYear = (TInt16)aYear; + } + +/** +Sets the last year in which the time zone rules apply. +@param aYear The year. +@publishedAll +@released +*/ +EXPORT_C void CTzRules::SetEndYear(TInt aYear) + { + iEndYear = (TInt16)aYear; + } + +/** +Gets the number of time zone rules (TTzRules) in this set. +@return The number of rules. +@publishedAll +@released +*/ +EXPORT_C TInt CTzRules::Count() const + { + return iRules.Count(); + } + +/** +Gets the initial UTC offset for this set of time zone rules. +@return The offset in minutes. +@publishedAll +@released +*/ +EXPORT_C TInt CTzRules::InitialStdTimeOffset() const + { + return iInitialStdTimeOffset; + } + +/** +Sets the initial UTC offset for this set of time zone rules. +@param aOffset The offset in minutes. +@publishedAll +@released +*/ +EXPORT_C void CTzRules::SetInitialStdTimeOffset(TInt aOffset) + { + // The cache has been invalidated so clear it + delete iActualisedRulesCache; + iActualisedRulesCache = NULL; + + iInitialStdTimeOffset = aOffset; + } + +/** +Adds a time zone rule to this set. +@param aTRule The rule to be added. +@publishedAll +@released +*/ +EXPORT_C void CTzRules::AddRuleL(TTzRule aTRule) + { + // the cache has been invalidated so clear it + delete iActualisedRulesCache; + iActualisedRulesCache = NULL; + + TInt result = iRules.Append(aTRule); + User::LeaveIfError(result); + } + +/** +Removes a time zone rule from this set. +@param aIndex The index of the rule to be removed. +@publishedAll +@released +*/ +EXPORT_C void CTzRules::RemoveRule(TInt aIndex) + { + // the cache has been invalidated so clear it + delete iActualisedRulesCache; + iActualisedRulesCache = NULL; + + iRules.Remove(aIndex); + } + +/** +Gets a time zone rule from this set. +@param aIndex The index of the rule to be fetched. +@return Reference to the time zone rule. +@publishedAll +@released +*/ +EXPORT_C TTzRule& CTzRules::operator[](TInt aIndex) + { + // prevent array bounds error + __ASSERT_ALWAYS( (aIndex < iRules.Count() && aIndex >= 0), RTz::Panic(RTz::EPanicRulesIndexOutofRange)); + + return iRules[aIndex]; + } + +/** +Queries the time zone rule set to see if they apply to a specified time. +@param aTime The time to be checked, using the same time reference used when constructing + CTzRules. +@return ETrue if the time zone rules apply to the specified time. EFalse if not. +@publishedAll +@released +*/ + EXPORT_C TBool CTzRules::RulesApply(const TTime& aTime) const + { + const TTime KStart(TDateTime(iStartYear, EJanuary, 0, 0, 0, 0, 0)); + const TTime KEnd(TDateTime(iEndYear + 1, EJanuary, 0, 0, 0, 0, 0)); + return ((aTime >= KStart) && (aTime < KEnd)); + } + + +/** + +Compares two times with time references (TTzTimeReference). +TTzTimeReference is either UTC, or STD, or ETzWallTimeReference. + +Comparison is done at some point in time when aStdOffset applies to item(s) with ETzStdTimeReference, +and aWallOffset applies to item(s) with ETzWallTimeReference. + +If time references for both aTimeA and aTimeB are the same, then straight comparison is done to +aTimeA and aTimeB. Otherwise, STD or wall-clock time is converted to UTC using either aStdOffset, +or aWallOffset correspondingly. + +Thus, aStdOffset parameter is never used if none of aTimeA and aTimeB is ETzStdTimeReference; +and, similarly, aWallOffset is never used if none of aTimeA and aTimeB is ETzWallTimeReference. + +@param aTimeA - first time with reference; +@param aTimeB - second time with reference, to compare to aTimeA; +@param aStdOffset - an offset (in minutes) to be used for a time with ETzStdTimeReference; +@param aWallOffset - an offset (in minutes) to be used for a time with ETzWallTimeReference; +@param aMinutesDifference -on return, the time difference between aTimeA and aTimeB in minutes; + +@return TInt -1 if aTimeA is < aTimeB; + 0 if aTimeA is equal to aTimeB; + 1 if aTimeA is > aTimeB. +Example: + compare 20050403T020200 local with 20050403T020100Z UTC in PST time zone (-0800): + CompareTimesWithRef(20050403T020200, wall, 20050403T020100, utc, 0(not used), -8(PST)) gives -1; + while CompareTimesWithRef(20050403T020200, wall, 20050403T020100, utc, 0(not used), 0(GMT)) gives 1. + +@internalComponent +@released + */ +TInt CTzRules::CompareTimesWithRef(TTime aTimeA, TTzTimeReference aTimeARef, + TTime aTimeB, TTzTimeReference aTimeBRef, + TInt aStdOffset, TInt aWallOffset, TTimeIntervalMinutes* aMinutesDifference) const + { + TTime timeA(aTimeA), timeB(aTimeB); + if (aTimeARef != aTimeBRef) + { + if (aTimeARef == ETzStdTimeReference) + { + timeA -= TTimeIntervalMinutes(aStdOffset); + } + if (aTimeARef == ETzWallTimeReference) + { + timeA -= TTimeIntervalMinutes(aWallOffset); + } + if (aTimeBRef == ETzStdTimeReference) + { + timeB -= TTimeIntervalMinutes(aStdOffset); + } + if (aTimeBRef == ETzWallTimeReference) + { + timeB -= TTimeIntervalMinutes(aWallOffset); + } + } + + if (aMinutesDifference) + { + timeA.MinutesFrom(timeB, *aMinutesDifference); + } + + return (timeAtimeB) ? 1 : 0); + } + +/** +Based on the rules in the object calculates actualised rules for particular year and adds them +to the specified array of actualised rules (CVTzActualisedRules). + +@param aActRules - an array of actualised rules where new actualised rules are added; +@param aYear - a year for which actualised rules are added. + +@internalComponent +@released +*/ +void CTzRules::AddActualisedRulesL(CVTzActualisedRules& aActRules, TInt aYear) const + { + const TInt count=iRules.Count(); + const TTzRule* trule = NULL; + + const TTime KYearBegin(TDateTime(aYear, EJanuary, 0, 0, 0, 0, 0)); + const TTime KYearEnd(TDateTime(aYear + 1, EJanuary, 0, 0, 0, 0, 0)); + + for (TInt i = 0; i < count; i++) + { + trule = &(iRules[i]); + if ( trule->RuleApplies(KYearBegin, KYearEnd)) + { + TVTzActualisedRule tactRule = trule->Actualise(aYear); + + // ETzStdTimeReference is not useful for VTIMEZONE or for conversions + if(trule->iTimeReference == ETzStdTimeReference) + { + //# Time: Time for the DST switch. Important: sometimes the time is followed by a letter. The meaning of this letter is: + //no letter or w: wall clock time, actual local time. + //s: local standard time (winter time) + //u or g or z: UTC time. + //So, if a 'DST on' time is given as '0:00s', this means a switch on 0:00 local time. + //If a 'DST off' time is given as '0:00s', this means a switch on 1:00 local time (assuming a DST offset of 1 hour). + + //DST off time is always the lowest among the two. + //We do not want to use the oldlocaltimeoffset for DST Off conditions. + TInt standardOffset = trule->iOldLocalTimeOffset; + if(trule->iNewLocalTimeOffset < trule->iOldLocalTimeOffset) + { + standardOffset = trule->iNewLocalTimeOffset; + } + tactRule.iTimeOfChange -= TTimeIntervalMinutes(standardOffset); + tactRule.iTimeReference = ETzUtcTimeReference; + } + + if ( 0 >= CompareTimesWithRef(tactRule.iTimeOfChange, tactRule.iTimeReference, + trule->iTo.iTime, trule->iTo.iTimeReference, + 0, trule->iOldLocalTimeOffset, NULL) ) + { + // tactRule.iTimeOfChange w/ref is less or equal to trule->iTo + + /*Fix for INC117764: + Before adding make sure that rule does not occur before the rules which have already been added. + For Ex-"Europe\Tirane" + 1) Rule Starts at 1/4/1984, Rule Ends at 30/6/1984, Old offset = 60, New Offset = 120 (Summer rule) + 2) Rule Starts at 1/7/1984, Rule Ends at 1/7/1984, Old offset = 60, New Offset = 120 (Summer rule contd.) + 3) Rule Starts at 1/9/1984, Rule Ends at 31/12/1995, Old offset = 60, New Offset = 60(winter rule) + 4) Rule Starts at 1/3/1984, Rule Ends at 31/12/9998, Old offset = 60, New Offset = 120 (Summer Rule) + The 4th rule should follow from 7/7/1984 onwards, otherwise if 4th rule is actualised for year 1984 + it would look like --Rule Starts at 1/3/1984, NewOffset = 120, this will cause issues when converting time in March since summer + time won't start until April 1984. + */ + TBool ruleAdded = ETrue; + for(TInt loop = aActRules.Count()-1; loop >=0; --loop) + { + TVTzActualisedRule prevActRule = aActRules[loop]; + TDateTime prevRdt = prevActRule.iTimeOfChange.DateTime(); + TDateTime actRdt = tactRule.iTimeOfChange.DateTime(); + if(actRdt.Year() > prevRdt.Year()) + { + //no covering rule for this year in previously added rules + //no need of looking into other year rules + break; + } + if(actRdt.Year() == prevRdt.Year() && + actRdt.Month() < prevRdt.Month() && + tactRule.iNewOffset == prevActRule.iNewOffset) + { + //covering rule already exists so dont add this one + ruleAdded = EFalse; + break; + } + } + if(ruleAdded) + { + aActRules.AddRuleL(tactRule); + } + } + } + } + } + +/** +Get actualised rules for time zone rules. +@param aActRules Actualised rules for time zone rules. +@publishedAll +@released +*/ +EXPORT_C void CTzRules::GetActualisedRulesL(CVTzActualisedRules& aActRules) const + { + // Always add the initial offset because there may not have been + // any rules to actualise before the actualised rules range specified + TVTzActualisedRule tDefRule(TDateTime(iStartYear, EJanuary, 0, 0, 0, 0, 0), iInitialStdTimeOffset, ETzWallTimeReference); + aActRules.AddRuleL(tDefRule); + + if (iRules.Count() == 0) + { + // do nothing + } + if (iRules.Count() == 1) + { + // There is only one rule, perhaps this is an unknown timezone + // or a timezone that has never had DST, so just add that one rule + + TVTzActualisedRule tDefRule(TDateTime(aActRules.StartYear(), EJanuary, 0, 0, 0, 0, 0), iRules[0].iNewLocalTimeOffset, ETzWallTimeReference); + aActRules.AddRuleL(tDefRule); + } + else + { + // Add actualised rules for the year range spcified in aActRules + // also make sure that a year before this range is actualised + // so that the initial offset for the actualised rules range is correct + + TInt32 year(0); + for (year = aActRules.EndYear() ; year >= aActRules.StartYear() ; --year) + { + AddActualisedRulesL(aActRules, year); + } + + // try to add an actualised rule for the previous year + // so that the start of the range will be correct + const TInt KActRulesCount(aActRules.Count()); + AddActualisedRulesL(aActRules, year); + + if (aActRules.Count() == KActRulesCount) + { + // No rules were added for the year before the range being actualised + // so find the first rule that applies before this year + + TTime timeSoFar(TDateTime(iStartYear, EJanuary, 0, 0, 0, 0, 0)); + TTime endTime(TDateTime(year, EJanuary, 0, 0, 0, 0, 0)); + + TBool found(EFalse); + + for (TInt i(iRules.Count() - 1) ; i >= 0 ; --i) + { + if (iRules[i].iTo.iTime > timeSoFar && iRules[i].iTo.iTime < endTime) + { + timeSoFar = iRules[i].iTo.iTime; + found = ETrue; + } + } + + if (found) + { + AddActualisedRulesL(aActRules, timeSoFar.DateTime().Year()); + } + } + } + } + +/** +Converts the received local time to UTC time. +@param aTime The time to convert. On return, this contains the converted time. +@publishedAll +@released +*/ +EXPORT_C void CTzRules::ConvertToUtcL(TTime& aTime) const + { + // This check would not be necessary if we can ensure this function + // is never called with a null TTime + if (aTime == Time::NullTTime()) + { + return; + } + + TInt offset = GetOffsetL(aTime, ETzWallTimeReference); + aTime -= (TTimeIntervalMinutes)(offset); + } + +/** +Converts the received UTC time to local time. +@param aTime The time to convert. On return, this contains the converted time. +@publishedAll +@released +*/ +EXPORT_C void CTzRules::ConvertToLocalL(TTime& aTime) const + { + // This check would not be necessary if we can ensure this function + // is never called with a null TTime + if (aTime == Time::NullTTime()) + { + return; + } + + TInt offset = GetOffsetL(aTime, ETzUtcTimeReference); + aTime += (TTimeIntervalMinutes)(offset); + } + +/** +Calculate the local time offset at the supplied time. + +@return local time offset +@internalComponent +@released +*/ +EXPORT_C TInt CTzRules::GetOffsetL(const TTime& aTime, TTzTimeReference aTimeRef) const + { + if (iActualisedRulesCache) + { + TTime startTime(TDateTime(iActualisedRulesCache->StartYear(), EJanuary, 0, 0, 0, 0, 0)); + TTime endTime(TDateTime(iActualisedRulesCache->EndYear() + 1, EJanuary, 0, 0, 0, 0, 0)); + + if (aTime >= startTime && aTime <= endTime) + { + return iActualisedRulesCache->GetOffsetFromRuleL(aTime, aTimeRef); + } + } + + TInt year(aTime.DateTime().Year()); + + // actualise rules + delete iActualisedRulesCache; + iActualisedRulesCache = NULL; + iActualisedRulesCache = CVTzActualisedRules::NewL(year, year); + TRAPD(leaveCode,GetActualisedRulesL(*iActualisedRulesCache)); + if(leaveCode != KErrNone) + { + delete iActualisedRulesCache; + iActualisedRulesCache = NULL; + User::LeaveIfError(leaveCode); + } + + return iActualisedRulesCache->GetOffsetFromRuleL(aTime, aTimeRef);; + } + +/** +Creates a copy of these timezone rules. + +@capability None +@return A pointer to the CTzRules copy. +@publishedAll +@released +*/ +EXPORT_C CTzRules* CTzRules::CloneL() const + { + CTzRules* newRules = CTzRules::NewL(); + CleanupStack::PushL(newRules); + + newRules->SetStartYear(iStartYear); + newRules->SetEndYear(iEndYear); + newRules->SetInitialStdTimeOffset(iInitialStdTimeOffset); + + TInt numRules = Count(); + for (TInt i = 0; i < numRules; i++) + { + newRules->AddRuleL(iRules[i]); + } + + CleanupStack::Pop(newRules); + return newRules; + } + +/** Copy a time zone rule to this time zone rule. + +@capability None +@param aTzRule A rule to copy from. +@internalAll +@released +*/ +EXPORT_C void CTzRules::CopyL(const CTzRules& aTzRule) + { + SetStartYear(aTzRule.iStartYear); + SetEndYear(aTzRule.iEndYear); + SetInitialStdTimeOffset(aTzRule.iInitialStdTimeOffset); + iRules.Reset(); + const TInt numRules = aTzRule.Count(); + for (TInt i = 0; i < numRules; i++) + { + AddRuleL(aTzRule.iRules[i]); + } + } +/** +Compares two sets of timezone rules. + +@capability None +@param aRules The timezone rules to compare with. +@return ETrue if the rules are identical. EFalse if not. +@publishedAll +@released +*/ +EXPORT_C TBool CTzRules::IsEqualTo(const CTzRules& aRules) const + { + if (iStartYear != aRules.StartYear() || + iEndYear != aRules.EndYear() || + iInitialStdTimeOffset != aRules.InitialStdTimeOffset() || + Count() != aRules.Count()) + { + return EFalse; + } + + const TInt KRuleCount(Count()); + for (TInt i = 0; i < KRuleCount; i++) + { + TTzRule rule1 = const_cast(*this)[i]; + TTzRule rule2 = const_cast(aRules)[i]; + + if (rule1.iFrom != rule2.iFrom || + rule1.iTo != rule2.iTo || + rule1.iOldLocalTimeOffset != rule2.iOldLocalTimeOffset || + rule1.iNewLocalTimeOffset != rule2.iNewLocalTimeOffset || + rule1.iMonth != rule2.iMonth || + rule1.iDayRule != rule2.iDayRule || + rule1.iDayOfMonth != rule2.iDayOfMonth || + rule1.iDayOfWeek != rule2.iDayOfWeek || + rule1.iTimeReference != rule2.iTimeReference || + rule1.iTimeOfChange != rule2.iTimeOfChange ) + { + return EFalse; + } + } + return ETrue; + } + +/** +Converts between local (wall-clock) and UTC times. + +@return KErrNone if successful, otherwise KErrNotSupported or KErrNotFound +@param aRules The actualised rules to use. +@param aTime The time to convert, specified by aTimerRef if the time is in UTC or local time. On return, + it will contain the converted time from UTC to local time or vice versa, depending on aTimerRef's value. +@param aTimerRef What aTime is expressed in. +@publishedAll +@released +*/ +EXPORT_C TInt CTzRules::ConvertTime(CVTzActualisedRules& aRules, TTime& aTime, TTzTimeReference aTimeRef) const + { + TInt results = KErrNone; + TInt offset = 0; + +#ifdef _DEBUG + TDateTime aDateTime = aTime.DateTime(); +#endif + + TRAP( results, offset = aRules.GetOffsetFromRuleL(aTime, aTimeRef)); + + if (results == KErrNone) + { + switch (aTimeRef) + { + case ETzUtcTimeReference: + aTime += TTimeIntervalMinutes(offset); + break; + case ETzWallTimeReference: + aTime -= TTimeIntervalMinutes(offset); + break; + default: + results = KErrNotSupported; + break; + } + } + + return (results); + } + +// +// TVTzActualisedRule +// + +/** +Constructor for an actualised rule (local time change). + +@param aTimeOfChange Time of local time change +@param aNewOffset New UTC offset in minutes +@param aTimeReference Time reference +@publishedAll +@released +*/ +EXPORT_C TVTzActualisedRule::TVTzActualisedRule(TTime aTimeOfChange, TInt aNewOffset, TTzTimeReference aTimeReference) : + iTimeOfChange(aTimeOfChange), + iNewOffset(aNewOffset), + iTimeReference(aTimeReference) + { + } + + +/** +Default constructor for an actualised rule (local time change). +@publishedAll +@released +*/ +EXPORT_C TVTzActualisedRule::TVTzActualisedRule() : + iTimeOfChange(0), + iNewOffset(0), + iTimeReference(ETzUtcTimeReference) + { + } + +/** +Assignment operator for an actualised rule. +@param aRule Actualised rule +@publishedAll +@released +*/ +EXPORT_C void TVTzActualisedRule::operator=(TVTzActualisedRule aRule) + { + iTimeOfChange = aRule.iTimeOfChange; + iNewOffset = aRule.iNewOffset; + iTimeReference = aRule.iTimeReference; + } + +/** +Order actualised rules by time of local time change. +@internalComponent +@released +*/ +TInt TVTzActualisedRule::Order(const TVTzActualisedRule& aLeft, const TVTzActualisedRule& aRight) + { + if (aLeft.iTimeOfChange < aRight.iTimeOfChange) + { + return -1; + } + if (aLeft.iTimeOfChange == aRight.iTimeOfChange) + { + return 0; + } + return 1; + } + + +// +// CVTzActualisedRules +// + +/** +Factory method for CVTzActualisedRules objects. +Creates a new instance of CVTzActualizedRules - an array of actualised rules (TVTzActualisedRule). + +The array of rules can be populated later by invoking CTzRules::GetActualisedRulesL() method. + +Rules in the array are sorted ascending by rule's start time (TVTzActualisedRule.iTimeOfChange); +The very first rule in the array, if present, defines "default standard rule" for the time zone +indicating the STD time offset from UTC, in effect from the start of the first year (aStartYear). + +@param aStartYear, aEndYear - specify range of years for to which actualised rules apply. +@return A pointer to the newly created rules. +@panic RTz 10 if aStartYear is larger than aEndYear. +@publishedAll +@released +*/ +EXPORT_C CVTzActualisedRules* CVTzActualisedRules::NewL(TInt aStartYear, TInt aEndYear) + { + __ASSERT_ALWAYS(aStartYear <= aEndYear, RTz::Panic(RTz::EPanicInvalidArgument)); + + CVTzActualisedRules* self = new (ELeave) CVTzActualisedRules(aStartYear, aEndYear); + return self; + } + +/** +Constructor. +@internalComponent +@released +*/ +CVTzActualisedRules::CVTzActualisedRules(TInt aStartYear, TInt aEndYear) : + iStartYear(aStartYear), + iEndYear(aEndYear), + iRules(KVTzRulesGranularity) + { + } + +/** +Default constructor. +@internalComponent +@released +*/ +CVTzActualisedRules::CVTzActualisedRules() : + iRules(KVTzRulesGranularity) + { + } + +/** +Destructor. +@publishedAll +@released +*/ +EXPORT_C CVTzActualisedRules::~CVTzActualisedRules() + { + iRules.Reset(); + } + +/** +Returns specified TVTzActualisedRule. + +@param aIndex Index of a rule +@return A reference to TVTzActualised rule at index aIndex. +@panic RTz 4 if aIndex is out of bounds. +@publishedAll +@released +*/ +EXPORT_C TVTzActualisedRule& CVTzActualisedRules::operator[](TInt aIndex) const + { + // prevent array bounds error + __ASSERT_ALWAYS( (aIndex < iRules.Count() && aIndex >= 0), RTz::Panic(RTz::EPanicRulesIndexOutofRange)); + + return const_cast(iRules[aIndex]); + } + +/** +Add an actualised rule to the collection. + +@param aRule An actualised rule to be added into the collection. +@publishedAll +@released +*/ +EXPORT_C void CVTzActualisedRules::AddRuleL(const TVTzActualisedRule& aRule) + { + TLinearOrder order(TVTzActualisedRule::Order); + + User::LeaveIfError(iRules.InsertInOrderAllowRepeats(aRule, order)); + } + +/** +Returns number of rules currently held in array of rules. + +@return Number of rules. +@publishedAll +@released +*/ +EXPORT_C TInt CVTzActualisedRules::Count() const + { + return iRules.Count(); + } + +/** +Returns min year of the year range the CVTzActualisedRules object describes the rules for. +It is guaranteed that the array of rules does not contain any rules for years before that min year. + +@return Start year (e.g. 1998). +@publishedAll +@released +*/ +EXPORT_C TInt CVTzActualisedRules::StartYear() const + { + return iStartYear; + } + +/** +Returns max year of the year range the CVTzActualisedRules object describes the rules for. + +@return End year (e.g. 2005). +@publishedAll +@released +*/ +EXPORT_C TInt CVTzActualisedRules::EndYear() const + { + return iEndYear; + } + +/** +Tells if Daylight Savings Applies for the current time zone at the current time +@return EFalse if Daylight Savings does not apply (winter time) and ETrue if Daylight Savings does apply +@param aTime The time of interest given in UTC. +@panic RTz 5 if aTime is not covered by the current set of time zone rules. +@panic RTz 7 if one of the time zone rules uses standard time reference. +@publishedAll +@released +*/ +EXPORT_C TBool CVTzActualisedRules::IsDaylightSavingOn(TTime& aTime) const + { + const TInt count = iRules.Count(); + + // ensure that there are rules + __ASSERT_ALWAYS((count > 0), RTz::Panic(RTz::EPanicTimeNotCoveredByRules)); + + // initialisation separated from declaration to avoid arm4 'unused variable warning' + TDateTime dateTime; + dateTime = aTime.DateTime(); + + // ensure that there are rules defined for the time in question + __ASSERT_ALWAYS(dateTime.Year() >= StartYear() && dateTime.Year() <= EndYear(), + RTz::Panic(RTz::EPanicTimeNotCoveredByRules)); + + // the illustration below is for a location in the northern hemisphere + // the opposite applies to southern hemisphere locations + // + // --------|--------|--------- + // season: winter summer winter + // rule: [1] [2] + // + // the rules are sorted by iTimeOfChange. + // we start searching for the applicable rule with time 0. + // + // general dst example from southern hemisphere: + // America/Sao_Paulo + // UTC offset (dst off): -180min (-3h) + // UTC offset (dst on): -120min (-2h) + // starting dst (Nov) 4/11/2007 at 00:00:00 + // utc time: local time: expected dst status: + // 02:00 23:00 off (-180) + // 02:58 23:58 off (-180) + // 02:59 23:59 off (-180) + // n/a (00:00) n/a + // 03:00 01:00 on (-120) + // 03:01 01:01 on (-120) + // 04:00 02:00 on (-120) + // 05:00 03:00 on (-120) + // + // ending dst (Feb) 24/02/2008: 00:00:00 + // utc time: local time: expected dst status: + // 01:00 23:00 on (-120) + // 01:58 23:58 on (-120) + // 01:59 23:59 on (-120) + // n/a (00:00) n/a + // 02:00 23:00 off (-180) + // 02:58 23:58 off (-180) + // 02:59 23:59 off (-180) + // 03:00 00:00 off (-180) + // 04:00 01:00 off (-180) + // 05:00 02:00 off (-180) + // + TBool dstOn(EFalse); + TTime lastTimeOfChange(0); + TInt lowestOffset = 0; + TBool firstMatchFound = EFalse; + + // Retrieve lowest applicable time offset. + // This is used to determine if DST is on. + for (TInt i = 0; i < count; ++i) + { + const TVTzActualisedRule& rule = iRules[i]; + + // Only check for the same year as requested (aTime) + if (rule.iTimeOfChange.DateTime().Year() == dateTime.Year()) + { + if (!firstMatchFound) + { + lowestOffset = rule.iNewOffset; + firstMatchFound = ETrue; + } + + if (rule.iNewOffset < lowestOffset) + { + lowestOffset = rule.iNewOffset; + } + } + } + + if (firstMatchFound) // lowest offset has been found in year of interest + { + // there are only a few actualised rules + // so simple sequential search will suffice + // Times may be expressed in utc, or local + // so we need to compare like for like + for (TInt i = 0; i < count; ++i) + { + const TVTzActualisedRule& rule = iRules[i]; + TInt oldOffset = (i>0) ? iRules[i-1].iNewOffset : rule.iNewOffset; + + __ASSERT_ALWAYS(rule.iTimeReference!=ETzStdTimeReference, RTz::Panic(RTz::EPanicUnsupportedTimeReference)); + + TTime timeOfChange(rule.iTimeOfChange); + + // convert to utc + if (rule.iTimeReference == ETzWallTimeReference) + { + timeOfChange -= (TTimeIntervalMinutes)oldOffset; + } + + #if defined(_DEBUG) + // initialisation separated from declaration to avoid arm4 'unused variable warning' + TDateTime dateTimeofChange; + dateTimeofChange = timeOfChange.DateTime(); + #endif + + if (aTime >= timeOfChange) // continue until finding the rule which applies to the current time + { + if (timeOfChange > lastTimeOfChange) // a double check that rules are in order + { + // Check to see if the lowest offset is greater than the current + // offset. If it is, then this implies DST is on. + if (rule.iNewOffset > lowestOffset) + { + dstOn = ETrue; // daylight savings is on + } + else + { + dstOn = EFalse; // daylight savings is off + } + + lastTimeOfChange = timeOfChange; + } + } + else + { + // found the matching rule on previous run through loop + break; + } + } + } + return(dstOn); + } + +/** +Receives a time. Finds out which of these rules applies at the received time +and returns the offset in effect at the specified time. + +Leaves with KErrNotFound, if it doesn't find the rule +(i.e. aTime is earlier than the very first time in CVTzActualisedRules). + +@param aUserTime - time of interest +@param aUserTimeRef - time reference for the aUserTime (UTC or wall-time) + note: ETzStdTimeReference for aTimeRef is not supported, will + panic with RTz::EPanicUnsupportedTimeReference. + +@return TInt - offset from UTC (in minutes). + */ +EXPORT_C TInt CVTzActualisedRules::GetOffsetFromRuleL(const TTime& aUserTime, TTzTimeReference aUserTimeRef) const + { + // The first rule with iTimeOfChange <= aUserTime that is found going backwards in the array is the one that + // applies at the requested aUserTime. + __ASSERT_ALWAYS(aUserTimeRef!=ETzStdTimeReference, RTz::Panic(RTz::EPanicUnsupportedTimeReference)); + + // we traverse the array of rules, starting from the last one, until we find the + // rule that has a start time just before aTime + TBool ruleFound = EFalse; + TInt resultOffset = 0; + + TLinearOrder linearOrder(TVTzActualisedRule::Order); + TVTzActualisedRule findActualisedRule(aUserTime, 0, aUserTimeRef); + TInt position; + TInt error = iRules.FindInOrder(findActualisedRule, position, linearOrder); + + for (TInt i(Min(position, iRules.Count()-1)); i >= 0; i--) + { + const TVTzActualisedRule& actRule = iRules[i]; + TInt oldOffset = (i>0) ? iRules[i-1].iNewOffset : actRule.iNewOffset; + + TTime rolloverTime = actRule.iTimeOfChange; + TTzTimeReference rolloverTimeRef = actRule.iTimeReference; + + #if defined(_DEBUG) + TDateTime dtRolloverTime = rolloverTime.DateTime(); + TDateTime dtUserTime = aUserTime.DateTime(); + #endif + + TTimeIntervalMinutes diffMinutes; + // Find out if rolloverTime is earlier than aUserTime + if (CompareTimesWithRef(rolloverTime, rolloverTimeRef, aUserTime, aUserTimeRef, oldOffset, diffMinutes) <= 0) + {// rolloverTime is now earlier than aUserTime, so we are done iterating backwards + // and aUserTime is now between this iteration and the last iterated rule. + // This means we need to use the current iNewOffset for the correct offset to give aUserTime on return. + resultOffset = actRule.iNewOffset; + + // If diffMinutes is less than difference between iNewOffset and oldOffset, + // then we are potentially in the "missing" hour, which should return the + // oldOffset instead of iNewOffset. + if (aUserTimeRef == ETzWallTimeReference && + oldOffset < actRule.iNewOffset && + diffMinutes.Int() < (actRule.iNewOffset-oldOffset)) + { + // the missing hour - use oldOffset instead. + // see general dst example in CVTzActualisedRules::IsDaylightSavingOn + // why this is necessary. + resultOffset = oldOffset; + } + + ruleFound = ETrue; + break; + } + } + + if (!ruleFound) + { + User::Leave(KErrNotFound); + } + + return resultOffset; + } + +/** + +Compares two times with time references (TTzTimeReference). +TTzTimeReference is either ETzUtcTimeReference or ETzWallTimeReference. + +If time references for both aRolloverTime and aUserTime are the same, +then straight comparison is done. Otherwise aRolloverTime is converted to match +the time reference of aUserTime. + +@param aRolloverTime - the time of change from/to Daylight Savings Time (DST) +@param aTimeRefRollover - the time reference used in aRolloverTime +@param aUserTime - the time provided by the user to compare with +@param aTimeRefUser - the time reference used for the user time +@param aOldWallOffset - the offset that is active before aRolloverTime happens + +@return TInt -1 if aRolloverTime is earlier than aUserTime; + 0 if aRolloverTime is equal to aUserTime; + 1 if aRolloverTime is later than aUserTime. + */ +TInt CVTzActualisedRules::CompareTimesWithRef( + TTime aRolloverTime, TTzTimeReference aTimeRefRollover, + TTime aUserTime, TTzTimeReference aTimeRefUser, + TInt aOldWallOffset, TTimeIntervalMinutes& aDiffMinutes) const + { + TInt result = 0; + + // Always convert the Rollover time if it has not + // the same reference type as user time + if ( aTimeRefRollover != aTimeRefUser) + { + // As they are different; if RolloverTime is of + // wall time type, we need to convert it to Utc + // and vice versa, to match aTimeRefUser + if ( aTimeRefRollover == ETzWallTimeReference) + { + // Convert to utc time reference + aRolloverTime -= TTimeIntervalMinutes (aOldWallOffset); + } + else // aTimeRefRollover is ETzUtcTimeReference + { + // Convert to wall time reference + aRolloverTime += TTimeIntervalMinutes (aOldWallOffset); + } + } + + #if defined(_DEBUG) + TDateTime dtRollover = aRolloverTime.DateTime (); + TDateTime dtUser = aUserTime.DateTime (); + #endif + + TInt err = aUserTime.MinutesFrom(aRolloverTime, aDiffMinutes); + + if (aRolloverTime < aUserTime) + { + result = -1; + } + else if (aRolloverTime > aUserTime) + { + result = 1; + } + + return result; + } + + +