kernel/eka/euser/us_parse.cpp
changeset 0 a41df078684a
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/kernel/eka/euser/us_parse.cpp	Mon Oct 19 15:55:17 2009 +0100
@@ -0,0 +1,620 @@
+// Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
+// All rights reserved.
+// This component and the accompanying materials are made available
+// under the terms of the License "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:
+// e32\euser\us_parse.cpp
+// 
+//
+
+#include "us_std.h"
+
+class TStringToDateTime
+	{
+public:
+	TStringToDateTime(const TDesC& aDes,TInt aCenturyOffset);
+	TInt Parse(TTime& aTime);
+	enum {ETimePresent=1,EDatePresent};
+private:
+// tokens
+	enum {EDec=-12,ENov,EOct,ESep,EAug,EJul,EJun,EMay,EApr,EMar,EFeb,EJan};
+	enum {ETokenAm=-19,ETokenPm};
+	enum TDateSeparators{ESlash=-39,EDash,EComma,ESpace,EDateLocale1,EDateLocale2};
+	enum TTimeSeparators{EColon=-49,EDot,ETimeLocale1,ETimeLocale2};
+	enum TDecimalSeparators{EDecimalLocale=-59};
+	enum {EErrorToken=-99,ENullToken=-100};
+//
+	enum {ENumberOfDateSep=6,ENumberOfTimeSep=4,EMaxTokens=27};
+	enum {EFirstDateSep=ESlash,ELastDateSep=EDateLocale2,EFirstTimeSep=EColon,ELastTimeSep=ETimeLocale2};
+private:
+	TInt NextToken(TInt& aTokenLen);
+	void StripSpaceTokens();
+	TInt CrackTokenFormula();
+	TInt GetDate(TInt aFormulaPos,TInt& aTokenCount);
+	TInt GetTime(TInt aFormulaPos,TInt& aTokenCount);
+//	
+	TInt GetSeparatorToken(TChar aChar) const;	
+	TBool IsTimeSeparator(TChar  aChar) const;
+	TBool IsDateSeparator(TChar  aChar) const;
+	TBool IsDecimalSeparator(TChar  aChar) const;
+	TBool IsSeparator(TChar aChar) const;
+	TBool IsSeparator(TInt aToken) const;
+	inline TBool IsTimeSeparator(TInt aToken) const;
+	inline TBool IsDateSeparator(TInt aToken) const;
+	TBool IsDecimalSeparator(TInt aToken) const;
+	inline TBool IsAmPm(TInt aToken) const;
+	inline TBool IsAlphaMonth(TInt aToken) const;
+private:
+	TLex iLex;
+	TInt iCenturyOffset;
+	TDateFormat iDateFormat;
+	TChar iTimeSepChars[ENumberOfTimeSep];
+	TChar iDateSepChars[ENumberOfDateSep];
+	TChar iDecSepChar;
+	TDateTime iDateTime;
+	TInt iCount;
+	TInt iFormula[EMaxTokens];// 27 max possible with valid des (including spaces):" 10 : 00 : 00 . 000000 pm 6 / 12 / 99 "
+	TUint8 iTokenLen[EMaxTokens];
+	};
+
+inline TBool TStringToDateTime::IsTimeSeparator(TInt aToken) const
+	{
+	return(aToken >= EFirstTimeSep && aToken <=ELastTimeSep);
+	}
+inline TBool TStringToDateTime::IsDateSeparator(TInt aToken) const
+	{
+	return(aToken>=EFirstDateSep && aToken<=ELastDateSep);
+	}
+inline TBool TStringToDateTime::IsAmPm(TInt aToken) const
+	{
+	return(aToken==ETokenAm || aToken==ETokenPm);
+	}
+inline TBool TStringToDateTime::IsAlphaMonth(TInt aToken) const
+	{
+	return (aToken>=EDec && aToken<=EJan);
+	}
+
+inline TBool TStringToDateTime::IsDecimalSeparator(TChar aChar) const
+	{
+	return(aChar==iDecSepChar);
+	}
+inline TBool TStringToDateTime::IsDecimalSeparator(TInt aToken) const
+	{
+	return(aToken==EDecimalLocale);
+	}
+
+TStringToDateTime::TStringToDateTime(const TDesC& aDes,TInt aCenturyOffset)
+	: iLex(aDes),iCenturyOffset(aCenturyOffset),iDateTime(0,EJanuary,0,0,0,0,0)
+	{
+
+	__ASSERT_ALWAYS(aCenturyOffset>=0 && aCenturyOffset<100,Panic(ETTimeValueOutOfRange));
+	TLocale locale;
+	iDateFormat=locale.DateFormat();
+	
+	iTimeSepChars[0]=':';
+	iTimeSepChars[1]='.';
+	iTimeSepChars[2]=locale.TimeSeparator(1);
+	iTimeSepChars[3]=locale.TimeSeparator(2);
+
+	iDateSepChars[0]='/';
+	iDateSepChars[1]='-';
+	iDateSepChars[2]=',';
+	iDateSepChars[3]=' ';
+	iDateSepChars[4]=locale.DateSeparator(1);
+	iDateSepChars[5]=locale.DateSeparator(2);
+	iDecSepChar = locale.DecimalSeparator();
+	}
+
+TBool TStringToDateTime::IsTimeSeparator(TChar aChar) const
+	{
+
+	for (TInt ii=0;ii<ENumberOfTimeSep;++ii)
+		if (aChar==iTimeSepChars[ii])
+			return ETrue;
+	return(EFalse);
+	}
+
+TBool TStringToDateTime::IsDateSeparator(TChar aChar) const
+	{
+
+	for (TInt ii=0;ii<ENumberOfDateSep;++ii)
+		if (aChar==iDateSepChars[ii])
+			return ETrue;
+	return(EFalse);
+	}
+
+TBool TStringToDateTime::IsSeparator(TChar  aChar) const
+	{
+
+	return(IsTimeSeparator(aChar) || IsDateSeparator(aChar) || IsDecimalSeparator(aChar));
+	}
+
+TBool TStringToDateTime::IsSeparator(TInt aToken) const
+	{
+
+	return(IsTimeSeparator(aToken) || IsDateSeparator(aToken));
+	}
+
+TInt TStringToDateTime::GetSeparatorToken(TChar aChar) const
+	{
+
+	TInt ii=0;
+	for (ii=0;ii<ENumberOfDateSep;++ii)
+		if (aChar == iDateSepChars[ii])
+			return(EFirstDateSep+ii);
+	for (ii=0;ii<ENumberOfTimeSep;++ii)
+		if (aChar == iTimeSepChars[ii])
+			return(EFirstTimeSep+ii);
+	if (aChar == iDecSepChar)
+		return(EDecimalLocale);
+	return(ENullToken);
+	}
+
+void TStringToDateTime::StripSpaceTokens()
+// Removes excess space tokens from the formula
+// The end of the formula is marked with a Null token
+	{
+
+	TInt t = 0;
+	for (TInt s = 0 ; s < iCount ; ++s)
+		{
+		if (iFormula[s]==ESpace &&
+			(IsSeparator(iFormula[s-1]) || s == iCount-1 || IsSeparator(iFormula[s+1]) || IsAmPm(iFormula[s+1])))
+			continue;// Skip unwanted space token
+		iFormula[t]=iFormula[s];
+		iTokenLen[t]=iTokenLen[s];
+		++t;
+		}
+	iCount=t;
+	iFormula[t]=ENullToken;
+	}
+
+TInt TStringToDateTime::CrackTokenFormula()
+	{
+
+	if (iCount==0)
+		return KErrArgument;// Nothing to read
+	TInt token0=iFormula[0];
+	TInt token1=iFormula[1];
+	TInt numberOfTokens;
+	TInt dummy=0;
+	TInt error;
+	if (IsDateSeparator(token1) || IsAlphaMonth(token0))
+		{// Assume formula is a Date or DateTime
+		if ((error=GetDate(0,numberOfTokens))!=EDatePresent)
+			return error;
+		numberOfTokens+=1;// Space char between the Date & Time
+		return(GetTime(numberOfTokens,dummy));
+		}
+	else if (IsTimeSeparator(token1) || IsAmPm(token1))
+		{// Assume formula is a Time or TimeDate
+		if ((error=GetTime(0,numberOfTokens))!=ETimePresent)
+			return error;
+		numberOfTokens+=1;// Space char between the Time & Date
+		return(GetDate(numberOfTokens,dummy));
+		}
+	else
+		return(KErrArgument);
+	}
+
+TInt TStringToDateTime::GetDate(TInt aOffset,TInt& aTokenCount)
+	// if aOffset == 0  then Date or DateTime format
+	// if aOffset != 0  then TimeDate format
+	{
+
+	TInt relativeCount=iCount;
+	if (aOffset!=0)// aFormat==ETimeDate
+		{
+		relativeCount-=aOffset;
+		if (relativeCount<=-1)
+			return(ETimePresent);
+		}
+	TInt numberOfDateFields=0;
+	if (relativeCount==3)
+		numberOfDateFields=2;
+	else if (relativeCount==5)
+		numberOfDateFields=3;
+	else if (aOffset==0)
+		{// DateTime
+		if (IsTimeSeparator(iFormula[5]) || IsAmPm(iFormula[5]))
+			numberOfDateFields=2;
+		else
+			numberOfDateFields=3;
+		}
+	else// (aOffset!=0)
+		{// Date
+		if (relativeCount==3)
+			numberOfDateFields=2;
+		else if (relativeCount==5)
+			numberOfDateFields=3;
+		else
+			return(KErrArgument);
+		}
+
+	if (!IsDateSeparator(iFormula[1+aOffset])) 
+		return(KErrArgument);
+	if (numberOfDateFields==2)
+		{
+		if (aOffset!=0 && relativeCount!=3)// ie TimeDate
+			return(KErrArgument);
+		}
+	if (numberOfDateFields==3)
+		{
+		if (aOffset!=0 && relativeCount!=5)// ie TimeDate
+			return(KErrArgument);
+		if (!IsDateSeparator(iFormula[3+aOffset]))
+			return(KErrArgument);
+		}
+
+	// A month will always be in the first two fields // DMY MDY YMD
+	TBool alphaMonth=(IsAlphaMonth(iFormula[0+aOffset]) || IsAlphaMonth(iFormula[2+aOffset]) );
+	
+	TInt dayIndex;
+	TInt monthIndex;
+	TInt yearIndex=4;// Reset if Japanese
+
+	if (iDateFormat==EDateJapanese)
+		{// 1996 feb 3
+		if (numberOfDateFields==2)
+			{
+			monthIndex=0;
+			dayIndex=2;
+			}
+		else
+			{
+			yearIndex=0;
+			monthIndex=2;
+			dayIndex=4;
+			}
+		}
+	else if (IsAlphaMonth(iFormula[0+aOffset])
+		|| (!alphaMonth && iDateFormat==EDateAmerican))// Amer Euro 
+		{// feb 3 1996 valid Amer or Euro format // 2 3 1996 Amer
+		monthIndex=0;
+		dayIndex=2;
+		}		
+	else
+		{// 3 feb 1996 valid Amer or Euro format // 3 2 1996 Euro
+		__ASSERT_DEBUG(
+			IsAlphaMonth(iFormula[2+aOffset]) || 
+			(!alphaMonth && iDateFormat==EDateEuropean),User::Invariant());
+		monthIndex=2;
+		dayIndex=0;
+		}
+	
+	TTime timeNow;
+	timeNow.HomeTime();
+	TDateTime now=timeNow.DateTime();
+	TInt currentCentury=((now.Year()/100)*100);// Integer arithmetic
+	TInt currentTwoDigitYear=now.Year()-currentCentury;
+
+	TInt year=0;
+	if (numberOfDateFields==3)// then year value exists
+		{
+		year=iFormula[yearIndex+aOffset];
+		if (year<0)// ie a token has been returned as a year
+			return(KErrArgument);
+		else if (iTokenLen[yearIndex+aOffset]<=2)
+			{
+			if (currentTwoDigitYear>=iCenturyOffset)
+				{
+				if (year>=00 && year<iCenturyOffset) 
+					year+=currentCentury+100;// next century
+				else
+					year+=currentCentury;
+				}
+			else
+				{
+				if (year>=00 && year<iCenturyOffset) 
+					year+=currentCentury;
+				else
+					year+=currentCentury-100;// last century
+				}
+			}
+		}
+
+	TInt month=iFormula[monthIndex+aOffset];
+	if (IsAlphaMonth(month))
+		month=-month;// alphaMonth is -ve enum token
+	month-=1;// months start at zero
+
+	TInt error;// Set Year, Month and Day
+	if ((error=iDateTime.SetYear(year))==KErrNone)
+		if ((error=iDateTime.SetMonth((TMonth)month))==KErrNone)
+			error=iDateTime.SetDay(iFormula[dayIndex+aOffset]-1);
+	if (error!=KErrNone)
+		return(error);
+
+
+	if (numberOfDateFields==2)
+		aTokenCount=3;
+	else if (numberOfDateFields==3)
+		aTokenCount=5;
+	
+	if (aOffset!=0)
+		return(EDatePresent|ETimePresent);
+	return(EDatePresent);
+	}
+
+TInt TStringToDateTime::GetTime(TInt aOffset,TInt& aTokenCount)
+	// aFormulaPos == 0  Time format or TimeDate format with Time first
+	// aFormulaPos != 0  Date preceeds Time i.e. DateTime format
+	// 7 formats 10:00:00.012345 // 10:00:00.012345pm // 10:00:00pm // 10:00:00 // 10:00pm // 10:00 // 10pm
+	// offset and relativeCount allow this function to check times 
+	// when both the Time(10:00pm) and DateTime(3-feb-69 10:00pm) formats are used.
+	{
+
+	TInt relativeCount=iCount;
+	if (aOffset!=0)// DateTime // else format==Time
+		{
+		relativeCount-=aOffset;
+		if (relativeCount<=-1)
+			return(EDatePresent);
+		}
+	TInt fields=0;
+
+	if (IsTimeSeparator(iFormula[1+aOffset]) && IsTimeSeparator(iFormula[3+aOffset])&& 
+		 (IsTimeSeparator(iFormula[5+aOffset]) || IsDecimalSeparator(iFormula[5+aOffset])))
+		{
+		fields=4;// 10:00:00.000000 (am)
+		aTokenCount=7; 
+		if (IsAmPm(iFormula[7+aOffset]))
+			aTokenCount+=1;
+		}
+	else if (IsTimeSeparator(iFormula[1+aOffset]) && IsTimeSeparator(iFormula[3+aOffset]))
+		{
+		fields=3;// 10:00:00 (am)
+		aTokenCount=5; 
+		if (IsAmPm(iFormula[5+aOffset]))
+			aTokenCount+=1;
+		}
+	else if (IsTimeSeparator(iFormula[1+aOffset]))
+		{
+		fields=2;// 10:00 (am)
+		aTokenCount=3;
+		if (IsAmPm(iFormula[3+aOffset]))
+			aTokenCount+=1;
+		}
+	else if (IsAmPm(iFormula[1+aOffset]))
+		{
+		fields=1;// 10am
+		aTokenCount=2;
+		}
+	if (fields==0 || (fields==4 && relativeCount==6) || (fields==3 && relativeCount==4) || (fields==2 && relativeCount==2))
+		return(KErrArgument);// Colon\DecimalPoint in wrong place 10:00:00. || 10:00: || 10:
+	
+	TInt error;
+	if ((error=iDateTime.SetHour(iFormula[0+aOffset]))!=KErrNone)
+		return error;
+	if (fields==2)
+		error=iDateTime.SetMinute(iFormula[2+aOffset]);
+	else if (fields==3)
+		{
+		if ((error=iDateTime.SetMinute(iFormula[2+aOffset]))==KErrNone)
+			error=iDateTime.SetSecond(iFormula[4+aOffset]);
+		}
+	else if (fields==4)
+		{
+		if ((error=iDateTime.SetMinute(iFormula[2+aOffset]))==KErrNone)
+			 if ((error=iDateTime.SetSecond(iFormula[4+aOffset]))==KErrNone)
+				 error = iDateTime.SetMicroSecond(iFormula[6+aOffset]);
+		}
+	if (error!=KErrNone)
+		return(error);
+
+	TInt ampmIndex=2*fields-1;
+	if (iFormula[ampmIndex+aOffset]==ETokenAm && iDateTime.Hour()==12)
+		error=iDateTime.SetHour(00);// 12am->00 hrs. Ignore 13am
+	else if (iFormula[ampmIndex+aOffset]==ETokenPm && iDateTime.Hour()<12)
+		error=iDateTime.SetHour(iDateTime.Hour()+12);
+	if (error!=KErrNone)
+		return(error);
+
+	if (aOffset!=0)
+		return(ETimePresent|EDatePresent);
+	return(ETimePresent);
+	}
+
+TInt TStringToDateTime::NextToken(TInt& aTokenLen)
+	{
+	if (iLex.Eos())
+		return ENullToken;
+
+	TChar ch=iLex.Peek();
+
+	if (ch.IsDigit())
+		{
+		iLex.Mark();
+		do iLex.Inc(); while (iLex.Peek().IsDigit());
+
+		TPtrC des=iLex.MarkedToken();
+
+		TInt digit;
+		TLex lex(des);
+		if (lex.Val(digit)!=KErrNone)
+			return(EErrorToken);
+		aTokenLen = des.Length();
+		return(digit);
+		}
+	else if (IsSeparator(ch))
+		{
+		iLex.Inc();
+		iLex.SkipSpace();
+		aTokenLen = 1;
+		return(GetSeparatorToken(ch));
+		}
+	else
+		{
+		iLex.Mark();
+		do iLex.Inc(); while (iLex.Peek().IsAlpha() || iLex.Peek().IsDigit());
+
+		TPtrC des=iLex.MarkedToken();
+		aTokenLen = des.Length();
+
+		for (TInt month=EJanuary; month<=EDecember; ++month)
+			{
+			// Abbreviated month name
+			TMonthNameAbb nameAbb((TMonth)month);
+			if (nameAbb.CompareF(des)==0)
+				return(-(month+1)); // All values negative
+
+			// Full month name
+			TMonthName name((TMonth)month);
+			if (name.CompareF(des)==0)
+				return(-(month+1)); // All values negative
+			}
+
+		// Substring of am or pm
+		TAmPmName am(EAm);
+		TAmPmName pm(EPm);
+			
+		if (am.FindF(des)==0)
+			return(ETokenAm);
+		else if (pm.FindF(des)==0)
+			return(ETokenPm);
+		
+		return(EErrorToken);
+		}
+	}
+
+
+TInt TStringToDateTime::Parse(TTime& aTime)
+	{
+
+	iLex.SkipSpace();
+	TInt i = 0;
+	for (;;)
+		{
+		if (i==EMaxTokens-1)	// space left to append NullToken
+			return KErrArgument;
+		TInt len;
+		TInt token=NextToken(len);// uses iLex
+		if (token==EErrorToken)
+			return KErrArgument;
+		if (token==ENullToken)
+			break;
+		iFormula[i]=token;	// append token to formula
+		iTokenLen[i]=(TUint8)Min(len, 255);
+		++i;
+		}
+	iCount=i;
+
+	StripSpaceTokens();// Uses then resets iCount
+	TInt ret=CrackTokenFormula();
+	if (ret<0)
+		return(ret);
+	if (iDateTime.Year()>9999)
+		return KErrArgument;
+	aTime=iDateTime;
+	return(ret);
+	}
+
+EXPORT_C TInt TTime::Parse(const TDesC& aDes,TInt aCenturyOffset)
+/**
+Parses a descriptor containing either or both a date and time, and sets this 
+TTime to the value of the parsed descriptor.
+	
+The descriptor may contain the date only, the time only, the date followed 
+by the time, or the time followed by the date. When both the date and time 
+are specified in the descriptor, they should be separated using one or more 
+space characters. 
+	
+Leading zeros and spaces preceding any time or date components are discarded.
+	
+Dates may be specified either with all three components (day, month and year), 
+or with just two components; for example month and day. The date suffix ("st" 
+"nd" "rd" or "th") may not be included in the descriptor.
+	
+The date and its components may take different forms:
+	
+1. The month may be represented by text or by numbers.
+	
+2  European (DD/MM/YYYY), American (MM/DD/YYYY) and Japanese (YYYY/MM/DD) date 
+   formats are supported. An exception to this ordering of date components occurs 
+   when European or American formatting is used and the month is represented 
+   by text. In this case, the month may be positioned in either the first or 
+   second field. When using Japanese date format, the month, whether text or 
+  numbers, must always be the second field.
+	
+3. The year may be two or four digits. When the year is a two digit number, (e.g. 
+   97 rather than 1997), to resolve any confusion as to which century the year 
+   falls in, the second argument determines the century. For example, if the 
+   current year is 1997, a value for aCenturyOffset of 20 means that any two 
+   digit year will resolve to a year in the range 1920 to 2019. In this case, 
+   two digit years between 00 and 19 inclusive refer to the years between 2000 
+   and 2019 and two digit years between 20 and 99 inclusive refer to the years 
+   between 1920 and 1999. By default, two digit years are in the current century 
+   (aCenturyOffset = 0).
+	
+4. Any of the following characters may be used as the date separator: /(slash) 
+   - (dash) , (comma), spaces, or either of the date separator characters specified 
+   in TLocale::SetDateSeparator() (at index 1 or 2). Other characters are illegal.
+	
+If a colon or a dot has been specified in TLocale as the date separator character, 
+neither may be used as date separators in this function.
+	
+If specified, the time must include the hour, but both minutes and seconds, 
+or seconds alone may be omitted. 
+	
+The time and its components may take different forms:
+	
+1. An am/pm time suffix may be appended to the time. If 24 hour clock format 
+   is in use, this text will be ignored. 
+	
+2. The am/pm suffix may be abbreviated to "a" or "p".
+	
+3. Any of the following characters may be used as the time separator: :(colon) 
+   .(dot) or either of the time separator characters specified in
+   TLocale::SetDateSeparator() (at index 1 or 2). Other characters are illegal.
+	
+When a character can be interpreted as either a date or time separator character, 
+this function will interpret it as a date separator.
+	
+Look out for cases in which wrongly interpreting the contents of a descriptor, 
+based on the interpretation of separator characters, causes an error. For 
+example, trying to interpret "5.6.1996" as a time is invalid and will return 
+an error of -2 because 1,996 seconds is out of range.
+	
+Notes:
+	
+1. The entire content of the descriptor must be valid and syntactically correct, 
+   or an error will be returned and the parse will fail. So, excepting whitespace, 
+   which is discarded, any trailing characters within the descriptor which do 
+   not form part of the date or time are illegal. 
+	
+2. If no time is specified in the descriptor, the hours, minutes and seconds 
+   of this TTime are all set to zero, corresponding to midnight at the start 
+   of the day specified in the date. If no date is specified, each of this TTime's 
+   date components are set to zero.
+
+@param aDes           Descriptor containing any combination of date and
+                      time as text.
+@param aCenturyOffset Offset between zero (the default) and 99. Allows a flexible 
+                      interpretation of the century for two digit year values.
+                      If less than zero, or greater than 99, a panic occurs.
+                      
+@return If equal to or greater than zero, the function completed successfully. 
+        EParseDatePresent and/or EParseTimePresent indicate whether either or both 
+        of the date or time are present.
+        If less than zero, an error code.
+        KErrGeneral indicates that the time or date value is out of range,
+        e.g. if the hour is greater than 23 or if the minute is greater
+        than 59.
+        KErrNotSupported indicates that a two field date has been entered.
+        KErrArgument indicates that the descriptor was syntactically incorrect.
+        If the function fails, this TTime object remains unchanged.
+*/
+	{
+
+	TStringToDateTime parse(aDes,aCenturyOffset);
+	return parse.Parse(*this);
+	}
+