sl@0: // Copyright (c) 2002-2009 Nokia Corporation and/or its subsidiary(-ies). sl@0: // All rights reserved. sl@0: // This component and the accompanying materials are made available sl@0: // under the terms of "Eclipse Public License v1.0" sl@0: // which accompanies this distribution, and is available sl@0: // at the URL "http://www.eclipse.org/legal/epl-v10.html". sl@0: // sl@0: // Initial Contributors: sl@0: // Nokia Corporation - initial contribution. sl@0: // sl@0: // Contributors: sl@0: // sl@0: // Description: sl@0: // This file contains the implementation of internal classes used to parse TInternetDate data sl@0: // sl@0: // sl@0: sl@0: /** sl@0: @file tinternetdateparser.cpp sl@0: @see tinternetdateparser.h sl@0: */ sl@0: sl@0: #include "tinternetdateparser.h" sl@0: #include "inetprottextutils.h" sl@0: #include sl@0: sl@0: //constants sl@0: // sl@0: const TInt KRfc1123DateTimeLength = 29; // Sun, 06 Nov 1994 08:49:37 GMT sl@0: const TInt KHHMMSSFormatLength=8; // HH:MM:SS sl@0: sl@0: // Long form day names, 'weekday' in RFC850 sl@0: // sl@0: static const TText8* const KInternetWeekDays[] = sl@0: { sl@0: _S8("Monday"), sl@0: _S8("Tuesday"), sl@0: _S8("Wednesday"), sl@0: _S8("Thursday"), sl@0: _S8("Friday"), sl@0: _S8("Saturday"), sl@0: _S8("Sunday") sl@0: }; sl@0: sl@0: // Month names, 'month' in RFC1123 & RFC850 sl@0: // sl@0: static const TText8* const KInternetMonths[] = sl@0: { sl@0: _S8("Jan"), sl@0: _S8("Feb"), sl@0: _S8("Mar"), sl@0: _S8("Apr"), sl@0: _S8("May"), sl@0: _S8("Jun"), sl@0: _S8("Jul"), sl@0: _S8("Aug"), sl@0: _S8("Sep"), sl@0: _S8("Oct"), sl@0: _S8("Nov"), sl@0: _S8("Dec") sl@0: }; sl@0: sl@0: /** sl@0: Converts from TDateTime to Internet RFC1123 Style date strings sl@0: sl@0: @since 7.0 sl@0: @param aDateTime The date/time to be parsed sl@0: @leave KErrNoMemory sl@0: @return HBufC8* aDescriptor containing a RFC1123 style date. sl@0: */ sl@0: HBufC8* TInternetDateParser::ConvertToRfc1123FormL(const TDateTime& aDateTime) sl@0: { sl@0: const TChar KSpace = ' '; sl@0: sl@0: HBufC8* dateTime = HBufC8::NewLC(KRfc1123DateTimeLength); sl@0: TPtr8 dateTimePtr = dateTime->Des(); sl@0: // Append the wkDay sl@0: TTime time(aDateTime); sl@0: TInt wkDay = time.DayNoInWeek(); sl@0: TPtrC8 wkDayText(KInternetWeekDays[wkDay]); sl@0: dateTimePtr.Append(wkDayText.Left(3)); sl@0: sl@0: sl@0: // Append the day number sl@0: _LIT8(KDayNumberFormat, ", %02d"); sl@0: TInt dayNumber = aDateTime.Day() + 1; sl@0: dateTimePtr.AppendFormat(KDayNumberFormat, dayNumber); sl@0: sl@0: // Append month sl@0: dateTimePtr.Append(KSpace); sl@0: TInt month = aDateTime.Month(); sl@0: dateTimePtr.Append(TPtrC8(KInternetMonths[month])); sl@0: sl@0: // Append year sl@0: _LIT8(KYearDigitFormat, " %4d"); sl@0: TInt year = aDateTime.Year(); sl@0: dateTimePtr.AppendFormat(KYearDigitFormat, year); sl@0: sl@0: // Append Time HH:MM:SS sl@0: sl@0: dateTimePtr.Append(KSpace); sl@0: _LIT8(KHHMMSSFormat, "%02d:%02d:%02d GMT"); sl@0: dateTimePtr.AppendFormat(KHHMMSSFormat, aDateTime.Hour(),aDateTime.Minute(),aDateTime.Second()); sl@0: sl@0: CleanupStack::Pop(dateTime); sl@0: return dateTime; sl@0: } sl@0: sl@0: sl@0: /** sl@0: Used to parse internet text date/time into a TDateTime sl@0: sl@0: @since 7.0 sl@0: @param aInternetTextDateTime The date/time in a text format sl@0: @param aDateTime The result of parsing is placed here sl@0: @leave KErrNoMemory, KErrCorrupt If the format of the descriptor is not valid sl@0: */ sl@0: void TInternetDateParser::ConvertFromInternetFormL(const TDesC8& aInternetTextDateTime, TDateTime& aDateTime) sl@0: { sl@0: const TChar KComma = ','; sl@0: TInt colonLoc = aInternetTextDateTime.Locate(KComma); sl@0: switch (colonLoc) sl@0: { sl@0: case -1: // ANSI C asctime() format: Sun Nov 6 08:49:37 1994 sl@0: ConvertFromAscTimeFormL(aInternetTextDateTime, aDateTime); sl@0: break; sl@0: case 3: // RFC 1123 Style Date: Sun, 06 Nov 1994 08:49:37 GMT sl@0: case 6: // RFC 850 Style Date: Sunday, 06-Nov-94 08:49:37 GMT sl@0: case 7: sl@0: case 8: sl@0: case 9: sl@0: { sl@0: TPtrC8 dateTime(aInternetTextDateTime.Right(aInternetTextDateTime.Length() -(colonLoc+1))); // Remove "," sl@0: ConvertFromRfc1123And850FormL(dateTime, aDateTime); sl@0: } sl@0: break; sl@0: default: sl@0: User::Leave(KErrCorrupt); sl@0: }; sl@0: } sl@0: sl@0: /** sl@0: @param aRfcDateTime sl@0: @param aDateTime The result of parsing is placed here sl@0: */ sl@0: void TInternetDateParser::ConvertFromRfc1123And850FormL(const TDesC8& aRfcDateTime, TDateTime& aDateTime) sl@0: { sl@0: TPtrC8 dateTime(aRfcDateTime); sl@0: InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveBoth); sl@0: TInt textLeft = ParseRfcDateDayMonthYearL(dateTime, aDateTime); sl@0: if (textLeft <= 0) sl@0: User::Leave(KErrCorrupt); sl@0: dateTime.Set(dateTime.Right(textLeft)); sl@0: InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); sl@0: ParseHHMMSSTimeL(dateTime, aDateTime); sl@0: textLeft= dateTime.Length() - KHHMMSSFormatLength; sl@0: if (textLeft > 1) // Must still have leading space sl@0: { sl@0: dateTime.Set(dateTime.Right(textLeft)); sl@0: InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); sl@0: TTimeIntervalMinutes timeOffset = 0; sl@0: ParseTimeOffsetL(dateTime, timeOffset); sl@0: sl@0: if (timeOffset.Int() > 0 || timeOffset.Int() < 0) sl@0: { sl@0: TTime time(aDateTime); sl@0: time -= timeOffset; sl@0: aDateTime = time.DateTime(); sl@0: } sl@0: } sl@0: else sl@0: User::Leave(KErrCorrupt); // Must include a timezone or timeoffset sl@0: } sl@0: sl@0: /** sl@0: @param aTimeOffset sl@0: @param aOffsetMinutes sl@0: */ sl@0: void TInternetDateParser::ParseHHMMTimeOffsetL(const TDesC8& aTimeOffset, TTimeIntervalMinutes& aOffsetMinutes) sl@0: { sl@0: TInt offsetTextLength=aTimeOffset.Length(); sl@0: if (offsetTextLength != 5) // +0000 or -0000 sl@0: User::Leave(KErrCorrupt); sl@0: sl@0: TPtrC8 timeOffset(aTimeOffset.Right(offsetTextLength-1)); sl@0: sl@0: TInt num=0; sl@0: if (InetProtTextUtils::ConvertDescriptorToInt(timeOffset, num) !=4) sl@0: User::Leave(KErrCorrupt); // not 0000 sl@0: sl@0: TInt minutes = num % 100; sl@0: TInt hours = num / 100; sl@0: TInt offsetMinutes=0; sl@0: if (minutes < 60 && hours < 24) sl@0: offsetMinutes = minutes + (hours * 60); sl@0: sl@0: if (aTimeOffset[0] == '-') sl@0: offsetMinutes *= -1; sl@0: sl@0: aOffsetMinutes = offsetMinutes; sl@0: } sl@0: sl@0: /** sl@0: @param aTimeOffset sl@0: @param aOffsetMinutes sl@0: */ sl@0: void TInternetDateParser::ParseTimeOffsetL(const TDesC8& aTimeOffset, TTimeIntervalMinutes& aOffsetMinutes) sl@0: { sl@0: TInt offsetMinutes=0; sl@0: TInt offsetTextLength = aTimeOffset.Length(); sl@0: if (offsetTextLength > 1) sl@0: { sl@0: // check second letter to see if UT, GMT sl@0: // and to differentiate between Standard and Daylight Savings sl@0: switch (aTimeOffset[1]) sl@0: { sl@0: case 'M' : // GMT sl@0: case 'T' : // UT sl@0: { sl@0: return; sl@0: } sl@0: case 'S': // Standard Time (Daylight Savings Time is one hour ahead); sl@0: offsetMinutes -= 60; sl@0: break; sl@0: case 'D' : // Daylight Savings Time sl@0: break; sl@0: default: sl@0: // Must be either corrupt or an explicit HHMM +/- offset sl@0: ParseHHMMTimeOffsetL(aTimeOffset, aOffsetMinutes); sl@0: return; sl@0: }; sl@0: sl@0: // Check first letter to find out if its Eastern, Central, Mountain or Pacific Time sl@0: switch (aTimeOffset[0]) sl@0: { sl@0: case 'E' : // EST or EDT -4 sl@0: offsetMinutes -=240; sl@0: break; sl@0: case 'C' : // CST or CDT -5 sl@0: offsetMinutes -=300; sl@0: break; sl@0: case 'M' : // MST or MDT -6 sl@0: offsetMinutes -= 360; sl@0: break; sl@0: case 'P' : // PST or PDT -7 sl@0: offsetMinutes -=420; sl@0: break; sl@0: default: sl@0: // either the format is invalid or it is a non standard 3 letter timezone sl@0: // in either case sl@0: return; sl@0: }; sl@0: } sl@0: else // This must be a military single letter format time zone sl@0: // According to RFC2882 These Military Timezones are obsolete. Also since A can be either +1 or -1 depending on the specification sl@0: // it should be ignored unless there is additional Out of band Information sl@0: { sl@0: return; // Do nothing sl@0: } sl@0: sl@0: aOffsetMinutes = offsetMinutes; sl@0: } sl@0: sl@0: /** sl@0: @param aDateTimeText sl@0: @param aDateTime sl@0: */ sl@0: void TInternetDateParser::ParseHHMMSSTimeL(const TDesC8& aDateTimeText, TDateTime& aDateTime) sl@0: { sl@0: if (aDateTimeText.Length() < KHHMMSSFormatLength) // Must be at least HH:MM:SS sl@0: User::Leave(KErrCorrupt); sl@0: TInt num=0; sl@0: TInt consumed = InetProtTextUtils::ConvertDescriptorToInt(aDateTimeText, num); sl@0: if (consumed != 2 || num < 0 || num > 23) sl@0: User::Leave(KErrCorrupt); sl@0: TPtrC8 dateTime(aDateTimeText.Right(aDateTimeText.Length() -3)); // Remove HH: sl@0: aDateTime.SetHour(num); sl@0: sl@0: consumed = InetProtTextUtils::ConvertDescriptorToInt(dateTime, num); sl@0: if (consumed != 2 || num < 0 || num > 59) sl@0: User::Leave(KErrCorrupt); sl@0: dateTime.Set(dateTime.Right(dateTime.Length() -3)); // Remove MM: sl@0: aDateTime.SetMinute(num); sl@0: sl@0: consumed = InetProtTextUtils::ConvertDescriptorToInt(dateTime, num); sl@0: if (consumed != 2 || num < 0 || num > 59) sl@0: User::Leave(KErrCorrupt); sl@0: aDateTime.SetSecond(num); sl@0: } sl@0: sl@0: /** sl@0: @param aDateTimeText sl@0: @param aDateTime sl@0: @return sl@0: */ sl@0: TInt TInternetDateParser::ParseRfcDateDayMonthYearL(const TDesC8& aDateTimeText, TDateTime& aDateTime) sl@0: { sl@0: // Parse RFC 1123 and RFC 850 style date month year parts sl@0: // 06 Nov 1994 (RFC 1123) sl@0: // 06-Nov-94 (RFC 850) In this case if YY < 50 then treat as 20YY otherwise 19YY sl@0: TInt day =0; sl@0: TInt consumed = InetProtTextUtils::ConvertDescriptorToInt(aDateTimeText,day); sl@0: if ((consumed !=1 && consumed != 2) || aDateTimeText.Length() <= 2) sl@0: User::Leave(KErrCorrupt); sl@0: sl@0: --day; // Symbian OS month days start from 0 sl@0: sl@0: // Skip over DD and separator sl@0: TPtrC8 dateTimeText(aDateTimeText.Right(aDateTimeText.Length()-(consumed+1))); sl@0: TMonth month = GetMonthL(dateTimeText); sl@0: // Skip over MMM and separator sl@0: const TInt KConsumedMonthLength = 4; sl@0: TInt rightLength = dateTimeText.Length() - KConsumedMonthLength ; sl@0: if( rightLength < 0) sl@0: { sl@0: User::Leave(KErrCorrupt); sl@0: } sl@0: dateTimeText.Set(dateTimeText.Right(rightLength)); sl@0: sl@0: // Now get year sl@0: TInt year; sl@0: consumed = InetProtTextUtils::ConvertDescriptorToInt(dateTimeText, year); sl@0: if (consumed != 4 && consumed !=2) sl@0: User::Leave(KErrCorrupt); sl@0: sl@0: // check for YY and adjust sl@0: if (consumed == 2) sl@0: year += year > 50 ? 1900 : 2000; sl@0: sl@0: if (aDateTime.Set(year, month, day, 0, 0, 0, 0) != KErrNone) sl@0: User::Leave(KErrCorrupt); sl@0: sl@0: return dateTimeText.Length() - (consumed +1); sl@0: } sl@0: sl@0: /** sl@0: @param aMonthText sl@0: @return sl@0: */ sl@0: TMonth TInternetDateParser::GetMonthL(const TDesC8& aMonthText) sl@0: { sl@0: if (aMonthText.Length() < 3) sl@0: User::Leave(KErrCorrupt); sl@0: sl@0: switch (aMonthText[0]) sl@0: { sl@0: case 'J': // Jan, Jun, Jul sl@0: return (aMonthText[1] == 'a' ? EJanuary : aMonthText[2] == 'n' ? EJune : EJuly); sl@0: case 'F': // Feb sl@0: return EFebruary; sl@0: case 'M': // Mar, May sl@0: return (aMonthText[2] == 'r' ? EMarch : EMay); sl@0: case 'A': // Apr, Aug sl@0: return (aMonthText[1] == 'p' ? EApril : EAugust); sl@0: case 'S': // Sep sl@0: return ESeptember; sl@0: case 'O': // Oct sl@0: return EOctober; sl@0: case 'N': // Nov sl@0: return ENovember; sl@0: case 'D': // Dec sl@0: return EDecember; sl@0: default: sl@0: User::Leave(KErrCorrupt); sl@0: }; sl@0: return EJanuary; sl@0: } sl@0: sl@0: sl@0: /** sl@0: @param aAscTimeDateTime sl@0: @param aDateTime sl@0: */ sl@0: void TInternetDateParser::ConvertFromAscTimeFormL(const TDesC8& aAscTimeDateTime, TDateTime& aDateTime) sl@0: { sl@0: // Convert a date in ascTime format ( Sun Nov 6 08:49:37 1994 ) sl@0: sl@0: const TChar KSpace = ' '; sl@0: sl@0: // First remove the wkDay sl@0: TPtrC8 dateTime(aAscTimeDateTime); sl@0: InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveBoth); sl@0: TInt endOfWkDayLoc = dateTime.Locate(KSpace); sl@0: if (endOfWkDayLoc != 3) // Must be following a wkDay "Sun " sl@0: User::Leave(KErrCorrupt); sl@0: dateTime.Set(dateTime.Right(dateTime.Length() - 3)); sl@0: InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); sl@0: // Now parse the month then skip over MMM sl@0: TMonth month = GetMonthL(dateTime); sl@0: dateTime.Set(dateTime.Right(dateTime.Length() - 3)); sl@0: InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); sl@0: // Now parse the monthDay sl@0: TInt day = 0; sl@0: TInt consumed = InetProtTextUtils::ConvertDescriptorToInt(dateTime, day); sl@0: if (consumed < 0) sl@0: User::Leave(KErrCorrupt); sl@0: sl@0: dateTime.Set(dateTime.Right(dateTime.Length() - consumed)); sl@0: InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); sl@0: sl@0: // Now get the time sl@0: ParseHHMMSSTimeL(dateTime, aDateTime); sl@0: dateTime.Set(dateTime.Right(dateTime.Length() - KHHMMSSFormatLength)); sl@0: // Now the year sl@0: InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); sl@0: TInt year; sl@0: consumed = InetProtTextUtils::ConvertDescriptorToInt(dateTime, year); sl@0: if (consumed !=4) sl@0: User::Leave(KErrCorrupt); sl@0: sl@0: if (aDateTime.Set(year, month, day-1, aDateTime.Hour(), aDateTime.Minute(), aDateTime.Second(), 0) != KErrNone) sl@0: User::Leave(KErrCorrupt); sl@0: }