Update contrib.
1 // Copyright (c) 1996-2009 Nokia Corporation and/or its subsidiary(-ies).
2 // All rights reserved.
3 // This component and the accompanying materials are made available
4 // under the terms of the License "Eclipse Public License v1.0"
5 // which accompanies this distribution, and is available
6 // at the URL "http://www.eclipse.org/legal/epl-v10.html".
8 // Initial Contributors:
9 // Nokia Corporation - initial contribution.
14 // e32\euser\us_parse.cpp
20 class TStringToDateTime
23 TStringToDateTime(const TDesC& aDes,TInt aCenturyOffset);
24 TInt Parse(TTime& aTime);
25 enum {ETimePresent=1,EDatePresent};
28 enum {EDec=-12,ENov,EOct,ESep,EAug,EJul,EJun,EMay,EApr,EMar,EFeb,EJan};
29 enum {ETokenAm=-19,ETokenPm};
30 enum TDateSeparators{ESlash=-39,EDash,EComma,ESpace,EDateLocale1,EDateLocale2};
31 enum TTimeSeparators{EColon=-49,EDot,ETimeLocale1,ETimeLocale2};
32 enum TDecimalSeparators{EDecimalLocale=-59};
33 enum {EErrorToken=-99,ENullToken=-100};
35 enum {ENumberOfDateSep=6,ENumberOfTimeSep=4,EMaxTokens=27};
36 enum {EFirstDateSep=ESlash,ELastDateSep=EDateLocale2,EFirstTimeSep=EColon,ELastTimeSep=ETimeLocale2};
38 TInt NextToken(TInt& aTokenLen);
39 void StripSpaceTokens();
40 TInt CrackTokenFormula();
41 TInt GetDate(TInt aFormulaPos,TInt& aTokenCount);
42 TInt GetTime(TInt aFormulaPos,TInt& aTokenCount);
44 TInt GetSeparatorToken(TChar aChar) const;
45 TBool IsTimeSeparator(TChar aChar) const;
46 TBool IsDateSeparator(TChar aChar) const;
47 TBool IsDecimalSeparator(TChar aChar) const;
48 TBool IsSeparator(TChar aChar) const;
49 TBool IsSeparator(TInt aToken) const;
50 inline TBool IsTimeSeparator(TInt aToken) const;
51 inline TBool IsDateSeparator(TInt aToken) const;
52 TBool IsDecimalSeparator(TInt aToken) const;
53 inline TBool IsAmPm(TInt aToken) const;
54 inline TBool IsAlphaMonth(TInt aToken) const;
58 TDateFormat iDateFormat;
59 TChar iTimeSepChars[ENumberOfTimeSep];
60 TChar iDateSepChars[ENumberOfDateSep];
64 TInt iFormula[EMaxTokens];// 27 max possible with valid des (including spaces):" 10 : 00 : 00 . 000000 pm 6 / 12 / 99 "
65 TUint8 iTokenLen[EMaxTokens];
68 inline TBool TStringToDateTime::IsTimeSeparator(TInt aToken) const
70 return(aToken >= EFirstTimeSep && aToken <=ELastTimeSep);
72 inline TBool TStringToDateTime::IsDateSeparator(TInt aToken) const
74 return(aToken>=EFirstDateSep && aToken<=ELastDateSep);
76 inline TBool TStringToDateTime::IsAmPm(TInt aToken) const
78 return(aToken==ETokenAm || aToken==ETokenPm);
80 inline TBool TStringToDateTime::IsAlphaMonth(TInt aToken) const
82 return (aToken>=EDec && aToken<=EJan);
85 inline TBool TStringToDateTime::IsDecimalSeparator(TChar aChar) const
87 return(aChar==iDecSepChar);
89 inline TBool TStringToDateTime::IsDecimalSeparator(TInt aToken) const
91 return(aToken==EDecimalLocale);
94 TStringToDateTime::TStringToDateTime(const TDesC& aDes,TInt aCenturyOffset)
95 : iLex(aDes),iCenturyOffset(aCenturyOffset),iDateTime(0,EJanuary,0,0,0,0,0)
98 __ASSERT_ALWAYS(aCenturyOffset>=0 && aCenturyOffset<100,Panic(ETTimeValueOutOfRange));
100 iDateFormat=locale.DateFormat();
102 iTimeSepChars[0]=':';
103 iTimeSepChars[1]='.';
104 iTimeSepChars[2]=locale.TimeSeparator(1);
105 iTimeSepChars[3]=locale.TimeSeparator(2);
107 iDateSepChars[0]='/';
108 iDateSepChars[1]='-';
109 iDateSepChars[2]=',';
110 iDateSepChars[3]=' ';
111 iDateSepChars[4]=locale.DateSeparator(1);
112 iDateSepChars[5]=locale.DateSeparator(2);
113 iDecSepChar = locale.DecimalSeparator();
116 TBool TStringToDateTime::IsTimeSeparator(TChar aChar) const
119 for (TInt ii=0;ii<ENumberOfTimeSep;++ii)
120 if (aChar==iTimeSepChars[ii])
125 TBool TStringToDateTime::IsDateSeparator(TChar aChar) const
128 for (TInt ii=0;ii<ENumberOfDateSep;++ii)
129 if (aChar==iDateSepChars[ii])
134 TBool TStringToDateTime::IsSeparator(TChar aChar) const
137 return(IsTimeSeparator(aChar) || IsDateSeparator(aChar) || IsDecimalSeparator(aChar));
140 TBool TStringToDateTime::IsSeparator(TInt aToken) const
143 return(IsTimeSeparator(aToken) || IsDateSeparator(aToken));
146 TInt TStringToDateTime::GetSeparatorToken(TChar aChar) const
150 for (ii=0;ii<ENumberOfDateSep;++ii)
151 if (aChar == iDateSepChars[ii])
152 return(EFirstDateSep+ii);
153 for (ii=0;ii<ENumberOfTimeSep;++ii)
154 if (aChar == iTimeSepChars[ii])
155 return(EFirstTimeSep+ii);
156 if (aChar == iDecSepChar)
157 return(EDecimalLocale);
161 void TStringToDateTime::StripSpaceTokens()
162 // Removes excess space tokens from the formula
163 // The end of the formula is marked with a Null token
167 for (TInt s = 0 ; s < iCount ; ++s)
169 if (iFormula[s]==ESpace &&
170 (IsSeparator(iFormula[s-1]) || s == iCount-1 || IsSeparator(iFormula[s+1]) || IsAmPm(iFormula[s+1])))
171 continue;// Skip unwanted space token
172 iFormula[t]=iFormula[s];
173 iTokenLen[t]=iTokenLen[s];
177 iFormula[t]=ENullToken;
180 TInt TStringToDateTime::CrackTokenFormula()
184 return KErrArgument;// Nothing to read
185 TInt token0=iFormula[0];
186 TInt token1=iFormula[1];
190 if (IsDateSeparator(token1) || IsAlphaMonth(token0))
191 {// Assume formula is a Date or DateTime
192 if ((error=GetDate(0,numberOfTokens))!=EDatePresent)
194 numberOfTokens+=1;// Space char between the Date & Time
195 return(GetTime(numberOfTokens,dummy));
197 else if (IsTimeSeparator(token1) || IsAmPm(token1))
198 {// Assume formula is a Time or TimeDate
199 if ((error=GetTime(0,numberOfTokens))!=ETimePresent)
201 numberOfTokens+=1;// Space char between the Time & Date
202 return(GetDate(numberOfTokens,dummy));
205 return(KErrArgument);
208 TInt TStringToDateTime::GetDate(TInt aOffset,TInt& aTokenCount)
209 // if aOffset == 0 then Date or DateTime format
210 // if aOffset != 0 then TimeDate format
213 TInt relativeCount=iCount;
214 if (aOffset!=0)// aFormat==ETimeDate
216 relativeCount-=aOffset;
217 if (relativeCount<=-1)
218 return(ETimePresent);
220 TInt numberOfDateFields=0;
221 if (relativeCount==3)
222 numberOfDateFields=2;
223 else if (relativeCount==5)
224 numberOfDateFields=3;
227 if (IsTimeSeparator(iFormula[5]) || IsAmPm(iFormula[5]))
228 numberOfDateFields=2;
230 numberOfDateFields=3;
234 if (relativeCount==3)
235 numberOfDateFields=2;
236 else if (relativeCount==5)
237 numberOfDateFields=3;
239 return(KErrArgument);
242 if (!IsDateSeparator(iFormula[1+aOffset]))
243 return(KErrArgument);
244 if (numberOfDateFields==2)
246 if (aOffset!=0 && relativeCount!=3)// ie TimeDate
247 return(KErrArgument);
249 if (numberOfDateFields==3)
251 if (aOffset!=0 && relativeCount!=5)// ie TimeDate
252 return(KErrArgument);
253 if (!IsDateSeparator(iFormula[3+aOffset]))
254 return(KErrArgument);
257 // A month will always be in the first two fields // DMY MDY YMD
258 TBool alphaMonth=(IsAlphaMonth(iFormula[0+aOffset]) || IsAlphaMonth(iFormula[2+aOffset]) );
262 TInt yearIndex=4;// Reset if Japanese
264 if (iDateFormat==EDateJapanese)
266 if (numberOfDateFields==2)
278 else if (IsAlphaMonth(iFormula[0+aOffset])
279 || (!alphaMonth && iDateFormat==EDateAmerican))// Amer Euro
280 {// feb 3 1996 valid Amer or Euro format // 2 3 1996 Amer
285 {// 3 feb 1996 valid Amer or Euro format // 3 2 1996 Euro
287 IsAlphaMonth(iFormula[2+aOffset]) ||
288 (!alphaMonth && iDateFormat==EDateEuropean),User::Invariant());
295 TDateTime now=timeNow.DateTime();
296 TInt currentCentury=((now.Year()/100)*100);// Integer arithmetic
297 TInt currentTwoDigitYear=now.Year()-currentCentury;
300 if (numberOfDateFields==3)// then year value exists
302 year=iFormula[yearIndex+aOffset];
303 if (year<0)// ie a token has been returned as a year
304 return(KErrArgument);
305 else if (iTokenLen[yearIndex+aOffset]<=2)
307 if (currentTwoDigitYear>=iCenturyOffset)
309 if (year>=00 && year<iCenturyOffset)
310 year+=currentCentury+100;// next century
312 year+=currentCentury;
316 if (year>=00 && year<iCenturyOffset)
317 year+=currentCentury;
319 year+=currentCentury-100;// last century
324 TInt month=iFormula[monthIndex+aOffset];
325 if (IsAlphaMonth(month))
326 month=-month;// alphaMonth is -ve enum token
327 month-=1;// months start at zero
329 TInt error;// Set Year, Month and Day
330 if ((error=iDateTime.SetYear(year))==KErrNone)
331 if ((error=iDateTime.SetMonth((TMonth)month))==KErrNone)
332 error=iDateTime.SetDay(iFormula[dayIndex+aOffset]-1);
337 if (numberOfDateFields==2)
339 else if (numberOfDateFields==3)
343 return(EDatePresent|ETimePresent);
344 return(EDatePresent);
347 TInt TStringToDateTime::GetTime(TInt aOffset,TInt& aTokenCount)
348 // aFormulaPos == 0 Time format or TimeDate format with Time first
349 // aFormulaPos != 0 Date preceeds Time i.e. DateTime format
350 // 7 formats 10:00:00.012345 // 10:00:00.012345pm // 10:00:00pm // 10:00:00 // 10:00pm // 10:00 // 10pm
351 // offset and relativeCount allow this function to check times
352 // when both the Time(10:00pm) and DateTime(3-feb-69 10:00pm) formats are used.
355 TInt relativeCount=iCount;
356 if (aOffset!=0)// DateTime // else format==Time
358 relativeCount-=aOffset;
359 if (relativeCount<=-1)
360 return(EDatePresent);
364 if (IsTimeSeparator(iFormula[1+aOffset]) && IsTimeSeparator(iFormula[3+aOffset])&&
365 (IsTimeSeparator(iFormula[5+aOffset]) || IsDecimalSeparator(iFormula[5+aOffset])))
367 fields=4;// 10:00:00.000000 (am)
369 if (IsAmPm(iFormula[7+aOffset]))
372 else if (IsTimeSeparator(iFormula[1+aOffset]) && IsTimeSeparator(iFormula[3+aOffset]))
374 fields=3;// 10:00:00 (am)
376 if (IsAmPm(iFormula[5+aOffset]))
379 else if (IsTimeSeparator(iFormula[1+aOffset]))
381 fields=2;// 10:00 (am)
383 if (IsAmPm(iFormula[3+aOffset]))
386 else if (IsAmPm(iFormula[1+aOffset]))
391 if (fields==0 || (fields==4 && relativeCount==6) || (fields==3 && relativeCount==4) || (fields==2 && relativeCount==2))
392 return(KErrArgument);// Colon\DecimalPoint in wrong place 10:00:00. || 10:00: || 10:
395 if ((error=iDateTime.SetHour(iFormula[0+aOffset]))!=KErrNone)
398 error=iDateTime.SetMinute(iFormula[2+aOffset]);
401 if ((error=iDateTime.SetMinute(iFormula[2+aOffset]))==KErrNone)
402 error=iDateTime.SetSecond(iFormula[4+aOffset]);
406 if ((error=iDateTime.SetMinute(iFormula[2+aOffset]))==KErrNone)
407 if ((error=iDateTime.SetSecond(iFormula[4+aOffset]))==KErrNone)
408 error = iDateTime.SetMicroSecond(iFormula[6+aOffset]);
413 TInt ampmIndex=2*fields-1;
414 if (iFormula[ampmIndex+aOffset]==ETokenAm && iDateTime.Hour()==12)
415 error=iDateTime.SetHour(00);// 12am->00 hrs. Ignore 13am
416 else if (iFormula[ampmIndex+aOffset]==ETokenPm && iDateTime.Hour()<12)
417 error=iDateTime.SetHour(iDateTime.Hour()+12);
422 return(ETimePresent|EDatePresent);
423 return(ETimePresent);
426 TInt TStringToDateTime::NextToken(TInt& aTokenLen)
431 TChar ch=iLex.Peek();
436 do iLex.Inc(); while (iLex.Peek().IsDigit());
438 TPtrC des=iLex.MarkedToken();
442 if (lex.Val(digit)!=KErrNone)
444 aTokenLen = des.Length();
447 else if (IsSeparator(ch))
452 return(GetSeparatorToken(ch));
457 do iLex.Inc(); while (iLex.Peek().IsAlpha() || iLex.Peek().IsDigit());
459 TPtrC des=iLex.MarkedToken();
460 aTokenLen = des.Length();
462 for (TInt month=EJanuary; month<=EDecember; ++month)
464 // Abbreviated month name
465 TMonthNameAbb nameAbb((TMonth)month);
466 if (nameAbb.CompareF(des)==0)
467 return(-(month+1)); // All values negative
470 TMonthName name((TMonth)month);
471 if (name.CompareF(des)==0)
472 return(-(month+1)); // All values negative
475 // Substring of am or pm
479 if (am.FindF(des)==0)
481 else if (pm.FindF(des)==0)
489 TInt TStringToDateTime::Parse(TTime& aTime)
496 if (i==EMaxTokens-1) // space left to append NullToken
499 TInt token=NextToken(len);// uses iLex
500 if (token==EErrorToken)
502 if (token==ENullToken)
504 iFormula[i]=token; // append token to formula
505 iTokenLen[i]=(TUint8)Min(len, 255);
510 StripSpaceTokens();// Uses then resets iCount
511 TInt ret=CrackTokenFormula();
514 if (iDateTime.Year()>9999)
520 EXPORT_C TInt TTime::Parse(const TDesC& aDes,TInt aCenturyOffset)
522 Parses a descriptor containing either or both a date and time, and sets this
523 TTime to the value of the parsed descriptor.
525 The descriptor may contain the date only, the time only, the date followed
526 by the time, or the time followed by the date. When both the date and time
527 are specified in the descriptor, they should be separated using one or more
530 Leading zeros and spaces preceding any time or date components are discarded.
532 Dates may be specified either with all three components (day, month and year),
533 or with just two components; for example month and day. The date suffix ("st"
534 "nd" "rd" or "th") may not be included in the descriptor.
536 The date and its components may take different forms:
538 1. The month may be represented by text or by numbers.
540 2 European (DD/MM/YYYY), American (MM/DD/YYYY) and Japanese (YYYY/MM/DD) date
541 formats are supported. An exception to this ordering of date components occurs
542 when European or American formatting is used and the month is represented
543 by text. In this case, the month may be positioned in either the first or
544 second field. When using Japanese date format, the month, whether text or
545 numbers, must always be the second field.
547 3. The year may be two or four digits. When the year is a two digit number, (e.g.
548 97 rather than 1997), to resolve any confusion as to which century the year
549 falls in, the second argument determines the century. For example, if the
550 current year is 1997, a value for aCenturyOffset of 20 means that any two
551 digit year will resolve to a year in the range 1920 to 2019. In this case,
552 two digit years between 00 and 19 inclusive refer to the years between 2000
553 and 2019 and two digit years between 20 and 99 inclusive refer to the years
554 between 1920 and 1999. By default, two digit years are in the current century
555 (aCenturyOffset = 0).
557 4. Any of the following characters may be used as the date separator: /(slash)
558 - (dash) , (comma), spaces, or either of the date separator characters specified
559 in TLocale::SetDateSeparator() (at index 1 or 2). Other characters are illegal.
561 If a colon or a dot has been specified in TLocale as the date separator character,
562 neither may be used as date separators in this function.
564 If specified, the time must include the hour, but both minutes and seconds,
565 or seconds alone may be omitted.
567 The time and its components may take different forms:
569 1. An am/pm time suffix may be appended to the time. If 24 hour clock format
570 is in use, this text will be ignored.
572 2. The am/pm suffix may be abbreviated to "a" or "p".
574 3. Any of the following characters may be used as the time separator: :(colon)
575 .(dot) or either of the time separator characters specified in
576 TLocale::SetDateSeparator() (at index 1 or 2). Other characters are illegal.
578 When a character can be interpreted as either a date or time separator character,
579 this function will interpret it as a date separator.
581 Look out for cases in which wrongly interpreting the contents of a descriptor,
582 based on the interpretation of separator characters, causes an error. For
583 example, trying to interpret "5.6.1996" as a time is invalid and will return
584 an error of -2 because 1,996 seconds is out of range.
588 1. The entire content of the descriptor must be valid and syntactically correct,
589 or an error will be returned and the parse will fail. So, excepting whitespace,
590 which is discarded, any trailing characters within the descriptor which do
591 not form part of the date or time are illegal.
593 2. If no time is specified in the descriptor, the hours, minutes and seconds
594 of this TTime are all set to zero, corresponding to midnight at the start
595 of the day specified in the date. If no date is specified, each of this TTime's
596 date components are set to zero.
598 @param aDes Descriptor containing any combination of date and
600 @param aCenturyOffset Offset between zero (the default) and 99. Allows a flexible
601 interpretation of the century for two digit year values.
602 If less than zero, or greater than 99, a panic occurs.
604 @return If equal to or greater than zero, the function completed successfully.
605 EParseDatePresent and/or EParseTimePresent indicate whether either or both
606 of the date or time are present.
607 If less than zero, an error code.
608 KErrGeneral indicates that the time or date value is out of range,
609 e.g. if the hour is greater than 23 or if the minute is greater
611 KErrNotSupported indicates that a two field date has been entered.
612 KErrArgument indicates that the descriptor was syntactically incorrect.
613 If the function fails, this TTime object remains unchanged.
617 TStringToDateTime parse(aDes,aCenturyOffset);
618 return parse.Parse(*this);