diff -r 000000000000 -r 2e3d3ce01487 tzservices/tzserver/Server/Source/tzdbentities.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/tzservices/tzserver/Server/Source/tzdbentities.cpp Tue Feb 02 10:12:00 2010 +0200 @@ -0,0 +1,1236 @@ +// Copyright (c) 2004-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 "tzdbentities.h" +#include "ReadOnlyTzDb.h" +#include + +/** +This const must match the last TTzRuleDay enumeration in tzdefines.h. +@internalComponent +*/ +const TInt KValidatedLastDayRule = ETzDayInLastWeekOfMonth; + +/** +This const must match the last TTzTimeReference enumeration in tzdefines.h. +@internalComponent +*/ +const TInt KValidatedLastTimeReference = ETzWallTimeReference; + +//============================================================================= + +CTzDbStringsTable* CTzDbStringsTable::NewL(const TTzStringsTable& aRegionsTable) + { + CTzDbStringsTable* self = new(ELeave) CTzDbStringsTable(aRegionsTable); + return self; + } + +CTzDbStringsTable::CTzDbStringsTable(const TTzStringsTable& aRegionsTable) + : iPersistedEntity(aRegionsTable) + { + } + +TPtrC8 CTzDbStringsTable::GetStringL(TUint aReferenceToString) + { + const TInt KStringsBaseAddress = (TInt)&iPersistedEntity; + const TInt KLengthAddress = KStringsBaseAddress + aReferenceToString; + const TInt KStringAddress = KLengthAddress + 1; // length occupies 1 byte + TUint8 length = *(reinterpret_cast(KLengthAddress)); + TPtrC8 theString(reinterpret_cast(KStringAddress),length); + return theString; + } + +//============================================================================= + +CTzDbRegionsTable* CTzDbRegionsTable::NewL(CReadOnlyTzDb& aReadOnlyTzDb, const TTzRegionsTable& aRegionsTable) + { + CTzDbRegionsTable* self = new(ELeave) CTzDbRegionsTable(aReadOnlyTzDb, aRegionsTable); + return self; + } + +CTzDbRegionsTable::CTzDbRegionsTable(CReadOnlyTzDb& aReadOnlyTzDb, const TTzRegionsTable& aRegionsTable) +: iReadOnlyTzDb(aReadOnlyTzDb), + iPersistedEntity(aRegionsTable) + { + } + +// +// Looks for a region given its name. +// Returns the region wrapped in a CTzDbRegion object. +// Returns NULL if region is not found +// +CTzDbRegion* CTzDbRegionsTable::FindRegionL(const TDesC8& aRegionName) + { + const TInt KRegionCount = iPersistedEntity.iNumberOfRegions; + TTzRegion* region(NULL); + TPtrC8 name; + TInt regionAddress = reinterpret_cast(&iPersistedEntity) + sizeof(iPersistedEntity.iNumberOfRegions); + for (TInt i = 0; i < KRegionCount; i++, regionAddress += sizeof(TTzRegion)) + { + region = reinterpret_cast(regionAddress); + name.Set(iReadOnlyTzDb.GetStringL(region->iOffsetToRegionName)); + + // compare both strings + if (aRegionName.Compare(name) == 0) // found our region + { + return CTzDbRegion::NewL(iReadOnlyTzDb,*region); + } + } + + // if it gets here, it means that the region has not been found + return NULL; + } + +//============================================================================= + +CTzDbRegionalZonesTable* CTzDbRegionalZonesTable::NewL(CReadOnlyTzDb& aReadOnlyTzDb, const TTzRegionalZonesTable& aRegionalZonesTable) + { + CTzDbRegionalZonesTable* self = new(ELeave) CTzDbRegionalZonesTable(aReadOnlyTzDb, aRegionalZonesTable); + return self; + } + +CTzDbRegionalZonesTable::CTzDbRegionalZonesTable(CReadOnlyTzDb& aReadOnlyTzDb, const TTzRegionalZonesTable& aRegionalZonesTable) +: iReadOnlyTzDb(aReadOnlyTzDb), + iPersistedEntity(aRegionalZonesTable) + { + } + +const TTzRegionalZoneIndex& CTzDbRegionalZonesTable::GetTRegionalZoneIndex(TUint aReference) + { + return * reinterpret_cast (aReference + reinterpret_cast(&iPersistedEntity)); + } + +//============================================================================= + +CTzDbZonesTable* CTzDbZonesTable::NewL(CReadOnlyTzDb& aReadOnlyTzDb, const TTzZonesTable& aZonesTable, TInt aZonesDataBaseAddress) + { + CTzDbZonesTable* self = new(ELeave) CTzDbZonesTable(aReadOnlyTzDb, aZonesTable, aZonesDataBaseAddress); + return self; + } + +CTzDbZonesTable::CTzDbZonesTable(CReadOnlyTzDb& aReadOnlyTzDb, const TTzZonesTable& aZonesTable, TInt aZonesDataBaseAddress) +: iReadOnlyTzDb(aReadOnlyTzDb), + iPersistedEntity(aZonesTable), + iZonesDataBaseAddress(aZonesDataBaseAddress) + { + } + +CTzDbZone* CTzDbZonesTable::GetZoneL(TUint aReference) + { + return CTzDbZone::NewL(iReadOnlyTzDb, GetTZone(aReference)); + } + +TTzZone& CTzDbZonesTable::GetTZone(TUint aReference) + { + return *reinterpret_cast(aReference + iZonesDataBaseAddress); + } + +// +// Performs a binary search in the zones table, looking for the zone with the +// supplied numeric ID. +// +// IMPORTANT: Assumes zones are sorted by LocationId in zones table. +// +CTzDbZone* CTzDbZonesTable::GetZoneByIdL(TUint aNumericId) + { + TTzZone* tzone(NULL); + const TInt KZoneCount = iPersistedEntity.iNumberOfZones; + TInt start = 0; + TInt end = KZoneCount - 1; + TInt mid; + TBool found = EFalse; + while(start <= end) + { + mid = (end + start) / 2; + tzone = &GetTZone(iPersistedEntity.iOffsetsToZones[mid]); + if (aNumericId < tzone->iLocationId) + { + end = mid - 1; + } + else if (aNumericId == tzone->iLocationId) + { + // found our zone + found = ETrue; + break; + } + else + { + start = mid + 1; + } + } + + if (found) + { + return CTzDbZone::NewL(iReadOnlyTzDb, *tzone); + } + else + { + return NULL; + } + } + +//============================================================================= + +CTzDbLinksTable* CTzDbLinksTable::NewL(CReadOnlyTzDb& aReadOnlyTzDb, const TTzLinksTable& aLinksTable) + { + CTzDbLinksTable* self = new(ELeave) CTzDbLinksTable(aReadOnlyTzDb, aLinksTable); + return self; + } + +CTzDbLinksTable::CTzDbLinksTable(CReadOnlyTzDb& aReadOnlyTzDb, const TTzLinksTable& aLinksTable) +: iReadOnlyTzDb(aReadOnlyTzDb), + iPersistedEntity(aLinksTable) + { + } + +CTzDbZone* CTzDbLinksTable::FindZoneL(const TDesC8& aLinkName) + { + const TInt KLinkCount = iPersistedEntity.iNumberOfLinks; + TPtrC8 name; + TTzLink* link(NULL); + TInt linkAddress = reinterpret_cast(&iPersistedEntity) + sizeof(iPersistedEntity.iNumberOfLinks); + for (TInt i = 0; i < KLinkCount; i++, linkAddress += sizeof(TTzLink)) + { + link = reinterpret_cast(linkAddress); + name.Set(iReadOnlyTzDb.GetStringL(link->iOffsetToLinkName)); + if (!name.CompareC(aLinkName)) + { // we found our link + return iReadOnlyTzDb.GetZoneL(link->iOffsetToZone); + } + } + + // if it gets here, it means that the zone has not been found + return NULL; + } + +//============================================================================= + +CTzDbStdTimeAlignmentsTable* CTzDbStdTimeAlignmentsTable::NewL(const TTzStdTimeAlignmentsTable& aStdTimeAlignmentsTable) + { + CTzDbStdTimeAlignmentsTable* self = new(ELeave) CTzDbStdTimeAlignmentsTable(aStdTimeAlignmentsTable); + return self; + } + +CTzDbStdTimeAlignmentsTable::CTzDbStdTimeAlignmentsTable(const TTzStdTimeAlignmentsTable& aStdTimeAlignmentsTable) + : iPersistedEntity(aStdTimeAlignmentsTable) + { + } + +TTzStdTimeAlignment& CTzDbStdTimeAlignmentsTable::GetTStdTimeAlignment(TUint aReference) + { + return *reinterpret_cast(aReference + (TInt)&iPersistedEntity); + } + +//============================================================================= + +CTzDbRuleSetsTable* CTzDbRuleSetsTable::NewL(CReadOnlyTzDb& aReadOnlyTzDb, const TTzRuleSetsTable& aRuleSetsTable) + { + CTzDbRuleSetsTable* self = new(ELeave) CTzDbRuleSetsTable(aReadOnlyTzDb, aRuleSetsTable); + return self; + } + +CTzDbRuleSetsTable::CTzDbRuleSetsTable(CReadOnlyTzDb& aReadOnlyTzDb, const TTzRuleSetsTable& aRuleSetsTable) + : iReadOnlyTzDb(aReadOnlyTzDb), iPersistedEntity(aRuleSetsTable) + { + } + +CTzDbRuleSet* CTzDbRuleSetsTable::GetRuleSetL(TUint aReference) + { + return CTzDbRuleSet::NewL(iReadOnlyTzDb, GetTRuleSet(aReference)); + } + +TTzRuleSet& CTzDbRuleSetsTable::GetTRuleSet(TUint aReference) + { + return * reinterpret_cast(aReference + (TInt)&iPersistedEntity); + } + +//============================================================================= + +CTzDbRuleUsesTable* CTzDbRuleUsesTable::NewL(const TTzRuleUsesTable& aRuleUsesTable) + { + CTzDbRuleUsesTable* self = new(ELeave) CTzDbRuleUsesTable(aRuleUsesTable); + return self; + } + +CTzDbRuleUsesTable::CTzDbRuleUsesTable(const TTzRuleUsesTable& aRuleUsesTable) + : iPersistedEntity(aRuleUsesTable) + { + } + +TTzRuleUse& CTzDbRuleUsesTable::GetTRuleUse(TUint aReference) + { + return * reinterpret_cast (aReference + (TInt)&iPersistedEntity); + } + +//============================================================================= + +CTzDbRuleDefinitionsTable* CTzDbRuleDefinitionsTable::NewL(const TTzRuleDefinitionsTable& aRuleDefinitionsTable) + { + CTzDbRuleDefinitionsTable* self = new(ELeave) CTzDbRuleDefinitionsTable(aRuleDefinitionsTable); + return self; + } + +CTzDbRuleDefinitionsTable::CTzDbRuleDefinitionsTable(const TTzRuleDefinitionsTable& aRuleDefinitionsTable) + : iPersistedEntity(aRuleDefinitionsTable) + { + } + +const TTzRuleDefinition& CTzDbRuleDefinitionsTable::GetRuleDefinition(TUint aReference) + { + return * reinterpret_cast(aReference + (TInt)&iPersistedEntity); + } + +//============================================================================= + +CTzDbRegion* CTzDbRegion::NewL(CReadOnlyTzDb& aReadOnlyTzDb, const TTzRegion& aRegion) + { + CTzDbRegion* self = new(ELeave) CTzDbRegion(aReadOnlyTzDb,aRegion); + return self; + } + +CTzDbRegion::CTzDbRegion(CReadOnlyTzDb& aReadOnlyTzDb, const TTzRegion& aRegion) +: iReadOnlyTzDb(aReadOnlyTzDb), + iPersistedEntity(aRegion) + { + } + +CTzDbZone* CTzDbRegion::FindZoneL(TUint aCityNameReference) + { + TTzRegionalZoneIndex* zoneIndex = const_cast(&iReadOnlyTzDb.GetTRegionalZoneIndex(iPersistedEntity.iOffsetToRegionalZoneIndex)); + const TInt KZoneCount = zoneIndex->iNumberOfZones; + TTzZone* tzone(NULL); + for (TInt i = 0; i < KZoneCount; i++) + { + tzone = const_cast(&iReadOnlyTzDb.GetTZone(zoneIndex->iOffsetsToZones[i])); + if (tzone->iOffsetToZoneName == aCityNameReference) + { // found our zone + return CTzDbZone::NewL(iReadOnlyTzDb,*tzone); + } + } + + // if it gets here, it means that the zone has not been found + return NULL; + } + +CTzDbZone* CTzDbRegion::FindZoneL(const TDesC8& aCityName) + { + TTzZone* tzone(NULL); + TTzRegionalZoneIndex* zoneIndex = const_cast(&iReadOnlyTzDb.GetTRegionalZoneIndex(iPersistedEntity.iOffsetToRegionalZoneIndex)); + const TInt KZoneCount = zoneIndex->iNumberOfZones; + TPtrC8 name; + + TBool found = EFalse; + TInt start = 0; + TInt end = KZoneCount - 1; + TInt mid; + + // perform a binary search for aCityName among the zones listed in the zone index. This assumes + // that zones are sorted alphabetically in the zone index. + while (start <= end) + { + mid = (end + start) / 2; + tzone = const_cast(&iReadOnlyTzDb.GetTZone(zoneIndex->iOffsetsToZones[mid])); + + // compare this zone's name with the name we've been given as a parameter + // use strict comparison method Compare, as CompareC may cause the search to fail: + // for example, Stanley will come BEFORE St_Helens using the standard collation method in Symbian OS, + // but it will have been placed AFTER St_Helens by std::sort in he TZ Compiler. This would result + // in this binary search to fail. + name.Set(iReadOnlyTzDb.GetStringL(tzone->iOffsetToZoneName)); + if (aCityName.Compare(name) < 0) + { + end = mid - 1; + } + else if (aCityName.Compare(name) == 0) + { // found our zone + found = ETrue; + break; + } + else if (aCityName.Compare(name) > 0) + { + start = mid + 1; + } + } + + if (found) + { + return CTzDbZone::NewL(iReadOnlyTzDb,*tzone); + } + else + { + return NULL; + } + } + +//============================================================================= + +CTzDbZone* CTzDbZone::NewL(CReadOnlyTzDb& aReadOnlyTzDb, const TTzZone& aZone) + { + CTzDbZone* self = new(ELeave) CTzDbZone(aReadOnlyTzDb,aZone); + return self; + } + +CTzDbZone::CTzDbZone(CReadOnlyTzDb& aReadOnlyTzDb, const TTzZone& aZone) +: iReadOnlyTzDb(aReadOnlyTzDb), + iPersistedEntity(aZone) + { + } + +void CTzDbZone::GetRulesL(CTzRules& aRules) + { + // start and end years of interest + TInt firstYearOfInterest = aRules.StartYear(); + TInt lastYearOfInterest = aRules.EndYear(); + + // record last instant of end year, in order to see if we need to get another time alignment + TDateTime endDateTimeOfInterest(lastYearOfInterest,EDecember,30,23,59,59,0); + TTime endTimeOfInterest(endDateTimeOfInterest); + + // initialise start time to beginning of startYear (UTC) + TDateTime startDateTime(firstYearOfInterest,EJanuary,0,0,0,0,0); + TTime startTime(startDateTime); + TTime staEndTime; + TDateTime staEndDateTime; + TTzTimeReference staTimeReference(ETzUtcTimeReference); + + // the following variables are used to iterate through the list of standard time alignments for this location + TInt taCount = iPersistedEntity.iNumberOfStdTimeAlignments; + TInt tmpOffset; + TTzStdTimeAlignment* ta(NULL); + TTzStdTimeAlignment* prevTa(NULL); + CTzDbStdTimeAlignment* sta = NULL; + + // the standard time offset at the beginning of the period of interest has to be set in aTzRules + TBool initialOffsetHasBeenSet = EFalse; + + // Iterate through std time alignments until we find the one that contains our date of interest. + // Get the rules for this time alignment, up to the end of the time alignment or the end of our + // period of interest (whichever comes first). + // If the period of interest is covered by only one time alignment, finish and return. + // If the period of interest is covered by more than one time alignment, repeat procedure with + // next time alignment(s) until we reach the end of the period of interest + for (TInt i = 0; (i < taCount) && (startTime < endTimeOfInterest ); i++) + { + // get a standard time alignment + + prevTa = ta; // this will be needed to eventually wrap the time alignment in a CTzDbStdTimeAlignment object + tmpOffset = iPersistedEntity.iOffsetsToTimeAlignments[i]; + ta = const_cast(&iReadOnlyTzDb.GetTStdTimeAlignment(tmpOffset)); + + // check if our time of interest is within this time alignment + if (IsTimeInStdTimeAlignment(*ta, startDateTime.Year(), + startDateTime.Month(), + startDateTime.Day(), + startDateTime.Hour(), + startDateTime.Minute(), + staTimeReference) ) + { + // wrap the time alignment in a CTzDbStdTimeAlignment object + sta = CTzDbStdTimeAlignment::NewL(iReadOnlyTzDb,*ta,prevTa); + if (sta) + { + CleanupStack::PushL(sta); // push #1 - sta + + if (initialOffsetHasBeenSet == EFalse) + { + aRules.SetInitialStdTimeOffset(ta->iUtcOffset); + initialOffsetHasBeenSet = ETrue; + } + + // staStartDateTime, staEndTime are passed by reference and their values + // are updated in GetRulesL + sta->GetRulesL(aRules,startDateTime,staEndDateTime); + + // The time reference to calculate the start of the next time alignment is the time reference + // in which the end time of this time alignment is expressed + staTimeReference = static_cast(sta->UntilTimeReference()); + CleanupStack::PopAndDestroy(sta); // pop #1 - sta + + staEndTime = staEndDateTime; + if (staEndTime == Time::MaxTTime()) + { + startTime = staEndTime; // this will cause loop to finish + } + else + { + // move just after end of time alignment + startTime = staEndTime + static_cast(1); + startDateTime = startTime.DateTime(); + } + } + } // if IsTimeInStdTimeAlignment(...) + } // for + + } + +TBool CTzDbZone::IsTimeInStdTimeAlignment(const TTzStdTimeAlignment& aStdTa, TInt aYear, TInt aMonth, TInt aDay, TInt aHour, TInt aMinute, TTzTimeReference aTimeRef) + { + TInt found = EFalse; + + if (aYear > (TInt)KMaxTUint16) + { + aYear = KMaxTUint16; // KMaxTUint16 (0xFFFF) is the value given to year when the end of a time alignment is unknown + } + + if (aStdTa.iUntilYear > aYear) + { + found = ETrue; + } + else if (aStdTa.iUntilYear == aYear) + { + if ( (aStdTa.iUntilMonth > aMonth) || (aStdTa.iUntilMonth == KMaxTUint8) ) + { + found = ETrue; + } + else if (aStdTa.iUntilMonth == aMonth) + { + // evaluate the day and time the time alignment ends + // and see if the received time falls in or out of the t.a. + TTime ourTime; + + if (aYear >= (TInt)KMaxTUint16) + { + ourTime = Time::MaxTTime(); + } + else + { + TDateTime ourDateTime(aYear, + (TMonth)aMonth, + aDay, + aHour, + aMinute, + 0, + 0); + + ourTime = ourDateTime; + + // convert ourTime to UTC + if (aTimeRef == ETzStdTimeReference) + { + ourTime -= static_cast(aStdTa.iUtcOffset); + } + } + + TTime endTime; + if (aStdTa.iUntilYear == KMaxTUint16) + { + endTime = Time::MaxTTime(); + } + else + { + TDateTime endDateTime(aStdTa.iUntilYear, + (TMonth)aStdTa.iUntilMonth, + aStdTa.iUntilDayOfMonth, + (aStdTa.iUntilTimeInMinutes / 60), + (aStdTa.iUntilTimeInMinutes % 60), + 0, + 0); + + endTime = endDateTime; + + // convert endTime to UTC + if (aTimeRef == ETzStdTimeReference) + { + endTime -= static_cast(aStdTa.iUtcOffset); + } + } + + // and finally compare both times + if (endTime >= ourTime) + { + found = ETrue; + } + } + } // else if (aStdTa->iUntilYear == aYear) + + return found; + } +// +// Traverses the collection of Std Time Alignments for the current zone, looking for the one that +// matches the given time. +// Returns a CTzDbStdTimeAlignment object. +// +CTzDbStdTimeAlignment* CTzDbZone::FindStdTimeAlignmentL(TInt aYear, TInt aMonth, TInt aDay, TInt aHour, TInt aMinute, const TTzTimeReference aTimeRef) + { + TTzStdTimeAlignment* ta(NULL); + TTzStdTimeAlignment* prevTa(NULL); + TInt taCount = iPersistedEntity.iNumberOfStdTimeAlignments; + TBool found = EFalse; + TInt tmpOffset = 0; + for (TInt i = 0; (i < taCount) && (!found); i++) + { + prevTa = ta; + tmpOffset = iPersistedEntity.iOffsetsToTimeAlignments[i]; + ta = const_cast(&iReadOnlyTzDb.GetTStdTimeAlignment(tmpOffset)); + + found = IsTimeInStdTimeAlignment(*ta,aYear,aMonth,aDay,aHour,aMinute,aTimeRef); + } + + if (found) + { + return CTzDbStdTimeAlignment::NewL(iReadOnlyTzDb, *ta, prevTa); + } + else + { + return NULL; + } + } + +HBufC8* CTzDbZone::GetFullZoneNameLC() + { + HBufC8* fullName = HBufC8::NewL(KMaxTimeZoneIdSize); + CleanupStack::PushL(fullName); + TPtr8 fullNamePtr(fullName->Des() ); + fullNamePtr.Append(iReadOnlyTzDb.GetStringL(iPersistedEntity.iOffsetToRegionName)); + if (fullNamePtr.Length() > 0) + { + // if the region name is empty (e.g. zone "CET"), don't add the slash + fullNamePtr.Append('/'); + } + fullNamePtr.Append(iReadOnlyTzDb.GetStringL(iPersistedEntity.iOffsetToZoneName)); + + return fullName; + } + +//======================================================================== + +CTzDbStdTimeAlignment* CTzDbStdTimeAlignment::NewL(CReadOnlyTzDb& aReadOnlyTzDb, const TTzStdTimeAlignment& aTimeAlignment, TTzStdTimeAlignment* aPrevTimeAlignment) + { + CTzDbStdTimeAlignment* self = new(ELeave) CTzDbStdTimeAlignment(aReadOnlyTzDb,aTimeAlignment,aPrevTimeAlignment); + return self; + } + +CTzDbStdTimeAlignment::CTzDbStdTimeAlignment(CReadOnlyTzDb& aReadOnlyTzDb, + const TTzStdTimeAlignment& aTimeAlignment, TTzStdTimeAlignment* aPrevTimeAlignment) + : iReadOnlyTzDb(aReadOnlyTzDb) + { + iPersistedEntity = aTimeAlignment; + iPrevTimeAlignment = aPrevTimeAlignment; + } + +CTzDbStdTimeAlignment::~CTzDbStdTimeAlignment() + { + } + +void CTzDbStdTimeAlignment::GetRulesL(CTzRules& aRules, const TDateTime& aStartDateTime, TDateTime& aEndDateTime) + { + // get the ruleSet for this time alignment + CTzDbRuleSet* ruleSet = iReadOnlyTzDb.GetRuleSetL(iPersistedEntity.iOffsetToRuleSet); + if (ruleSet) + { + CleanupStack::PushL(ruleSet); // PUSH #1 - RULESET + + // get end time of time alignment + TTime endTime; + CalculateEndTime(endTime); + if (endTime == Time::MaxTTime()) + { + aEndDateTime.SetYear(KMaxTUint16); + } + else + { + aEndDateTime = endTime.DateTime(); + } + + TInt firstYearOfInterest = (aRules.StartYear() >= aStartDateTime.Year()) ? aRules.StartYear() : aStartDateTime.Year(); + TInt lastYearOfInterest = (aRules.EndYear() <= aEndDateTime.Year()) ? aRules.EndYear() : aEndDateTime.Year(); + CTzRules* newRules = CTzRules::NewL(firstYearOfInterest, lastYearOfInterest); + CleanupStack::PushL(newRules); // PUSH #2 - NEWRULES + + ruleSet->GetRulesL(*newRules,iPersistedEntity.iUtcOffset,aStartDateTime,aEndDateTime); + + // the new rules obtained for the current std time alignment must be merged into + // the global rule collection + AddRulesToCollectionL(aRules,*newRules); + + CleanupStack::PopAndDestroy(2, ruleSet); // POP #2,#1 - NEWRULES, RULESET + } + } + +void CTzDbStdTimeAlignment::AddRulesToCollectionL(CTzRules& aRuleCollection, CTzRules& aNewRules) + { + TTzRule* tRule(NULL); + TVTzActualisedRule tActRule; + TInt count = aNewRules.Count(); + for (TInt i = 0; i < count; i++) + { + TBool ruleAdded = EFalse; + tRule = &(aNewRules[i]); + TInt basicRuleFromYear = tRule->iFrom.iTime.DateTime().Year(); + TInt basicRuleToYear = tRule->iTo.iTime.DateTime().Year(); + + if ( (iPrevTimeAlignment != NULL) + && (basicRuleFromYear <= iPrevTimeAlignment->iUntilYear) + && (basicRuleToYear >= iPrevTimeAlignment->iUntilYear) ) + { // the rule is in effect during the first year of the time alignment + // actualise rule for this year and remove it if it falls outside the time alignment + tActRule = tRule->Actualise(iPrevTimeAlignment->iUntilYear); + + // work out start time of time alignment (i.e. end time of the prev. alignment) + + TDateTime staStartDateTime( iPrevTimeAlignment->iUntilYear, + (TMonth)iPrevTimeAlignment->iUntilMonth, + iPrevTimeAlignment->iUntilDayOfMonth, + iPrevTimeAlignment->iUntilTimeInMinutes/60, // hour + iPrevTimeAlignment->iUntilTimeInMinutes%60, // minute + 0,0); // second, ms + + // make sure times are expressed in the same reference (UTC or Local) + if (tActRule.iTimeReference != iPrevTimeAlignment->iUntilTimeReference) + { + if (iPrevTimeAlignment->iUntilTimeReference == ETzWallTimeReference) + { // convert sta start time to UTC + TTime staTime(staStartDateTime); + staTime -= (TTimeIntervalMinutes)(iPrevTimeAlignment->iUtcOffset); + staStartDateTime = staTime.DateTime(); + } + else if (iPrevTimeAlignment->iUntilTimeReference == ETzUtcTimeReference) + { // convert time of change to UTC + tActRule.iTimeOfChange -= (TTimeIntervalMinutes)tRule->iOldLocalTimeOffset; + } + } + + // add rule to collection only if its time of change falls within the time alignment + if (tActRule.iTimeOfChange >= staStartDateTime) + { + aRuleCollection.AddRuleL(*tRule); + ruleAdded = ETrue; + } + else + { + TDateTime tActRuleDt = tActRule.iTimeOfChange.DateTime(); + /* + Fix for INC117764: + Update the rule's month and daytime information only if the rule starts and ends in the same year + otherwise just add them to the collection. + For example: + Iqaluit rules: + -6:00 Canada C%sT 2000 Oct 29 2:00 + -5:00 Canada E%sT + + Rule Canada 1974 2006 - Oct lastSun 2:00 0 S + Rule Canada 1987 2006 - Apr Sun>=1 2:00 1:00 D + Rule Canada 2007 max - Mar Sun>=8 2:00 1:00 D + Rule Canada 2007 max - Nov Sun>=1 2:00 0 S + Before this fix, In such a case for all the rules between 2000 and 2006 would be overwritten with start month as 10 whereas they + should be alternating between October and April till 2006. + */ + if ((tActRuleDt.Year() == staStartDateTime.Year()) && (basicRuleFromYear == basicRuleToYear) ) + { + tRule->iMonth = staStartDateTime.Month(); + tRule->iDayOfMonth = staStartDateTime.Day(); + tRule->iDayRule = ETzFixedDate; + aRuleCollection.AddRuleL(*tRule); + ruleAdded = ETrue; + } + else if (tActRuleDt.Year() == staStartDateTime.Year()) + { + aRuleCollection.AddRuleL(*tRule); + ruleAdded = ETrue; + } + } + } + else if ( + (tRule->iFrom.iTime.DateTime().Year() <= iPersistedEntity.iUntilYear) + &&(tRule->iTo.iTime.DateTime().Year() >= iPersistedEntity.iUntilYear) + ) + { + // the rule is in effect during the last year of the time alignment + tActRule = tRule->Actualise(iPersistedEntity.iUntilYear); + + TDateTime staEndDateTime(iPersistedEntity.iUntilYear, (TMonth)iPersistedEntity.iUntilMonth, + iPersistedEntity.iUntilDayOfMonth, + iPersistedEntity.iUntilTimeInMinutes / 60, // hour + iPersistedEntity.iUntilTimeInMinutes % 60, // minute + 0,0); // second, ms + + if (tActRule.iTimeReference != iPersistedEntity.iUntilTimeReference) + { + if (iPersistedEntity.iUntilTimeReference == ETzWallTimeReference) + { + TTime staTime(staEndDateTime); + staTime -= (TTimeIntervalMinutes)iPersistedEntity.iUtcOffset; + staEndDateTime = staTime.DateTime(); + } + else if (iPersistedEntity.iUntilTimeReference == ETzUtcTimeReference) + { + tActRule.iTimeOfChange -= (TTimeIntervalMinutes)tRule->iOldLocalTimeOffset; + } + } + + // add rule to collection only if its time of change falls withing the time alignment + if (tActRule.iTimeOfChange < staEndDateTime) + { + aRuleCollection.AddRuleL(*tRule); + ruleAdded = ETrue; + } + } + else + { + // no doubt that the rule falls within the time alignment + aRuleCollection.AddRuleL(*tRule); + ruleAdded = ETrue; + } + /* + Fix for INC117764: + Updating the default rule for a year if any new rule overlaps with the default rule.(Default rules start and end at same time) + The offsets are updated if default rule and newly added rule start during same time and newly added rule month occurs before + the default rule month. + */ + if( ruleAdded ) + { + if(basicRuleFromYear != basicRuleToYear) + { + TInt ruleCount = aRuleCollection.Count() - 2; + for(TInt loop = ruleCount;loop >= 0; --loop) + { + TTzRule prevAddedRule = aRuleCollection[loop]; + //check that we do not update a winter rule using a summer rule and vice versa + //Summer rules start before July and winter rules after + if(tRule->iMonth < EJuly && prevAddedRule.iMonth > EJuly) + { + continue; + } + if(tRule->iMonth > EJuly && prevAddedRule.iMonth < EJuly) + { + continue; + } + if(basicRuleFromYear > prevAddedRule.iFrom.iTime.DateTime().Year()) + { + //no need to check beyond the newly added rules start year. + break; + } + if((tRule->iFrom == prevAddedRule.iFrom) && + (prevAddedRule.iFrom == prevAddedRule.iTo) && + (tRule->iMonth < prevAddedRule.iMonth)) + { + prevAddedRule.iOldLocalTimeOffset = tRule->iOldLocalTimeOffset; + prevAddedRule.iNewLocalTimeOffset = tRule->iNewLocalTimeOffset; + aRuleCollection.RemoveRule(loop); + aRuleCollection.AddRuleL(prevAddedRule); + } + } + } + } + } // for + } + +TInt CTzDbStdTimeAlignment::UtcOffset() + { + return iPersistedEntity.iUtcOffset; + } + +TUint CTzDbStdTimeAlignment::RuleSetReference() + { + return iPersistedEntity.iOffsetToRuleSet; + } + +TInt CTzDbStdTimeAlignment::UntilYear() + { + return iPersistedEntity.iUntilYear; + } + +TInt CTzDbStdTimeAlignment::UntilMonth() + { + return iPersistedEntity.iUntilMonth; + } + +TInt CTzDbStdTimeAlignment::UntilDay() + { + return iPersistedEntity.iUntilDayOfMonth; + } + +TInt CTzDbStdTimeAlignment::UntilTimeInMinutes() + { + return iPersistedEntity.iUntilTimeInMinutes; + } + +TInt CTzDbStdTimeAlignment::UntilTimeReference() + { + return iPersistedEntity.iUntilTimeReference; + } + +// +// Calculate End Time of TimeAlignment (UTC) +// +void CTzDbStdTimeAlignment::CalculateEndTime(TTime& aEndTime) + { + TInt endYear; + TInt endMonth; + TInt endDay; + TInt endHour; + TInt endMinute; + TInt endSecond = 0; + TInt endMicrosecond = 0; + + if (iPersistedEntity.iUntilYear == KMaxTUint16) + { + aEndTime = Time::MaxTTime(); + } + else + { + + endYear = iPersistedEntity.iUntilYear; + endMonth = iPersistedEntity.iUntilMonth; if (endMonth > EDecember) endMonth = EJanuary; + endDay = iPersistedEntity.iUntilDayOfMonth; if (endDay > 31) endDay = 0; + if (iPersistedEntity.iUntilTimeInMinutes == KMaxTUint16) + { + endHour = endMinute = 0; + } + else + { + endHour = iPersistedEntity.iUntilTimeInMinutes / 60; + endMinute = iPersistedEntity.iUntilTimeInMinutes % 60; + } + + TDateTime endDateTime(endYear,(TMonth)endMonth,endDay,endHour,endMinute,endSecond,endMicrosecond); + aEndTime = endDateTime; + + // convert time to UTC + TTzTimeReference taTimeReference(static_cast(iPersistedEntity.iUntilTimeReference) ); + if (taTimeReference == ETzStdTimeReference) + { + aEndTime -= static_cast(iPersistedEntity.iUtcOffset); + } + + // aEndTime is, at this point, the beginning of the the next Std Time Alignment + aEndTime -= static_cast(1); + } + } + +// +// Calculate Start Time of TimeAlignment (UTC) +// +void CTzDbStdTimeAlignment::CalculateStartTime(TTime& aStartTime) + { + TInt startYear; + TInt startMonth; + TInt startDay; + TInt startHour; + TInt startMinute; + TInt startSecond = 0; + TInt startMicrosecond = 0; + + if (iPrevTimeAlignment == NULL) + { + aStartTime = 0; + } + else + { + startYear = iPrevTimeAlignment->iUntilYear; + startMonth = iPrevTimeAlignment->iUntilMonth; if (startMonth == (TInt)KMaxTUint8) startMonth = EJanuary; + startDay = iPrevTimeAlignment->iUntilDayOfMonth; if (startDay == (TInt)KMaxTUint8) startDay = 0; + if (iPrevTimeAlignment->iUntilTimeInMinutes == KMaxTUint16) + { + startHour = startMinute = 0; + } + else + { + startHour = iPrevTimeAlignment->iUntilTimeInMinutes / 60; + startMinute = iPrevTimeAlignment->iUntilTimeInMinutes % 60; + } + + TDateTime startDateTime(startYear,(TMonth)startMonth,startDay,startHour,startMinute,startSecond,startMicrosecond); + aStartTime = startDateTime; + // convert time to UTC + TTzTimeReference taTimeReference(static_cast(iPersistedEntity.iUntilTimeReference) ); + if (taTimeReference == ETzStdTimeReference) + { + aStartTime -= static_cast(iPersistedEntity.iUtcOffset); + } + } + } + +//======================================================================== + +CTzDbRuleSet* CTzDbRuleSet::NewL(CReadOnlyTzDb& aReadOnlyTzDb, const TTzRuleSet& aRuleSet) + { + CTzDbRuleSet* self = new(ELeave) CTzDbRuleSet(aReadOnlyTzDb,aRuleSet); + return self; + } + + +CTzDbRuleSet::CTzDbRuleSet(CReadOnlyTzDb& aReadOnlyTzDb, const TTzRuleSet& aRuleSet) + : iReadOnlyTzDb(aReadOnlyTzDb), + iPersistedEntity(aRuleSet) + { + } + +// +// Gets the encoded rules for the current standard time alignment, between aStartDateTime and aEndDateTime +// +void CTzDbRuleSet::GetRulesL(CTzRules& aTzRules, TInt aUtcOffset, const TDateTime& aStartDateTime, const TDateTime& aEndDateTime) const + { + TInt startYear = aStartDateTime.Year(); + // the last year we are interested in is the earliest of the following: + // 1) the last year of the CTzRules + // 2) the year of aEndDateTime (the end of the invoking std time alignment) + + TInt endYear = (aEndDateTime.Year() < aTzRules.EndYear()) + ? aEndDateTime.Year() : aTzRules.EndYear(); + const TDateTime rulesEndDateTime(aTzRules.EndYear(), EDecember, 30, 0,0,0,0); + const TDateTime& endDateTime = (aEndDateTime.Year() <= aTzRules.EndYear()) + ? aEndDateTime : rulesEndDateTime; + + RArray ruleDefs; + CleanupClosePushL(ruleDefs); // PUSH #1 + RArray ruleUses; + CleanupClosePushL(ruleUses); // PUSH #2 + + FetchRuleDefinitionsL(ruleDefs,ruleUses,startYear,endYear); + + // fetch rules for previous year (these will be needed to work out the "Old Offset" field of + // the first rule in aStartYear + TInt initialLocalTimeOffset = GetLocalTimeOffsetAtEndOfYearL(startYear-1,aUtcOffset); + + // convert rule definitions (together with rule uses) to TTzRules and add them to aTzRules + + CompleteRulesAndAddToCollectionL(aTzRules,ruleDefs,ruleUses,aUtcOffset,initialLocalTimeOffset,aStartDateTime,endDateTime); + + CleanupStack::PopAndDestroy(2,&ruleDefs); // POP #2,#1 - ruleUses, ruleDefs + } + +TInt CTzDbRuleSet::GetLocalTimeOffsetAtEndOfYearL(TInt aYear, TInt aUtcOffset) const + { + RArray ruleDefs; + CleanupClosePushL(ruleDefs); + RArray ruleUses; + CleanupClosePushL(ruleUses); + + TDateTime startDateTime(aYear,EJanuary,0,0,0,0,0); + TDateTime endDateTime(aYear,EDecember,30,23,59,59,0); + + FetchRuleDefinitionsL(ruleDefs,ruleUses,aYear,aYear); + + CVTzActualisedRules* actRules = CVTzActualisedRules::NewL(aYear,aYear); + CleanupStack::PushL(actRules); + TVTzActualisedRule tDefaultRule(startDateTime,aUtcOffset,ETzUtcTimeReference); + ActualiseRuleDefinitionsL(*actRules,ruleDefs,ruleUses,aUtcOffset,startDateTime,endDateTime,tDefaultRule); + + TInt count = actRules->Count(); + TInt finalOffset = (*actRules)[count-1].iNewOffset; + + CleanupStack::PopAndDestroy(3,&ruleDefs); + return finalOffset; + } + + +void CTzDbRuleSet::CompleteRulesAndAddToCollectionL( + CTzRules& aTzRules, + const RArray& aRuleDefs, + const RArray& aRuleUses, + TInt aUtcOffset, TInt aInitialLocalTimeOffset, + TDateTime aStart, TDateTime aEnd) const + { + TInt count = aRuleDefs.Count(); + if (count != aRuleUses.Count()) + { + User::Leave(KErrCorrupt); + } + + TTimeWithReference startOfAlignment(aStart,ETzUtcTimeReference); + TTimeWithReference endOfAlignment(aEnd,ETzUtcTimeReference); + + //Keep track of changes to UTC offset + // even if there are no DST rules. + TTzRule trule( + startOfAlignment, startOfAlignment, + (TUint16)aInitialLocalTimeOffset, (TUint16)aInitialLocalTimeOffset, EJanuary, ETzFixedDate, + 0, 0, + ETzUtcTimeReference, + 0); + aTzRules.AddRuleL(trule); + //Calculate the new and old offsets for each rule. + TInt prevOffset = 0; + TInt maxOffset = 0; + for (TInt i = 0; i < count; i++) + { + TInt oldOffset = aInitialLocalTimeOffset; + TInt dstOffset = aRuleDefs[i]->iStdTimeOffset; + TInt newOffset = aUtcOffset + dstOffset; + //newoffset and oldoffset should always have a difference of dstoffset. + if (newOffset <= oldOffset ) + { + //there are cases when dstoffset in the set of rules is same for few continuous rules + //in such cases, newoffset and oldoffset would have a difference of maxoffset. + if(prevOffset == dstOffset) + { + oldOffset = aUtcOffset + maxOffset; + } + else + { + oldOffset = aUtcOffset + prevOffset; + } + } + else + //newoffset is more than oldoffset so maintain the difference with dstoffset. + { + oldOffset = newOffset - dstOffset; + } + prevOffset = dstOffset; + //store maximum offset, to handle cases in which dstoffset in the set of rules + //is same for few continuous rules + if(dstOffset > maxOffset) + { + maxOffset = dstOffset; + } + + TUint16 timeOfChange = aRuleDefs[i]->iTimeOfChange; + TTzTimeReference timeReference = + static_cast(aRuleDefs[i]->iTimeReference); + + // + + // Find the intersection of the date ranges: + // use latest start year, earliest end year + TDateTime start = aStart; + TDateTime end = aEnd; + if(aRuleUses[i]->iFromYear > start.Year()) + { + start = TDateTime(aRuleUses[i]->iFromYear,EJanuary,0,0,0,0,0); + } + + if(aEnd.Year() > aRuleUses[i]->iUntilYear) + { + end = TDateTime(aRuleUses[i]->iUntilYear,EDecember,30,23,59,59,0); + } + + TTimeWithReference startOfRule(start,ETzUtcTimeReference); + TTimeWithReference endOfRule(end,ETzUtcTimeReference); + if(endOfRule.iTime >= startOfRule.iTime) + { + TTzRule trule( + startOfRule, endOfRule, + (TUint16)oldOffset, (TUint16)newOffset, (TMonth)aRuleDefs[i]->iMonth, (TTzRuleDay)aRuleDefs[i]->iDayRule, + aRuleDefs[i]->iDayOfMonth, aRuleDefs[i]->iDayOfWeek, + timeReference, timeOfChange); + + aTzRules.AddRuleL(trule); + + } + } + } + +void CTzDbRuleSet::FetchRuleDefinitionsL(RArray& aTzRuleDefinitions, RArray& aTzRuleUses, TInt aStartYear, TInt aEndYear) const + { + TInt ruleUseCount = iPersistedEntity.iNumberOfRuleUses; + TTzRuleUse* truleUse(NULL); + TTzRuleDefinition* truleDef(NULL); + for (TInt i = 0; i < ruleUseCount; i++) + { + truleUse = const_cast(&iReadOnlyTzDb.GetTRuleUse(iPersistedEntity.iOffsetsToRuleUses[i])); + + // intersect rule use with (startYear,endYear) period + if ( (truleUse->iFromYear <= aEndYear) && (truleUse->iUntilYear >= aStartYear) ) + { + // Found one rule use valid during the years of interest - fetch rule definition. + // For every year, check if it occurs within (aStartTime,aEndTime) + truleDef = const_cast(&iReadOnlyTzDb.GetTRuleDefinition(truleUse->iOffsetToRuleDefinition)); + // check that the rule definition has valid data. leave if the data is not valid, as it would mean that the database is corrupt + User::LeaveIfError(CTzHelpers::Validate((const TTzRuleDefinition&)*truleDef)); + + // Add rule definition ptr to array + aTzRuleDefinitions.Append(truleDef); + // Add rule use ptr to array + aTzRuleUses.Append(truleUse); + } + } + } + +void CTzDbRuleSet::ActualiseRuleDefinitionsL(CVTzActualisedRules& aActualisedRules, const RArray& aTzRuleDefinitions, const RArray& aTzRuleUses, TInt aUtcOffset, const TDateTime& aStartDateTime, const TDateTime& aEndDateTime, const TVTzActualisedRule& aDefaultRule) const + { + TInt startYear = aStartDateTime.Year(); + TInt endYear = (aActualisedRules.EndYear() < (TUint)aEndDateTime.Year()) ? aActualisedRules.EndYear() : aEndDateTime.Year(); + TInt rulesAddedSoFar = 0; + TInt yearOfFirstRule = endYear; + TInt count = aTzRuleDefinitions.Count(); + + TInt oldOffset = 0; // Arbitrarily set to zero. Required to create TTzRule. + + for (TInt i = 0; i < count; i++) + { + for (TInt year = (startYear > aTzRuleUses[i]->iFromYear) ? startYear : aTzRuleUses[i]->iFromYear ; (year <= endYear) && (year <= aTzRuleUses[i]->iUntilYear); year++) + { + TTzRule trule( + static_cast(startYear), static_cast(endYear), + static_cast(oldOffset), static_cast(aUtcOffset + aTzRuleDefinitions[i]->iStdTimeOffset),static_cast(aTzRuleDefinitions[i]->iMonth), static_cast(aTzRuleDefinitions[i]->iDayRule), + static_cast(aTzRuleDefinitions[i]->iDayOfMonth), static_cast(aTzRuleDefinitions[i]->iDayOfWeek), + static_cast(aTzRuleDefinitions[i]->iTimeReference), static_cast(aTzRuleDefinitions[i]->iTimeOfChange)); + + TVTzActualisedRule tActRule = trule.Actualise(year); + if ( (tActRule.iTimeOfChange < aEndDateTime) && (tActRule.iTimeOfChange >= aStartDateTime) ) + { + aActualisedRules.AddRuleL(tActRule); + // record the year of the first rule added + if (rulesAddedSoFar == 0) + { + yearOfFirstRule = year; + } + rulesAddedSoFar++; + } + } + } + + // In some cases we need to add a "default rule" to aRules. ("Default rule" + // means a rule with zero as DST offset and the start-time of the time-alignment + // as time-of-change). This default rule will be added if no rule exists for the + // first year of the time alignment. + if ( (rulesAddedSoFar == 0) || (yearOfFirstRule > startYear) ) + { + aActualisedRules.AddRuleL(aDefaultRule); + } + } + +//============================================================================================ + +TInt CTzHelpers::Validate(const TTzRuleDefinition& aRuleDefinition) + { + if (aRuleDefinition.iStdTimeOffset > KMaxDstOffset) + { + return KErrCorrupt; + } + + if (aRuleDefinition.iMonth > EDecember) + { + return KErrCorrupt; + } + + if (aRuleDefinition.iDayRule > KValidatedLastDayRule) // last element of enum TTzRuleDay + { + return KErrCorrupt; + } + + if (aRuleDefinition.iDayOfMonth > KMaxDayOfMonth) + { + return KErrCorrupt; + } + + if (aRuleDefinition.iDayOfWeek > ESunday) + { + return KErrCorrupt; + } + + if (aRuleDefinition.iTimeReference > KValidatedLastTimeReference) // last element of enum TTzTimeReference + { + return KErrCorrupt; + } + + if (aRuleDefinition.iTimeOfChange >= KMinutesInOneDay) + { + return KErrCorrupt; + } + + return KErrNone; + }