|
1 // Copyright (c) 2002-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 "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". |
|
7 // |
|
8 // Initial Contributors: |
|
9 // Nokia Corporation - initial contribution. |
|
10 // |
|
11 // Contributors: |
|
12 // |
|
13 // Description: |
|
14 // This file contains the implementation of internal classes used to parse TInternetDate data |
|
15 // |
|
16 // |
|
17 |
|
18 /** |
|
19 @file tinternetdateparser.cpp |
|
20 @see tinternetdateparser.h |
|
21 */ |
|
22 |
|
23 #include "tinternetdateparser.h" |
|
24 #include "inetprottextutils.h" |
|
25 #include <e32base.h> |
|
26 |
|
27 //constants |
|
28 // |
|
29 const TInt KRfc1123DateTimeLength = 29; // Sun, 06 Nov 1994 08:49:37 GMT |
|
30 const TInt KHHMMSSFormatLength=8; // HH:MM:SS |
|
31 |
|
32 // Long form day names, 'weekday' in RFC850 |
|
33 // |
|
34 static const TText8* const KInternetWeekDays[] = |
|
35 { |
|
36 _S8("Monday"), |
|
37 _S8("Tuesday"), |
|
38 _S8("Wednesday"), |
|
39 _S8("Thursday"), |
|
40 _S8("Friday"), |
|
41 _S8("Saturday"), |
|
42 _S8("Sunday") |
|
43 }; |
|
44 |
|
45 // Month names, 'month' in RFC1123 & RFC850 |
|
46 // |
|
47 static const TText8* const KInternetMonths[] = |
|
48 { |
|
49 _S8("Jan"), |
|
50 _S8("Feb"), |
|
51 _S8("Mar"), |
|
52 _S8("Apr"), |
|
53 _S8("May"), |
|
54 _S8("Jun"), |
|
55 _S8("Jul"), |
|
56 _S8("Aug"), |
|
57 _S8("Sep"), |
|
58 _S8("Oct"), |
|
59 _S8("Nov"), |
|
60 _S8("Dec") |
|
61 }; |
|
62 |
|
63 /** |
|
64 Converts from TDateTime to Internet RFC1123 Style date strings |
|
65 |
|
66 @since 7.0 |
|
67 @param aDateTime The date/time to be parsed |
|
68 @leave KErrNoMemory |
|
69 @return HBufC8* aDescriptor containing a RFC1123 style date. |
|
70 */ |
|
71 HBufC8* TInternetDateParser::ConvertToRfc1123FormL(const TDateTime& aDateTime) |
|
72 { |
|
73 const TChar KSpace = ' '; |
|
74 |
|
75 HBufC8* dateTime = HBufC8::NewLC(KRfc1123DateTimeLength); |
|
76 TPtr8 dateTimePtr = dateTime->Des(); |
|
77 // Append the wkDay |
|
78 TTime time(aDateTime); |
|
79 TInt wkDay = time.DayNoInWeek(); |
|
80 TPtrC8 wkDayText(KInternetWeekDays[wkDay]); |
|
81 dateTimePtr.Append(wkDayText.Left(3)); |
|
82 |
|
83 |
|
84 // Append the day number |
|
85 _LIT8(KDayNumberFormat, ", %02d"); |
|
86 TInt dayNumber = aDateTime.Day() + 1; |
|
87 dateTimePtr.AppendFormat(KDayNumberFormat, dayNumber); |
|
88 |
|
89 // Append month |
|
90 dateTimePtr.Append(KSpace); |
|
91 TInt month = aDateTime.Month(); |
|
92 dateTimePtr.Append(TPtrC8(KInternetMonths[month])); |
|
93 |
|
94 // Append year |
|
95 _LIT8(KYearDigitFormat, " %4d"); |
|
96 TInt year = aDateTime.Year(); |
|
97 dateTimePtr.AppendFormat(KYearDigitFormat, year); |
|
98 |
|
99 // Append Time HH:MM:SS |
|
100 |
|
101 dateTimePtr.Append(KSpace); |
|
102 _LIT8(KHHMMSSFormat, "%02d:%02d:%02d GMT"); |
|
103 dateTimePtr.AppendFormat(KHHMMSSFormat, aDateTime.Hour(),aDateTime.Minute(),aDateTime.Second()); |
|
104 |
|
105 CleanupStack::Pop(dateTime); |
|
106 return dateTime; |
|
107 } |
|
108 |
|
109 |
|
110 /** |
|
111 Used to parse internet text date/time into a TDateTime |
|
112 |
|
113 @since 7.0 |
|
114 @param aInternetTextDateTime The date/time in a text format |
|
115 @param aDateTime The result of parsing is placed here |
|
116 @leave KErrNoMemory, KErrCorrupt If the format of the descriptor is not valid |
|
117 */ |
|
118 void TInternetDateParser::ConvertFromInternetFormL(const TDesC8& aInternetTextDateTime, TDateTime& aDateTime) |
|
119 { |
|
120 const TChar KComma = ','; |
|
121 TInt colonLoc = aInternetTextDateTime.Locate(KComma); |
|
122 switch (colonLoc) |
|
123 { |
|
124 case -1: // ANSI C asctime() format: Sun Nov 6 08:49:37 1994 |
|
125 ConvertFromAscTimeFormL(aInternetTextDateTime, aDateTime); |
|
126 break; |
|
127 case 3: // RFC 1123 Style Date: Sun, 06 Nov 1994 08:49:37 GMT |
|
128 case 6: // RFC 850 Style Date: Sunday, 06-Nov-94 08:49:37 GMT |
|
129 case 7: |
|
130 case 8: |
|
131 case 9: |
|
132 { |
|
133 TPtrC8 dateTime(aInternetTextDateTime.Right(aInternetTextDateTime.Length() -(colonLoc+1))); // Remove "<day>," |
|
134 ConvertFromRfc1123And850FormL(dateTime, aDateTime); |
|
135 } |
|
136 break; |
|
137 default: |
|
138 User::Leave(KErrCorrupt); |
|
139 }; |
|
140 } |
|
141 |
|
142 /** |
|
143 @param aRfcDateTime |
|
144 @param aDateTime The result of parsing is placed here |
|
145 */ |
|
146 void TInternetDateParser::ConvertFromRfc1123And850FormL(const TDesC8& aRfcDateTime, TDateTime& aDateTime) |
|
147 { |
|
148 TPtrC8 dateTime(aRfcDateTime); |
|
149 InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveBoth); |
|
150 TInt textLeft = ParseRfcDateDayMonthYearL(dateTime, aDateTime); |
|
151 if (textLeft <= 0) |
|
152 User::Leave(KErrCorrupt); |
|
153 dateTime.Set(dateTime.Right(textLeft)); |
|
154 InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); |
|
155 ParseHHMMSSTimeL(dateTime, aDateTime); |
|
156 textLeft= dateTime.Length() - KHHMMSSFormatLength; |
|
157 if (textLeft > 1) // Must still have leading space |
|
158 { |
|
159 dateTime.Set(dateTime.Right(textLeft)); |
|
160 InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); |
|
161 TTimeIntervalMinutes timeOffset = 0; |
|
162 ParseTimeOffsetL(dateTime, timeOffset); |
|
163 |
|
164 if (timeOffset.Int() > 0 || timeOffset.Int() < 0) |
|
165 { |
|
166 TTime time(aDateTime); |
|
167 time -= timeOffset; |
|
168 aDateTime = time.DateTime(); |
|
169 } |
|
170 } |
|
171 else |
|
172 User::Leave(KErrCorrupt); // Must include a timezone or timeoffset |
|
173 } |
|
174 |
|
175 /** |
|
176 @param aTimeOffset |
|
177 @param aOffsetMinutes |
|
178 */ |
|
179 void TInternetDateParser::ParseHHMMTimeOffsetL(const TDesC8& aTimeOffset, TTimeIntervalMinutes& aOffsetMinutes) |
|
180 { |
|
181 TInt offsetTextLength=aTimeOffset.Length(); |
|
182 if (offsetTextLength != 5) // +0000 or -0000 |
|
183 User::Leave(KErrCorrupt); |
|
184 |
|
185 TPtrC8 timeOffset(aTimeOffset.Right(offsetTextLength-1)); |
|
186 |
|
187 TInt num=0; |
|
188 if (InetProtTextUtils::ConvertDescriptorToInt(timeOffset, num) !=4) |
|
189 User::Leave(KErrCorrupt); // not 0000 |
|
190 |
|
191 TInt minutes = num % 100; |
|
192 TInt hours = num / 100; |
|
193 TInt offsetMinutes=0; |
|
194 if (minutes < 60 && hours < 24) |
|
195 offsetMinutes = minutes + (hours * 60); |
|
196 |
|
197 if (aTimeOffset[0] == '-') |
|
198 offsetMinutes *= -1; |
|
199 |
|
200 aOffsetMinutes = offsetMinutes; |
|
201 } |
|
202 |
|
203 /** |
|
204 @param aTimeOffset |
|
205 @param aOffsetMinutes |
|
206 */ |
|
207 void TInternetDateParser::ParseTimeOffsetL(const TDesC8& aTimeOffset, TTimeIntervalMinutes& aOffsetMinutes) |
|
208 { |
|
209 TInt offsetMinutes=0; |
|
210 TInt offsetTextLength = aTimeOffset.Length(); |
|
211 if (offsetTextLength > 1) |
|
212 { |
|
213 // check second letter to see if UT, GMT |
|
214 // and to differentiate between Standard and Daylight Savings |
|
215 switch (aTimeOffset[1]) |
|
216 { |
|
217 case 'M' : // GMT |
|
218 case 'T' : // UT |
|
219 { |
|
220 return; |
|
221 } |
|
222 case 'S': // Standard Time (Daylight Savings Time is one hour ahead); |
|
223 offsetMinutes -= 60; |
|
224 break; |
|
225 case 'D' : // Daylight Savings Time |
|
226 break; |
|
227 default: |
|
228 // Must be either corrupt or an explicit HHMM +/- offset |
|
229 ParseHHMMTimeOffsetL(aTimeOffset, aOffsetMinutes); |
|
230 return; |
|
231 }; |
|
232 |
|
233 // Check first letter to find out if its Eastern, Central, Mountain or Pacific Time |
|
234 switch (aTimeOffset[0]) |
|
235 { |
|
236 case 'E' : // EST or EDT -4 |
|
237 offsetMinutes -=240; |
|
238 break; |
|
239 case 'C' : // CST or CDT -5 |
|
240 offsetMinutes -=300; |
|
241 break; |
|
242 case 'M' : // MST or MDT -6 |
|
243 offsetMinutes -= 360; |
|
244 break; |
|
245 case 'P' : // PST or PDT -7 |
|
246 offsetMinutes -=420; |
|
247 break; |
|
248 default: |
|
249 // either the format is invalid or it is a non standard 3 letter timezone |
|
250 // in either case |
|
251 return; |
|
252 }; |
|
253 } |
|
254 else // This must be a military single letter format time zone |
|
255 // According to RFC2882 These Military Timezones are obsolete. Also since A can be either +1 or -1 depending on the specification |
|
256 // it should be ignored unless there is additional Out of band Information |
|
257 { |
|
258 return; // Do nothing |
|
259 } |
|
260 |
|
261 aOffsetMinutes = offsetMinutes; |
|
262 } |
|
263 |
|
264 /** |
|
265 @param aDateTimeText |
|
266 @param aDateTime |
|
267 */ |
|
268 void TInternetDateParser::ParseHHMMSSTimeL(const TDesC8& aDateTimeText, TDateTime& aDateTime) |
|
269 { |
|
270 if (aDateTimeText.Length() < KHHMMSSFormatLength) // Must be at least HH:MM:SS |
|
271 User::Leave(KErrCorrupt); |
|
272 TInt num=0; |
|
273 TInt consumed = InetProtTextUtils::ConvertDescriptorToInt(aDateTimeText, num); |
|
274 if (consumed != 2 || num < 0 || num > 23) |
|
275 User::Leave(KErrCorrupt); |
|
276 TPtrC8 dateTime(aDateTimeText.Right(aDateTimeText.Length() -3)); // Remove HH: |
|
277 aDateTime.SetHour(num); |
|
278 |
|
279 consumed = InetProtTextUtils::ConvertDescriptorToInt(dateTime, num); |
|
280 if (consumed != 2 || num < 0 || num > 59) |
|
281 User::Leave(KErrCorrupt); |
|
282 dateTime.Set(dateTime.Right(dateTime.Length() -3)); // Remove MM: |
|
283 aDateTime.SetMinute(num); |
|
284 |
|
285 consumed = InetProtTextUtils::ConvertDescriptorToInt(dateTime, num); |
|
286 if (consumed != 2 || num < 0 || num > 59) |
|
287 User::Leave(KErrCorrupt); |
|
288 aDateTime.SetSecond(num); |
|
289 } |
|
290 |
|
291 /** |
|
292 @param aDateTimeText |
|
293 @param aDateTime |
|
294 @return |
|
295 */ |
|
296 TInt TInternetDateParser::ParseRfcDateDayMonthYearL(const TDesC8& aDateTimeText, TDateTime& aDateTime) |
|
297 { |
|
298 // Parse RFC 1123 and RFC 850 style date month year parts |
|
299 // 06 Nov 1994 (RFC 1123) |
|
300 // 06-Nov-94 (RFC 850) In this case if YY < 50 then treat as 20YY otherwise 19YY |
|
301 TInt day =0; |
|
302 TInt consumed = InetProtTextUtils::ConvertDescriptorToInt(aDateTimeText,day); |
|
303 if ((consumed !=1 && consumed != 2) || aDateTimeText.Length() <= 2) |
|
304 User::Leave(KErrCorrupt); |
|
305 |
|
306 --day; // Symbian OS month days start from 0 |
|
307 |
|
308 // Skip over DD and separator |
|
309 TPtrC8 dateTimeText(aDateTimeText.Right(aDateTimeText.Length()-(consumed+1))); |
|
310 TMonth month = GetMonthL(dateTimeText); |
|
311 // Skip over MMM and separator |
|
312 const TInt KConsumedMonthLength = 4; |
|
313 TInt rightLength = dateTimeText.Length() - KConsumedMonthLength ; |
|
314 if( rightLength < 0) |
|
315 { |
|
316 User::Leave(KErrCorrupt); |
|
317 } |
|
318 dateTimeText.Set(dateTimeText.Right(rightLength)); |
|
319 |
|
320 // Now get year |
|
321 TInt year; |
|
322 consumed = InetProtTextUtils::ConvertDescriptorToInt(dateTimeText, year); |
|
323 if (consumed != 4 && consumed !=2) |
|
324 User::Leave(KErrCorrupt); |
|
325 |
|
326 // check for YY and adjust |
|
327 if (consumed == 2) |
|
328 year += year > 50 ? 1900 : 2000; |
|
329 |
|
330 if (aDateTime.Set(year, month, day, 0, 0, 0, 0) != KErrNone) |
|
331 User::Leave(KErrCorrupt); |
|
332 |
|
333 return dateTimeText.Length() - (consumed +1); |
|
334 } |
|
335 |
|
336 /** |
|
337 @param aMonthText |
|
338 @return |
|
339 */ |
|
340 TMonth TInternetDateParser::GetMonthL(const TDesC8& aMonthText) |
|
341 { |
|
342 if (aMonthText.Length() < 3) |
|
343 User::Leave(KErrCorrupt); |
|
344 |
|
345 switch (aMonthText[0]) |
|
346 { |
|
347 case 'J': // Jan, Jun, Jul |
|
348 return (aMonthText[1] == 'a' ? EJanuary : aMonthText[2] == 'n' ? EJune : EJuly); |
|
349 case 'F': // Feb |
|
350 return EFebruary; |
|
351 case 'M': // Mar, May |
|
352 return (aMonthText[2] == 'r' ? EMarch : EMay); |
|
353 case 'A': // Apr, Aug |
|
354 return (aMonthText[1] == 'p' ? EApril : EAugust); |
|
355 case 'S': // Sep |
|
356 return ESeptember; |
|
357 case 'O': // Oct |
|
358 return EOctober; |
|
359 case 'N': // Nov |
|
360 return ENovember; |
|
361 case 'D': // Dec |
|
362 return EDecember; |
|
363 default: |
|
364 User::Leave(KErrCorrupt); |
|
365 }; |
|
366 return EJanuary; |
|
367 } |
|
368 |
|
369 |
|
370 /** |
|
371 @param aAscTimeDateTime |
|
372 @param aDateTime |
|
373 */ |
|
374 void TInternetDateParser::ConvertFromAscTimeFormL(const TDesC8& aAscTimeDateTime, TDateTime& aDateTime) |
|
375 { |
|
376 // Convert a date in ascTime format ( Sun Nov 6 08:49:37 1994 ) |
|
377 |
|
378 const TChar KSpace = ' '; |
|
379 |
|
380 // First remove the wkDay |
|
381 TPtrC8 dateTime(aAscTimeDateTime); |
|
382 InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveBoth); |
|
383 TInt endOfWkDayLoc = dateTime.Locate(KSpace); |
|
384 if (endOfWkDayLoc != 3) // Must be following a wkDay "Sun " |
|
385 User::Leave(KErrCorrupt); |
|
386 dateTime.Set(dateTime.Right(dateTime.Length() - 3)); |
|
387 InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); |
|
388 // Now parse the month then skip over MMM |
|
389 TMonth month = GetMonthL(dateTime); |
|
390 dateTime.Set(dateTime.Right(dateTime.Length() - 3)); |
|
391 InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); |
|
392 // Now parse the monthDay |
|
393 TInt day = 0; |
|
394 TInt consumed = InetProtTextUtils::ConvertDescriptorToInt(dateTime, day); |
|
395 if (consumed < 0) |
|
396 User::Leave(KErrCorrupt); |
|
397 |
|
398 dateTime.Set(dateTime.Right(dateTime.Length() - consumed)); |
|
399 InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); |
|
400 |
|
401 // Now get the time |
|
402 ParseHHMMSSTimeL(dateTime, aDateTime); |
|
403 dateTime.Set(dateTime.Right(dateTime.Length() - KHHMMSSFormatLength)); |
|
404 // Now the year |
|
405 InetProtTextUtils::RemoveWhiteSpace(dateTime, InetProtTextUtils::ERemoveLeft); |
|
406 TInt year; |
|
407 consumed = InetProtTextUtils::ConvertDescriptorToInt(dateTime, year); |
|
408 if (consumed !=4) |
|
409 User::Leave(KErrCorrupt); |
|
410 |
|
411 if (aDateTime.Set(year, month, day-1, aDateTime.Hour(), aDateTime.Minute(), aDateTime.Second(), 0) != KErrNone) |
|
412 User::Leave(KErrCorrupt); |
|
413 } |