--- /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);
+ }
+