diff -r 000000000000 -r 2e3d3ce01487 tzservices/tzserver/Server/Source/timezoneconfig.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tzservices/tzserver/Server/Source/timezoneconfig.cpp Tue Feb 02 10:12:00 2010 +0200 @@ -0,0 +1,1674 @@ +// Copyright (c) 1997-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 // CEnvironmentChangeNotifier. +#include // CRepository. +#include "tzconfigagent.h" +#include "dataprovider.h" +#include // auto-update notification constants. +#include +#include "tzpersisted.h" +#include "tzlocalizationdb.h" +#include +#include "cbackuprestorenotification.h" +#ifdef SYMBIAN_ENABLE_SPLIT_HEADERS +#include +#endif + +_LIT8(KUnknownTZName, "Unknown/Zone"); + +const TUint32 KUnknownTZId = 0x0ffffff0; +const TInt KSecondsPerMinute = 60; + +_LIT(KTimeStamps, "c:\\private\\1020383e\\timestamps"); +_LIT(KTzDbFileNameFlash,"c:\\private\\1020383e\\TZDB.DBZ"); +_LIT(KUserDatabaseName, "c:\\private\\1020383e\\SQLite__tzuserdata.db"); +_LIT(KResourceDir, "c:\\resource\\timezonelocalization\\"); + +// specifies the limits for rules caching. +// (year - KRuleCacheLowerLimit) , (year + KRuleCacheUpperLimit) +// the actual cache includes one more year at year - (KRuleCacheLowerLimit + 1) +// required to provide the offset used at the start of the search +const TUint KRuleCacheLowerLimit = 2; +const TUint KRuleCacheUpperLimit = 3; + +// Security policy for the Publish and Subscribe auto-update notification property. +const TInt KServerUid = 0x1020383E; +const TInt KNextDSTChangePropertyKey(0x10285B32); +_LIT_SECURITY_POLICY_PASS(KReadPolicy); +_LIT_SECURITY_POLICY_S0(KWritePolicy, KServerUid); + + +CTzConfigAgent::~CTzConfigAgent() + { + // Flag that the server is shutting down so that we don't try and set + // a new DST Change Timer when the current one is cancelled. + iShuttingDown = ETrue; + + delete iTzDataProvider; + + // Cancel any exitsing timer. + if (iDstTimer) + { + iDstTimer->Cancel(); + delete iDstTimer; + } + + delete iSystemTzCache; + delete iOtherTzCache; + delete iRepository; + delete iChangeNotifier; + delete iBackupNotifier; + iTzSwiObserver.RemoveObserver(this); + iChangeObservers.Close(); + iFs.Close(); + } + +/* +*/ +CTzConfigAgent::CTzConfigAgent(MTZCfgAgentObserver& aServer, CTzUserDataDb& aTzUserDataDb, + CTzSwiObserver& aTzSwiObserver) + : iTzServer(aServer), + iEnabled(RTz::ETZAutoDSTUpdateOn), + iTzSwiObserver(aTzSwiObserver), + iShuttingDown(EFalse), + iTzUserDataDb(aTzUserDataDb) + { + } +/* +*/ +void CTzConfigAgent::ConstructL() + { + User::LeaveIfError(iFs.Connect()); + + // Define a property using Publish and Subscribe. + // ** Note that the value of this property is unimportant - setting it + // (to any value) counts as a notification, so we do not set it here. + TInt err = RProperty::Define( + NTzUpdate::EUtcOffsetChangeNotification, + RProperty::EInt, + KReadPolicy, + KWritePolicy); + + if (err != KErrAlreadyExists) + { + User::LeaveIfError(err); + } + + err = RProperty::Define( + NTzUpdate::ECurrentTimeZoneId, + RProperty::EByteArray, + KReadPolicy, + KWritePolicy); + + if (err != KErrAlreadyExists) + { + User::LeaveIfError(err); + } + + err = RProperty::Define( + NTzUpdate::EHomeTimeZoneId, + RProperty::EByteArray, + KReadPolicy, + KWritePolicy); + + if (err != KErrAlreadyExists) + { + User::LeaveIfError(err); + } + + err = RProperty::Define( + NTzUpdate::ETzRulesChange, + RProperty::EByteArray, + KReadPolicy, + KWritePolicy); + + if (err != KErrAlreadyExists) + { + User::LeaveIfError(err); + } + + err = RProperty::Define( + NTzUpdate::ETzNamesChange, + RProperty::EByteArray, + KReadPolicy, + KWritePolicy); + + if (err != KErrAlreadyExists) + { + User::LeaveIfError(err); + } + + iTzDataProvider = CTzDataProvider::NewL(iFs, iTzSwiObserver, iTzUserDataDb, + *this); + + iDstTimer = CDstEventNotifier::NewL(*this); + + iRepository = CRepository::NewL(NTzUpdate::KPropertyCategory); + + // Get the DST auto-update configuration value from the repository. + User::LeaveIfError(iRepository->Get(EConfigurationKey, iEnabled)); + + // Get the current time zone from the repository. + TInt timeZoneIdentity(0); + User::LeaveIfError(iRepository->Get(ETimeZoneKey, timeZoneIdentity)); + + // Extract the current time zone identifier. This is stored in the bits + // not occupied by the old TLocale DST settings. + TUint localeZone = timeZoneIdentity & ~(EDstHome | EDstEuropean | + EDstNorthern | EDstSouthern); + + // If the current time zone identifier is set (non-zero) then check that + // this zone exists. + TBool timeZoneExists = EFalse; + if (localeZone > 0) + { + timeZoneExists = iTzDataProvider->IsIdInDbL(localeZone); + } + + CTzId* timeZoneId = NULL; + if(timeZoneExists) + { + // Use the current time zone identifier obtained from the repository. + timeZoneId = CTzId::NewL(localeZone); + } + else + { + // Either the current time zone is not set in the repository or it is + // and the time zone does not exist. In either case we need to use the + // default time zone identifier. + timeZoneId = iTzDataProvider->GetDefaultTimeZoneIdL(); + if (!timeZoneId) + { + // If we cannot obtain the default time zone identifier then use the + // "unknown" time zone identifier as a fallback solution. + timeZoneId = CTzId::NewL(KUnknownTZId); + } + } + + CleanupStack::PushL(timeZoneId); + + // Setting the time zone updates the UTC offset and sets a new DST change + // timer if required. + SetTimeZoneL(*timeZoneId, static_cast(this), ETrue); + + CleanupStack::PopAndDestroy(timeZoneId); + + // Create and start the environment change notifier. + iChangeNotifier = CEnvironmentChangeNotifier::NewL(CActive::EPriorityIdle, TCallBack(EnvironmentChangeCallBackL, this)); + iChangeNotifier->Start(); + + TRAP_IGNORE(PublishNewDstChangeInfoL()); + + iTzSwiObserver.AddObserverL(this); + + // Create backup notifier. Note that file name argument is unimportant. + iBackupNotifier = CBackupRestoreNotification::NewL(KTzDbFileNameFlash(),*this); + + // Publish any changes that may have happened to TZ databases or resources + // while the TZ Server was not running. + PublishTzDbChangeInfoL(); + } + +void CTzConfigAgent::PublishTzDbChangeInfoL() + { + RFile file; + CleanupClosePushL(file); + + TTime tzDbModified = Time::NullTTime(); + TTime userDbModified = Time::NullTTime(); + TTime resourceFileModified = Time::NullTTime(); + + TInt err = file.Open(iFs, KTimeStamps, EFileRead | EFileWrite); + if(err == KErrNone || err == KErrNotFound) + { + //Read time stamps + TInt err1 = iFs.Modified(KTzDbFileNameFlash, tzDbModified); + if(err1 != KErrNone && err1 != KErrNotFound) + { + User::Leave(err1); + } + err1 = iFs.Modified(KUserDatabaseName, userDbModified); + if(err1 != KErrNone && err1 != KErrNotFound) + { + User::Leave(err1); + } + resourceFileModified = GetResourceFileTimeStampsL(); + } + else + { + User::Leave(err); + } + + TBool tzRuleChanged = EFalse; + TBool tzNameChanged = EFalse; + + TTime tzDbSaved = Time::NullTTime(); + TTime userDbSaved = Time::NullTTime(); + TTime resourceFileSaved = Time::NullTTime(); + + if (err == KErrNone) + { + ReadTimeStampsL(file, tzDbSaved, userDbSaved, resourceFileSaved); + + if(tzDbModified != tzDbSaved) + { + tzRuleChanged = ETrue; + } + if(userDbModified != userDbSaved) + { + tzRuleChanged = ETrue; + tzNameChanged = ETrue; + } + if(resourceFileModified != resourceFileSaved) + { + tzNameChanged = ETrue; + } + } + else//err == KErrNotFound + {//Always publish the changes if the time stamps file is not there + tzRuleChanged = ETrue; + tzNameChanged = ETrue; + } + + if(tzRuleChanged || tzNameChanged) + {//Rewrite all three times + User::LeaveIfError(file.Replace(iFs, KTimeStamps, EFileRead | EFileWrite)); + WriteTimeStampsL(file, tzDbModified, userDbModified, resourceFileModified); + } + CleanupStack::PopAndDestroy(&file); // file.Close() + + if (tzRuleChanged || tzNameChanged) + { + iTzDbHasChanged = ETrue; + if (tzRuleChanged) + { + //Publish the tz rules change + NTzUpdate::TTzRulesChange rulesChange; + rulesChange.iUTCTimeOfRulesChange.UniversalTime(); + TPckgBuf bufRules(rulesChange); + RProperty::Set(NTzUpdate::KPropertyCategory, NTzUpdate::ETzRulesChange, bufRules); + } + + if(tzNameChanged ) + { + //Publish the names change for user defined DB or resource file change + NTzUpdate::TTzNamesChange namesChange; + namesChange.iUTCTimeOfNamesChange.UniversalTime(); + TPckgBuf bufNames(namesChange); + RProperty::Set(NTzUpdate::KPropertyCategory, NTzUpdate::ETzNamesChange, bufNames); + } + } + } + +void CTzConfigAgent::ReadTimeStampsL(RFile& aFile, TTime& aTzdbSaved, TTime& aUserDbSaved, TTime& aResourceFileSaved) + { + RFileReadStream readStream(aFile); + CleanupClosePushL(readStream); + + TUint32 low = readStream.ReadUint32L(); + TUint32 high = readStream.ReadUint32L(); + aTzdbSaved = TTime(MAKE_TINT64(high, low)); + + low = readStream.ReadUint32L(); + high = readStream.ReadUint32L(); + aUserDbSaved = TTime(MAKE_TINT64(high, low)); + + low = readStream.ReadUint32L(); + high = readStream.ReadUint32L(); + aResourceFileSaved = TTime(MAKE_TINT64(high, low)); + CleanupStack::PopAndDestroy(&readStream); + } + +void CTzConfigAgent::WriteTimeStampsL(RFile& aFile, const TTime& aTzdbModified, const TTime& aUserDbModified, const TTime& aResourceFileModified) + { + RFileWriteStream writeStream(aFile); + CleanupClosePushL(writeStream); + writeStream << I64LOW(aTzdbModified.Int64()); + writeStream << I64HIGH(aTzdbModified.Int64()); + writeStream << I64LOW(aUserDbModified.Int64()); + writeStream << I64HIGH(aUserDbModified.Int64()); + writeStream << I64LOW(aResourceFileModified.Int64()); + writeStream << I64HIGH(aResourceFileModified.Int64()); + writeStream.CommitL(); + CleanupStack::PopAndDestroy(&writeStream); + } + +/* Get the sum of time stamps of time zone resource files + * It returns Time::NullTTime() if there is no resorece files in the "c:\\resource\\timezonelocalization\\" + */ +TTime CTzConfigAgent::GetResourceFileTimeStampsL() + { + CDir* files = NULL; + TInt err = iFs.GetDir(KResourceDir, KEntryAttNormal, ESortNone, files); + CleanupStack::PushL(files); + TInt64 sum = 0; + if(err != KErrNone && err != KErrNotFound && err != KErrPathNotFound) + { + User::Leave(err); + } + if(files) + { + TInt count = files->Count(); + for(TInt ii = 0; ii < count; ++ii) + { + const TEntry& entry = (*files)[ii]; + sum = sum + entry.iModified.Int64(); + } + } + CleanupStack::PopAndDestroy(files); + if(sum==0) + { + return Time::NullTTime(); + } + return TTime(sum); + } + +void CTzConfigAgent::PublishNewDstChangeInfoL() + { + TInt propertyDefineError = RProperty::Define(KUidSystemCategory, + KNextDSTChangePropertyKey, + RProperty::EByteArray, + KReadPolicy, + KWritePolicy, + sizeof(NTzUpdate::TDSTChangeInfo)); + + if (propertyDefineError != KErrAlreadyExists) + { + // if there was an actual error defining the property then we should leave + User::LeaveIfError(propertyDefineError); + } + + // create the next DST change and set some default values + NTzUpdate::TDSTChangeInfo newDSTChangeInfo; + newDSTChangeInfo.iVersion = 2; + + if ( CalculateNextDstChangeL(newDSTChangeInfo.iNextDSTChangeUTC, newDSTChangeInfo.iPreviousDSTChangeUTC) + && (iEnabled == RTz::ETZAutoDSTUpdateOn) ) + { + // There is a future DST change in this time zone and auto DST updates are on + CVTzActualisedRules& actualisedRules = GetTimeZoneRulesL(newDSTChangeInfo.iNextDSTChangeUTC, ETzUtcTimeReference); + newDSTChangeInfo.iNextUTCOffset = actualisedRules.GetOffsetFromRuleL(newDSTChangeInfo.iNextDSTChangeUTC, ETzUtcTimeReference); + newDSTChangeInfo.iPreviousUTCOffset = actualisedRules.GetOffsetFromRuleL(newDSTChangeInfo.iPreviousDSTChangeUTC-TTimeIntervalMinutes(1), ETzUtcTimeReference); + } + else + { + newDSTChangeInfo.iNextDSTChangeUTC = Time::NullTTime(); + newDSTChangeInfo.iNextUTCOffset = 0; + newDSTChangeInfo.iPreviousDSTChangeUTC = Time::NullTTime(); + newDSTChangeInfo.iPreviousUTCOffset = 0; + } + + // get the current value that is published + TPckgBuf currentNextDSTChangeBuf; + User::LeaveIfError(RProperty::Get(KUidSystemCategory, KNextDSTChangePropertyKey, currentNextDSTChangeBuf)); + + // Publish if the next DST change is different from the previous version + if ((currentNextDSTChangeBuf().iNextUTCOffset != newDSTChangeInfo.iNextUTCOffset) + || (currentNextDSTChangeBuf().iNextDSTChangeUTC != newDSTChangeInfo.iNextDSTChangeUTC) + || (currentNextDSTChangeBuf().iPreviousUTCOffset != newDSTChangeInfo.iPreviousUTCOffset) + || (currentNextDSTChangeBuf().iPreviousDSTChangeUTC != newDSTChangeInfo.iPreviousDSTChangeUTC) + || (propertyDefineError != KErrAlreadyExists)) + { + // package the data and set the property value + TPckgBuf newDSTChangeInfoBuf(newDSTChangeInfo); + User::LeaveIfError(RProperty::Set(KUidSystemCategory, KNextDSTChangePropertyKey, newDSTChangeInfoBuf)); + } + } + +/* +*/ +CTzConfigAgent* CTzConfigAgent::NewL(MTZCfgAgentObserver& aServer, CTzUserDataDb& aUserDataDb, + CTzSwiObserver& aTzSwiObserver) + { + CTzConfigAgent* self = new(ELeave) CTzConfigAgent(aServer, aUserDataDb, aTzSwiObserver); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; + } + +const CTzId& CTzConfigAgent::GetTimeZoneIdL() const + { + if (!iSystemTzCache) + { + User::Leave(KErrNotFound); + } + + return iSystemTzCache->TimeZoneId(); + } + +const CTzId& CTzConfigAgent::SetTimeZoneL(const CTzId& aZoneId, const TAny* aRequester, TBool aUpdateCentralRepository, TBool aForceCacheUpdate) + { + TInt oldTzId = 0; + TBool zoneChanged = EFalse; + TTime now; + now.UniversalTime(); + + if (!iSystemTzCache) + { + iSystemTzCache = CSystemTzRulesCache::NewL(aZoneId, *this, now); + } + else if (iSystemTzCache->TimeZoneId() == aZoneId && !aForceCacheUpdate) + { + return (iSystemTzCache->TimeZoneId()); + } + else + { + oldTzId = iSystemTzCache->TimeZoneId().TimeZoneNumericID(); + // replace the rule. create new before deleting old + CSystemTzRulesCache* newZone = CSystemTzRulesCache::NewL(aZoneId, *this, now); + CleanupStack::PushL(newZone); + + //For Invalid Timezone, rules doesnot exist + CTzRules& newZoneRules = newZone->GetEncodedTimeZoneRulesL(); + if (newZoneRules.Count() == 0) + { + User::Leave(KErrNotFound); + } + + delete iSystemTzCache; + iSystemTzCache = newZone; + + CleanupStack::Pop(newZone); + zoneChanged = ETrue; + } + + TInt offset = iSystemTzCache->Rules().GetOffsetFromRuleL(now, ETzUtcTimeReference); + + // We need to publish an automatic time update event + // Stop the auto time update observer and + // remove the published auto-update time. + // if the old and new timezone ids are the same but + // the new offset differs from what was originally there + + if (iSystemTzCache->TimeZoneId().TimeZoneNumericID() != KUnknownTZId) + { + // Now set the new time zone and new UTC Offset. + TInt TimeZoneIdentity((TDaylightSavingZone)(iSystemTzCache->TimeZoneId().TimeZoneNumericID())); + + if(aUpdateCentralRepository) + { + // Set the currently used time zone from central repository + User::LeaveIfError(iRepository->Set(ETimeZoneKey, TimeZoneIdentity)); + } + + // Set the new UTC Offset. + const TTimeIntervalSeconds newUtcOffset = offset * KSecondsPerMinute; + User::SetUTCOffset(newUtcOffset); + + // replace cached current time zone + if (zoneChanged) + { + iTzServer.NotifyTZStatusChange(RTz::ETZSystemTimeZoneChanged, aRequester); + } + +// Do not publish updates or set timers if compiled as part of +// the unit test as this will conflict with the Time Zone Server. +#ifndef SYMBIAN_TZ_UNIT_TEST + + // Publish UTC Offset and set DST Change Timer if configured. + // Note that it would make sense to always publish the UTC Offset here (if it + // changes) as this is not a change due to the auto-update functionality, but + // this would make the PREQ 234 changes only partly configurable. + if (iEnabled == RTz::ETZAutoDSTUpdateOn) + { + // Set a new DST Change Timer if required. + SetDstChangeTimerL(); + } +#endif // SYMBIAN_TZ_UNIT_TEST + + NTzUpdate::TTimeZoneChange change; + change.iNewTimeZoneId = iSystemTzCache->TimeZoneId().TimeZoneNumericID(); + change.iOldTimeZoneId = oldTzId; + + TPckgBuf changeBuf(change); + RProperty::Set(NTzUpdate::KPropertyCategory, NTzUpdate::ECurrentTimeZoneId, changeBuf); + } + + iTzDataProvider->ReleaseTzRules(); + + TRAP_IGNORE(PublishNewDstChangeInfoL()); + + return (iSystemTzCache->TimeZoneId()); + } + +CTzRules& CTzConfigAgent::GetEncodedTimeZoneRulesL() + { + return iSystemTzCache->GetEncodedTimeZoneRulesL(); + } + +TInt CTzConfigAgent::GetEncodedTimeZoneRulesSizeL(const TTime& aStartTime, const TTime& aEndTime, TTzTimeReference aTimeRef) + { + TInt size = iSystemTzCache->GetEncodedTimeZoneRulesSizeL(aStartTime, aEndTime, aTimeRef); + iTzDataProvider->ReleaseTzRules(); + return size; + } + +CTzRules& CTzConfigAgent::GetForeignEncodedTimeZoneRulesL() + { + if (iOtherTzCache == NULL) + { + User::Leave(KErrNotFound); + } + + return iOtherTzCache->GetEncodedTimeZoneRulesL(); + } + +TInt CTzConfigAgent::GetForeignEncodedTimeZoneRulesSizeL(const CTzId& aZoneId, const TTime& aStartTime, const TTime& aEndTime, TTzTimeReference aTimeRef) + { + if (!iOtherTzCache) + { + iOtherTzCache = CTZRulesCache::NewL(aZoneId, *this, aStartTime); + } + else if (iOtherTzCache->TimeZoneId() != aZoneId) + { + // ids are different. + // replace the rule. create new before deleting old + CTZRulesCache* newZone = CTZRulesCache::NewL(aZoneId, *this, aStartTime); + + delete iOtherTzCache; + iOtherTzCache = newZone; + } + + TInt size = iOtherTzCache->GetEncodedTimeZoneRulesSizeL(aStartTime, aEndTime, aTimeRef); + iTzDataProvider->ReleaseTzRules(); + return size; + } + +#if defined(_DEBUG) +/** +Required for OOM testing. To obtain a balanced heap cell count check we need to +reset any cached foreign time zone rules. +*/ +void CTzConfigAgent::ResetForeignTimeZoneRulesCache(void) + { + delete iOtherTzCache; + iOtherTzCache = NULL; + } +#endif + +CVTzActualisedRules& CTzConfigAgent::GetTimeZoneRulesL(const TTime& aTime,TTzTimeReference aTimerRef) + { + CVTzActualisedRules& rules = iSystemTzCache->GetTimeZoneRulesL(aTime,aTimerRef); + iTzDataProvider->ReleaseTzRules(); + return rules; + } + +CVTzActualisedRules& CTzConfigAgent::GetForeignTimeZoneRulesL(const CTzId& aZoneId, + const TTime& aTime, + TTzTimeReference aTimerRef) + { + if (!iOtherTzCache) + { + iOtherTzCache = CTZRulesCache::NewL(aZoneId, *this, aTime); + } + else if (iOtherTzCache->TimeZoneId() != aZoneId) + { + // ids are different. + // replace the rule. create new before deleting old + CTZRulesCache* newZone = CTZRulesCache::NewL(aZoneId, *this, aTime); + + delete iOtherTzCache; + iOtherTzCache = newZone; + } + + CVTzActualisedRules& rules = iOtherTzCache->GetTimeZoneRulesL(aTime,aTimerRef); + iTzDataProvider->ReleaseTzRules(); + return rules; + } + +/* +Retrieves the UTC offset for an array of numeric time zone ids. +The offset is written back into aTimeZoneIdArray at the position that the numeric id +was read from, overwriting the id. +@param aTimeZoneIdArray flat buffer consisting of a TInt for the number of ids, +followed by the numeric time zone id (TInt) for which the current UTC offset is required. +*/ +void CTzConfigAgent::GetOffsetsForTimeZoneIdsL(CBufFlat& aTimeZoneIdArray) + { + iTzDataProvider->GetOffsetsForTimeZoneIdsL(aTimeZoneIdArray); + iTzDataProvider->ReleaseTzRules(); + } + +void CTzConfigAgent::ConvertL(const CTzId& aZone, TTime& aTime, TTzTimeReference aTimerRef) + { + CVTzActualisedRules& rules = GetForeignTimeZoneRulesL(aZone, aTime, aTimerRef); + CTzRules& tzRules = GetForeignEncodedTimeZoneRulesL(); + iTzDataProvider->ReleaseTzRules(); + + tzRules.ConvertTime(rules, aTime, aTimerRef); + } + +void CTzConfigAgent::ConvertL(TTime& aTime, TTzTimeReference aTimerRef) + { + CVTzActualisedRules& rules = iSystemTzCache->GetTimeZoneRulesL(aTime,aTimerRef); + CTzRules& tzRules = GetEncodedTimeZoneRulesL(); + iTzDataProvider->ReleaseTzRules(); + tzRules.ConvertTime(rules, aTime, aTimerRef); + } + +TInt CTzConfigAgent::IsDaylightSavingOnL(const CTzId& aZone, TTime& aUTCTime) +{ + const CVTzActualisedRules& rules = GetForeignTimeZoneRulesL(aZone, aUTCTime, ETzUtcTimeReference); + iTzDataProvider->ReleaseTzRules(); + return rules.IsDaylightSavingOn(aUTCTime); +} + +// There is a change to the TZ database. A new one may have been installed +// Reset the current time zone information to take advantage of any changes. +void CTzConfigAgent::NotifyTZDataStatusChangeL(RTz::TTzChanges aChange) + { + if (aChange == RTz::ETZLocalizationDataChanged) + { + TInt j = 0; + TInt jEnd = iChangeObservers.Count(); + while (j < jEnd) + { + iChangeObservers[j]->NotifyTZDataStatusChangeL(aChange); + ++j; + } + + NTzUpdate::TTzNamesChange namesChange; + namesChange.iUTCTimeOfNamesChange.UniversalTime(); + TPckgBuf bufNames(namesChange); + User::LeaveIfError(RProperty::Set(NTzUpdate::KPropertyCategory, NTzUpdate::ETzNamesChange, bufNames)); + } + else + { + delete iOtherTzCache; + iOtherTzCache = NULL; + SetTimeZoneL(iSystemTzCache->TimeZoneId(), static_cast(this), ETrue, ETrue); + iTzDataProvider->ReleaseTzRules(); + + // notify change + iTzServer.NotifyTZStatusChange(aChange, static_cast(this)); + + //Publish the property + NTzUpdate::TTzRulesChange rulesChange; + rulesChange.iUTCTimeOfRulesChange.UniversalTime(); + TPckgBuf bufRules(rulesChange); + User::LeaveIfError(RProperty::Set(NTzUpdate::KPropertyCategory, NTzUpdate::ETzRulesChange, bufRules)); + } + } + +/** +Calculates the time/date (in UTC) of the next DST change event that will take +place for the current time zone. Note that there may not be a DST change for +the current time zone, in which case EFalse is returned and aNextDstEvent is +not changed. + +@param aNextDstEvent returns the time of the next DST change event. +@return ETrue if a change event was found, else EFalse. +@internalComponent + +*/ +TBool CTzConfigAgent::CalculateNextDstChangeL(TTime& aNextDstEvent) + { + TTime dummy; + return CalculateNextDstChangeL(aNextDstEvent, dummy); + } + +/** +Calculates the time/date (in UTC) of the next DST change event that will take +place for the current time zone. Note that there may not be a DST change for +the current time zone, in which case EFalse is returned and aNextDstEvent is +not changed. + +@param aNextDstEvent returns the time of the next DST change event. +@return ETrue if a change event was found, else EFalse. +@internalComponent + +*/ +TBool CTzConfigAgent::CalculateNextDstChangeL(TTime& aNextDstEvent, TTime& aPreviousDstEvent) + { + // Get the current time/date. + TTime now; + now.UniversalTime(); + + // Get the actualised rules for the current time zone + // - the next DST change will always exist within the range returned. + // (Note that the current time zone may not have any DST changes.) + CVTzActualisedRules& rules = GetTimeZoneRulesL(now,ETzUtcTimeReference); + + // Iterate through the actualised rules until we get the next rule change + // after the current time/date. (Or we reach the end of the actualised rules + // - in which case no DST change applies.) + const TInt KRuleCount = rules.Count(); + + // Store the previous offset for converting the time of change to utc. + // Note: for times before any rules applied, the dst offset is always 0. + TInt offset; + + aPreviousDstEvent = Time::MinTTime(); + for (TInt i = 0; i < KRuleCount; i++) + { + // Get the next time of change as a UTC time. + TTime utcTimeOfChange = rules[i].iTimeOfChange; + + __ASSERT_ALWAYS(rules[i].iTimeReference!=ETzStdTimeReference, RTz::Panic(RTz::EPanicUnsupportedTimeReference)); + + if (rules[i].iTimeReference == ETzWallTimeReference) + { + // Get the previous offset as this was used to create the + // local time version of utcTimeOfChange. + if (i > 0) + { + // previous iNewOffset contains the STD offset plus + // the previous DST offset + offset = rules[i-1].iNewOffset; + } + else + { + // First offset will contain just the STD offset + offset = rules[i].iNewOffset; + } + utcTimeOfChange -= static_cast(offset); + } + + if (utcTimeOfChange <= now) + { + aPreviousDstEvent = utcTimeOfChange; + } + else + { + aNextDstEvent = utcTimeOfChange; + return ETrue; + } + } + + // No DST change has been found. + return EFalse; + } + + +/** +Calculates the current UTC offset using time zone rules for the current time +zone. Note that this may be different from the value set in User::UTCOffset. + +@param aOffset returns the current UTC Offset in seconds +@internalComponent + +*/ +void CTzConfigAgent::CalculateUtcOffsetL(TTimeIntervalSeconds& aOffset) + { + // Get the current time/date. + TTime now; + now.UniversalTime(); + + // Get actualised rules for the current time zone. + CVTzActualisedRules& rules = GetTimeZoneRulesL(now,ETzUtcTimeReference); + + // Get the current UTC Offset in minutes. + TInt offsetInMinutes = rules.GetOffsetFromRuleL(now, ETzUtcTimeReference); + + // Multiply up to get the offset in seconds. + aOffset = offsetInMinutes * KSecondsPerMinute; + } + + + +/** +Gets the time/date (in UTC) of the next DST change event for the current time +zone and sets a timer if it is a valid time. + +@internalComponent + +*/ +void CTzConfigAgent::SetDstChangeTimerL() + { + // Cancel any existing DST Change Timer. + iDstTimer->Cancel(); + + // Get the time of the next DST change event (in UTC). + TTime nextTimer; + + if (CalculateNextDstChangeL(nextTimer)) + { + // If the time is valid then set the timer. + iDstTimer->SetTimer(nextTimer); + } + } + +/** +Handles cancellation or completion of a previous DST Change Timer. Updates the +UTC Offset and sets a new DST Change Timer as necessary. + +@internalComponent + +*/ +void CTzConfigAgent::HandleDstChangeTimerL() + { + // UTC offset must be updated only when Auto Update is ON + if (iEnabled == RTz::ETZAutoDSTUpdateOn) + { + UpdateUtcOffsetL(); + } + + // Notification must be generated when Auto Update is ON or NOTIFY + // (unless the server is shutting down) + if (iEnabled != RTz::ETZAutoDSTUpdateOff && !iShuttingDown) + { + // reset dst timer + SetDstChangeTimerL(); + + // Publish update notification. + // Note that the value published is the same as + // the AutoUpdate Configuration + TInt err = RProperty::Set( + NTzUpdate::KPropertyCategory, + NTzUpdate::EUtcOffsetChangeNotification, iEnabled); + User::LeaveIfError(err); + } + + TRAP_IGNORE(PublishNewDstChangeInfoL()); + } + + +/** +Calculates what the UTC Offset should be - if this differs from the current +offset then the new offset is set using User::SetUTCOffset() and subscribed +clients are notified of the change through the Publish and Subscribe API. + +@internalComponent + +*/ +void CTzConfigAgent::UpdateUtcOffsetL() + { + + // Calculate the current UTC Offset in seconds. + TTimeIntervalSeconds currentOffset; + CalculateUtcOffsetL(currentOffset); + + if (currentOffset != User::UTCOffset()) + { + // Set the new UTC Offset. + iChangeNotifier->Cancel(); + User::SetUTCOffset(currentOffset); + // enable observer again + iChangeNotifier->Start(); + // Notify session observer. + iTzServer.NotifyTZStatusChange(RTz::ETZAutomaticTimeUpdate, static_cast(this)); + } + } + +/** +Sets the configuration of the UTC Offset auto-update functionality. + +@param aUpdateEnabled If set to RTz::ETZAutoDSTUpdateOn then the UTC Offset is automatically +updated for changes to Daylight Savings Time. If set to RTz::ETZAutoDSTUpdateOff then auto-update +is disabled. The RTz::ETZAutoDSTNotificationOnly - Means that the client app needs to confirm that the +time should be updated whenever a DST event occurs. + + +@internalComponent + +*/ +void CTzConfigAgent::SetAutoUpdateBehaviorL(TInt aUpdateEnabled) + { + // Check if the configuration has changed. + if (aUpdateEnabled != iEnabled) + { + // Persist the new configuration. + iEnabled = aUpdateEnabled; + iRepository->Set(EConfigurationKey, iEnabled); + + // Handle configuration change. + if (aUpdateEnabled != RTz::ETZAutoDSTUpdateOff) + { + // Update the UTC Offset (if required) and set a new DST Change Timer. + + SetDstChangeTimerL(); + // Don't update Kernel for Notify - only for ON + if (aUpdateEnabled == RTz::ETZAutoDSTUpdateOn) + { + UpdateUtcOffsetL(); + } + } + else + { + // Cancel any existing DST Change Timer. + iDstTimer->Cancel(); + } + } + + TRAP_IGNORE(PublishNewDstChangeInfoL()); + } + + /** +Change current local time. + +@param aHomeTime The time to set in wall-clock time. + +@return An error code. KErrNone is expected unless there is an error in +converting the given local time to UTC. + +@internalComponent + +*/ +TInt CTzConfigAgent::SetHomeTimeL(const TTime& aHomeTime) + { + TTime utcTime(aHomeTime); + + ConvertL(utcTime, ETzWallTimeReference); + + //Cancel environment change notification in case double updating UTC offset. + iChangeNotifier->Cancel(); + + //Cancel DST timer. + iDstTimer->Cancel(); + + TTimeIntervalSeconds utcOffset(0); + aHomeTime.SecondsFrom(utcTime,utcOffset); + + //Update kernel time and utc offset. + TInt result = User::SetUTCTimeAndOffset(utcTime, utcOffset); + + // Get the time of the next DST change event (in UTC). + TTime nextTimer; + + if (iEnabled != RTz::ETZAutoDSTUpdateOff) + { + if (CalculateNextDstChangeL(nextTimer)) + { + // Restart the dst timer. + iDstTimer->SetTimer(nextTimer); + } + } + + //Restart observe environment change notification. + iChangeNotifier->Start(); + + TRAP_IGNORE(PublishNewDstChangeInfoL()); + + return result; + } + +TInt CTzConfigAgent::SetUnknownTimeZoneTimeL(const TTime& aUTCTime, const TInt aUTCOffset, TBool aPersistInCenRep) + { + // + // Cancel DST timer. (It will not be restarted, as we won't have DST rules for this unknown zone) + // + iDstTimer->Cancel(); + + //Update kernel time and utc offset. + const TTimeIntervalSeconds newUtcOffset = aUTCOffset * KSecondsPerMinute; + + // + // Cancel environment change notification in case double updating UTC offset. + // + iChangeNotifier->Cancel(); + + TInt result = User::SetUTCTimeAndOffset(aUTCTime, newUtcOffset); + // + // Restart environment change notification. + // + iChangeNotifier->Start(); + + User::LeaveIfError(result); + + TInt oldTzId = iSystemTzCache->TimeZoneId().TimeZoneNumericID(); + + // replace the rule, create an new unknown before deleting old + CTzId* timeZoneId = CTzId::NewL(KUnknownTZId); + CleanupStack::PushL(timeZoneId); + + // CSystemTzRulesCache::NewL will create a default rule based on the offset in the Kernel + CSystemTzRulesCache* newZone = CSystemTzRulesCache::NewL(*timeZoneId, *this, aUTCTime); + CleanupStack::PushL(newZone); + + delete iSystemTzCache; + iSystemTzCache = newZone; + + if (aPersistInCenRep) + { + // Set the unknown time zone in central repository + // 0 is used to represent the Unknow time zone value although in the code KUnknownTZId is used + User::LeaveIfError(iRepository->Set(ETimeZoneKey, (TInt)KUnknownTZId)); + } + else + { + // Set the unknown time zone in central repository + // 0 is used to represent the Unknow time zone value although in the code KUnknownTZId is used + User::LeaveIfError(iRepository->Set(ETimeZoneKey, 0)); + } + + CleanupStack::Pop(newZone); + CleanupStack::PopAndDestroy(timeZoneId); + + iTzServer.NotifyTZStatusChange(RTz::ETZSystemTimeZoneChanged, static_cast(this)); + + + NTzUpdate::TTimeZoneChange change; + change.iNewTimeZoneId = 0; + change.iOldTimeZoneId = oldTzId; + + TPckgBuf changeBuf(change); + RProperty::Set(NTzUpdate::KPropertyCategory, NTzUpdate::ECurrentTimeZoneId, changeBuf); + + iTzDataProvider->ReleaseTzRules(); + + return KErrNone; + } + +/** +Gets the daylight saving update +*/ +TInt CTzConfigAgent::AutoUpdateSetting() + { + return iEnabled; + } + + +/** +Observer method called as a result of a change in system time. Updates the UTC +Offset and sets a new DST Change Timer as necessary. + +@internalComponent + +*/ +void CTzConfigAgent::HandleSystemTimeChangeL() + { + HandleDstChangeTimerL(); + } + +/** +Observer method called as a result of a DST Change Timer completing. Updates +the UTC Offset and sets a new DST Change Timer as necessary. + +@internalComponent + +*/ +void CTzConfigAgent::HandleDstTimerCompleteL() + { + HandleDstChangeTimerL(); + } + +/** +Observer method called as a result of a DST Change Timer being cancelled. (The +underlying RTimer is cancelled - for example, because of a change to the UTC +offset.) This is not called when Cancel() or DoCancel() are called.) Updates +the UTC Offset and sets a new DST Change Timer as necessary. + +@internalComponent + +*/ +void CTzConfigAgent::HandleDstTimerCancellationL() + { + HandleDstChangeTimerL(); + } + +/** +Observer method called due to an error with the DST Change Timer that tries to +recover from the error. We only expect this to get called with a value of +KErrUnderflow (if the system time is changed to a time past the current timer) +or a value of KErrOverflow as returned by RTimer. + +@param aError The error returned from the timer. +@internalComponent + +*/ +void CTzConfigAgent::DstTimerErrorOccurredL(TInt aError) + { + // Remove the compiler warning about aError not being used. (It's useful in debugging.) + aError = aError; + + // Try and recover by updating the UTC Offset and setting a new + // DST Change Timer as necessary. + HandleDstChangeTimerL(); + } + +/** +Environment observer callback - called when the kernel causes a system change. +This can be used to pick up any changes when there is no DST Change Timer and +may also provide a faster response to system time or locale changes. + +@param aSelf A pointer to this class. +@return Always returns ETrue. +@internalComponent + +*/ +TBool CTzConfigAgent::EnvironmentChangeCallBackL(TAny* aSelf) + { + CTzConfigAgent* self = static_cast(aSelf); + + const TInt KChanges = self->iChangeNotifier->Change(); + + if (KChanges == KErrCancel) + { + return ETrue; // Observer is being cancelled. + } + + if (KChanges & (EChangesLocale | EChangesSystemTime)) + { + // Update the UTC Offset and set a new DST Change Timer as necessary. + self->UpdateUtcOffsetL(); + self->SetDstChangeTimerL(); + } + + return ETrue; + } + +/** Reponding to the change in user defined time zone + +The CTzConfigAgent class maintains a cache of TZ rules (including actualised rules for +the last used rule) for the system (also known as current) TZ in the iSystemTzCache member +and for a “foreign” TZ in the iOtherTzCache member. Either of these members could be a cache +of the TZ rules from a user-defined TZ. If the associated user-defined TZ data is updated +or deleted or a restore of user-defined TZ data has taken place then this cache needs to be +updated appropriately. +*/ +void CTzConfigAgent::NotifyUserTzRulesChange(TTzUserDataChange aChange) + { + TRAP_IGNORE(NotifyUserTzRulesChangeL(aChange)); + } +void CTzConfigAgent::NotifyUserTzRulesChangeL(TTzUserDataChange aChange) + { + //Only interested when a user TZ rule is updated or deleted + if (aChange.iOperation == ETzUserDataUpdated || aChange.iOperation == ETzUserDataDeleted) + { + //Chached time zone (iOtherTzCache) should be cleaned off. + delete iOtherTzCache; + iOtherTzCache = NULL; + + TUint id = aChange.iTzId; + if(iSystemTzCache && id == iSystemTzCache->TimeZoneId().TimeZoneNumericID()) + { + CTzId* tzid = NULL; + if (aChange.iOperation == ETzUserDataUpdated) + { + //Cached symstem time zone (iSystemTzCache) should be updated. + tzid = CTzId::NewL(id); + } + else if (aChange.iOperation == ETzUserDataDeleted) + {//Cached symstem time zone (iSystemTzCache) should be reverted to the default time zone. + tzid = iTzDataProvider->GetDefaultTimeZoneIdL(); + if(!tzid) + { + tzid = CTzId::NewL(KUnknownTZId); + } + } + CleanupStack::PushL(tzid); + SetTimeZoneL(*tzid, static_cast(this), ETrue, ETrue); + CleanupStack::PopAndDestroy(tzid); + } + } + } + +void CTzConfigAgent::NotifyUserTzNamesChange(TTzUserDataChange /*aChange*/) + {// There is no action needed when the names of a user defined time zone has been changed. + } + +void CTzConfigAgent::AddObserverL(MTzDataObserver* aChangeObs) + { + User::LeaveIfError(iChangeObservers.Append(aChangeObs)); + } + +void CTzConfigAgent::RemoveObserver(MTzDataObserver* aChangeObs) + { + TInt j = 0; + TInt jEnd = iChangeObservers.Count(); + while (j < jEnd) + { + if (iChangeObservers[j] == aChangeObs) + { + iChangeObservers.Remove(j); + break; + } + ++j; + } + } + + +void CTzConfigAgent::BackupBeginningL() + { + iTzLocalizationDb->BackupBeginningL(); + iTzDataProvider->BackupBeginningL(); + } + +void CTzConfigAgent::BackupCompletedL() + { + iTzDataProvider->BackupCompletedL(); + iTzLocalizationDb->BackupCompletedL(); + } +void CTzConfigAgent::RestoreBeginningL() + { + iTzLocalizationDb->RestoreBeginningL(); + iTzDataProvider->RestoreBeginningL(); + } + +void CTzConfigAgent::RestoreCompletedL(TInt aRestoreResult) + { + if(aRestoreResult == KErrNone) + { + //Both read only DB and user defined DB will refresh the data + iTzDataProvider->RestoreCompletedL(); + + //Update cached tz rules + delete iOtherTzCache; + iOtherTzCache = NULL; + + if(iSystemTzCache) + { + // Find out whether the cached system time zone rule still exists in + // the rules database. If not, set the current time zone using the + // default time zone identifier. + TBool timeZoneExists = iTzDataProvider->IsIdInDbL(iSystemTzCache->TimeZoneId().TimeZoneNumericID()); + if(timeZoneExists) + { + SetTimeZoneL(iSystemTzCache->TimeZoneId(), static_cast(this), EFalse, ETrue); + } + else + { + CTzId* tzid = iTzDataProvider->GetDefaultTimeZoneIdL(); + CleanupStack::PushL(tzid); + SetTimeZoneL(*tzid, static_cast(this), EFalse, ETrue); + CleanupStack::PopAndDestroy(tzid); + } + } + + // Notify change to the clients. + iTzServer.NotifyTZStatusChange(RTz::ETZDatabaseChanged, static_cast(this)); + + // Notify the localization DB. + iTzLocalizationDb->RestoreCompletedL(); + + //Publish tz rules change + NTzUpdate::TTzRulesChange rulesChange; + rulesChange.iUTCTimeOfRulesChange.UniversalTime(); + TPckgBuf bufRules(rulesChange); + RProperty::Set(NTzUpdate::KPropertyCategory, NTzUpdate::ETzRulesChange, bufRules); + + //Publish the names change for user defined DB + NTzUpdate::TTzNamesChange namesChange; + namesChange.iUTCTimeOfNamesChange.UniversalTime(); + TPckgBuf bufNames(namesChange); + RProperty::Set(NTzUpdate::KPropertyCategory, NTzUpdate::ETzNamesChange, bufNames); + } + } +//------------------------------------------------------------------------------------ +CTZRulesCache::~CTZRulesCache() + { + delete iTimeZoneId; // time zone Id + delete iActualisedRules;// caches the last used rule for zone + delete iEncodedRules; + } + +// template method used for final construction of a time zone rules cache +// this is the base implementation -- see ConstructL() +void CTZRulesCache::SetDefaultZoneIdL() + { + // does nothing + User::Leave(KErrNotFound); + } + +const CTzRules& CTZRulesCache::doGetEncodedTimeZoneRulesL(TInt aStartYear, TInt aEndYear) + { + CTzDataProvider& tzDataProvider = iTimeZoneConfigAgent.TzDataProvider(); + + CTzRules* newRules = CTzRules::NewL(aStartYear, aEndYear); + CleanupStack::PushL(newRules); + + tzDataProvider.GetTzRulesL(*newRules, *iTimeZoneId); + + + CleanupStack::Pop(newRules); + + // replace current cached rules + delete iEncodedRules; + iEncodedRules = newRules; + + return *iEncodedRules; + } + +void CTZRulesCache::doGetActualisedTimeZoneRulesL(const TTime& aTime) + { + TDateTime dateTime(aTime.DateTime() ); + + // At least we need the rule for the year preceeding the converted time + // in order to convert the time to use it to find the correct rule. (Sounds roundabout!) + // hence the extra one added to the cache limit + + // avoid over/under flow + TUint startYear = dateTime.Year(); + TUint endYear = startYear; + if (startYear >= (KRuleCacheLowerLimit+1)) + { + startYear -= (KRuleCacheLowerLimit+1); + } + if (endYear < (KMaxTUint - KRuleCacheUpperLimit)) + { + endYear += KRuleCacheUpperLimit; + } + + CVTzActualisedRules* newRules = CVTzActualisedRules::NewL(startYear, endYear); + CleanupStack::PushL(newRules); + + iEncodedRules->GetActualisedRulesL(*newRules); + + if (newRules->Count() == 0) // there will always be at least one rule + { + User::Leave(KErrNotFound); + } + + CleanupStack::Pop(newRules); + + // replace current cached rules + delete iActualisedRules; + iActualisedRules = newRules; + } + +void CTZRulesCache::ConstructL(const CTzId& aTimeZoneId, const TTime& aTime) + { + if (aTimeZoneId.TimeZoneNumericID() != KUnknownTZId) + { + iTimeZoneId = aTimeZoneId.CloneL(); + + TRAPD(err,doGetEncodedTimeZoneRulesL(KMinYear, KMaxYear)); + if (err == KErrNotFound) + { + SetDefaultZoneIdL(); + } + else + { + User::LeaveIfError(err); + + // avoid over/under flow + TDateTime dateTime(aTime.DateTime() ); + TUint startYear = dateTime.Year(); + TUint endYear = startYear; + + if (startYear >= (KRuleCacheLowerLimit+1)) + { + startYear -= (KRuleCacheLowerLimit+1); + } + if (endYear < (KMaxTUint - KRuleCacheUpperLimit)) + { + endYear += KRuleCacheUpperLimit; + } + + iActualisedRules = CVTzActualisedRules::NewL(startYear, endYear); + iEncodedRules->GetActualisedRulesL(*iActualisedRules); + } + } + else + { + // Call Template Method to Set defaultZoneId + SetDefaultZoneIdL(); + } + } + + +CTZRulesCache* CTZRulesCache::NewL(const CTzId& aTimeZoneId, + CTzConfigAgent& aTimeZoneConfigAgent, + const TTime& aTime) + { + CTZRulesCache* self = new (ELeave) CTZRulesCache(aTimeZoneConfigAgent); + CleanupStack::PushL(self); + + self->ConstructL(aTimeZoneId, aTime); + + CleanupStack::Pop(self); + return self; + } + +CTzRules& CTZRulesCache::GetEncodedTimeZoneRulesL() + { + // return the rules held in cache + if (iEncodedRules == NULL) + { + User::Leave(KErrNotFound); + } + return *iEncodedRules; + } + +TInt CTZRulesCache::GetEncodedTimeZoneRulesSizeL(const TTime& aStartTime, const TTime& aEndTime, TTzTimeReference /*aTimeRef */) + { + // fetch a new set of rules from the data provider if the range of the rules in iEncodedRules does not + // contain both aStartTime and aEndTime + if ( + (iTimeZoneId->TimeZoneNumericID() != KUnknownTZId) + && + ( (iEncodedRules == NULL) || !(iEncodedRules->RulesApply(aStartTime)) || !(iEncodedRules->RulesApply(aEndTime) ) ) + ) + { + TInt startYear = aStartTime.DateTime().Year(); + TInt endYear = aEndTime.DateTime().Year(); + doGetEncodedTimeZoneRulesL(startYear, endYear); + } + + if (iEncodedRules) + { + return iEncodedRules->Count() * sizeof (TTzRule) + sizeof (CTzRules); + } + else + { + return 0; + } + } + +CVTzActualisedRules& CTZRulesCache::GetTimeZoneRulesL(const TTime& aTime,TTzTimeReference aTimerRef) + { + if ((iTimeZoneId->TimeZoneNumericID() != KUnknownTZId) + && (!(RuleApplies(*iActualisedRules, aTime, aTimerRef)))) + { + doGetActualisedTimeZoneRulesL(aTime); + } + return *iActualisedRules; + } + +/** +Tests if the period covered by the cached rules applies to the supplied time. +*/ +TBool CTZRulesCache::RuleApplies(CVTzActualisedRules& actRules, + const TTime& aTime, TTzTimeReference aTimerRef) const + { + TInt startYear = actRules.StartYear(); + TInt endYear = actRules.EndYear(); + + TDateTime dateTime(aTime.DateTime()); + + __ASSERT_ALWAYS(aTimerRef!=ETzStdTimeReference, RTz::Panic(RTz::EPanicUnsupportedTimeReference)); + + // aTime may be in UTC or wall time. If aTime is provided + // in wall time the range which is checked will be reduced by one year + // on either side, to avoid false inclusions. eg. If the time zone was + // Japan and the date passed in here was Jan 1st at 6:00 local time, + // that would be a different year in UTC time. This will result + // in more fetches to the database in some cases, but the alternative + // is to iterate through all the rules every time this method is called + // to find the matching rule, which is not very efficent in itself. + + if (aTimerRef == ETzWallTimeReference) + { + // This reduces the range that is checked. For example, if the + // actualised rules are from 2000 to 2006, this will + // check Jan 2001 to Jan 2006 + startYear++; + } + else + { + // This checks the whole range. For example, if the + // actualised rules are from 2000 to 2006, this will + // check Jan 2000 to Jan 2007 + endYear++; + } + + const TTime KStart(TDateTime(startYear, EJanuary, 0, 0, 0, 0, 0)); + const TTime KEnd(TDateTime(endYear, EJanuary, 0, 0, 0, 0, 0)); + return ((aTime >= KStart) && (aTime < KEnd)); + } + +//------------------------------------------------------------------------------------ +// +CSystemTzRulesCache::~CSystemTzRulesCache() + { + // does nothing + } + + +void CSystemTzRulesCache::SetDefaultZoneIdL() + { + // default to old TLocale DST Rule + delete iTimeZoneId; + iTimeZoneId = NULL; + iTimeZoneId = CTzId::NewL(KUnknownTZName); + iTimeZoneId->SetId(KUnknownTZId); + + // The UTC Offset combines both the standard and the dst offset + TInt stdOffset = User::UTCOffset().Int() / KSecondsPerMinute; + + // create a default std only encoded rule + delete iEncodedRules; + iEncodedRules = NULL; + iEncodedRules = CTzRules::NewL(KMinYear,KMaxYear); + TTimeWithReference timeRef; + TTimeWithReference timeRef2(TTimeWithReference::Max()); + TTzRule encodedRule(timeRef, timeRef2,stdOffset,stdOffset,EJanuary,ETzFixedDate,0,0,ETzWallTimeReference,0); + iEncodedRules->AddRuleL(encodedRule); + + delete iActualisedRules; + iActualisedRules = NULL; + iActualisedRules = CVTzActualisedRules::NewL(KMinYear, KMaxYear); + iEncodedRules->GetActualisedRulesL(*iActualisedRules); + } + + +CSystemTzRulesCache* CSystemTzRulesCache::NewL(const CTzId& aTimeZoneId, + CTzConfigAgent& aTimeZoneConfigAgent, + const TTime& aTime) + { + CSystemTzRulesCache* self = new (ELeave) CSystemTzRulesCache(aTimeZoneConfigAgent); + CleanupStack::PushL(self); + + self->ConstructL(aTimeZoneId, aTime); + + CleanupStack::Pop(self); + return self; + } + + + +//------------------------------------------------------------------------------------ +// CDstEventNotifier +// + +/** +Creates a new instance of CDstEventNotifier on the heap. + +@param aObserver the observer of the timer. +@return CDstEventNotifier* the instance created. +@internalComponent + +*/ +CDstEventNotifier* CDstEventNotifier::NewL(MDstEventObserver& aObserver) + { + CDstEventNotifier* self = new (ELeave) CDstEventNotifier(aObserver); + CleanupStack::PushL(self); + self->ConstructL(); // Construct CTimer. + CActiveScheduler::Add(self); + CleanupStack::Pop(self); + return self; + } + +/** +Sets a new DST Change Timer at the given time. + +@param aUtcTime the UTC time of the DST event. +@internalComponent + +*/ +void CDstEventNotifier::SetTimer(const TTime& aUtcTime) + { + // Cancel any existing timer + Cancel(); + // Start the new timer. + AtUTC(aUtcTime); + } + +/** +Constructor. + +@param aObserver the observer of the timer. +@internalComponent + +*/ +CDstEventNotifier::CDstEventNotifier(MDstEventObserver& aObserver) + : CTimer(EPriorityStandard), + iObserver(aObserver) + { + } + +/** +Called when the timer completes. The observer is notified. + +@internalComponent + +*/ +void CDstEventNotifier::RunL() + { + // Notify the observer. + // Notifications based on the behaviour of RTimer + + switch (iStatus.Int()) + { + case KErrNone: + // The timer completed normally at the requested time. + iObserver.HandleDstTimerCompleteL(); + break; + case KErrAbort: + // The timer was aborted because the system time changed. + // This is taken care of by the environment change notifier + // but is still handled to avoid falling into the default case + // and setting the alarm twice for each locale/time change. + break; + case KErrCancel: + // The timer was cancelled. + iObserver.HandleDstTimerCancellationL(); + break; + default: + // KErrUnderflow, KErrOverflow may be expected if an out-of-range timer is set. + iObserver.DstTimerErrorOccurredL(iStatus.Int()); + break; + } + } + +/** +Called if RunL leaves. + +@internalComponent + +*/ +TInt CDstEventNotifier::RunError(TInt aError) + { + // Just return the error. + return aError; + } + +/** +Cancels any existing timer. + +@internalComponent + +*/ +void CDstEventNotifier::DoCancel() + { + // Cancel the timer. + CTimer::DoCancel(); + + // Do not notify the observer here as it will create an infinite loop! + // Cancellation of the RTimer is notified in RunL() with a status of KErrCancel. + // That is what MDstEventObserver::HandleDstTimerCancellationL() is for. + }