|
1 /**************************************************************************** |
|
2 ** |
|
3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). |
|
4 ** All rights reserved. |
|
5 ** Contact: Nokia Corporation (qt-info@nokia.com) |
|
6 ** |
|
7 ** This file is part of the Qt Mobility Components. |
|
8 ** |
|
9 ** $QT_BEGIN_LICENSE:LGPL$ |
|
10 ** No Commercial Usage |
|
11 ** This file contains pre-release code and may not be distributed. |
|
12 ** You may use this file in accordance with the terms and conditions |
|
13 ** contained in the Technology Preview License Agreement accompanying |
|
14 ** this package. |
|
15 ** |
|
16 ** GNU Lesser General Public License Usage |
|
17 ** Alternatively, this file may be used under the terms of the GNU Lesser |
|
18 ** General Public License version 2.1 as published by the Free Software |
|
19 ** Foundation and appearing in the file LICENSE.LGPL included in the |
|
20 ** packaging of this file. Please review the following information to |
|
21 ** ensure the GNU Lesser General Public License version 2.1 requirements |
|
22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
|
23 ** |
|
24 ** In addition, as a special exception, Nokia gives you certain additional |
|
25 ** rights. These rights are described in the Nokia Qt LGPL Exception |
|
26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
|
27 ** |
|
28 ** If you have questions regarding the use of this file, please contact |
|
29 ** Nokia at qt-info@nokia.com. |
|
30 ** |
|
31 ** |
|
32 ** |
|
33 ** |
|
34 ** |
|
35 ** |
|
36 ** |
|
37 ** |
|
38 ** $QT_END_LICENSE$ |
|
39 ** |
|
40 ****************************************************************************/ |
|
41 |
|
42 #include "qmailtimestamp.h" |
|
43 |
|
44 #include <QDate> |
|
45 #include <QStringList> |
|
46 #include <QTime> |
|
47 |
|
48 #include <stdlib.h> |
|
49 |
|
50 |
|
51 class QMailTimeStampPrivate : public QSharedData |
|
52 { |
|
53 public: |
|
54 QMailTimeStampPrivate(); |
|
55 explicit QMailTimeStampPrivate(const QString& timeText); |
|
56 explicit QMailTimeStampPrivate(const QDateTime& dateTime); |
|
57 QMailTimeStampPrivate(const QMailTimeStampPrivate& other); |
|
58 ~QMailTimeStampPrivate(); |
|
59 |
|
60 const QMailTimeStampPrivate& operator= (const QMailTimeStampPrivate& other); |
|
61 |
|
62 QString toString(QMailTimeStamp::OutputFormat format) const; |
|
63 |
|
64 QDateTime toLocalTime() const; |
|
65 QDateTime toUTC() const; |
|
66 |
|
67 bool isNull() const; |
|
68 bool isValid() const; |
|
69 |
|
70 bool operator== (const QMailTimeStampPrivate& other) const; |
|
71 bool operator!= (const QMailTimeStampPrivate& other) const; |
|
72 |
|
73 bool operator< (const QMailTimeStampPrivate& other) const; |
|
74 bool operator<= (const QMailTimeStampPrivate& other) const; |
|
75 |
|
76 bool operator> (const QMailTimeStampPrivate& other) const; |
|
77 bool operator>= (const QMailTimeStampPrivate& other) const; |
|
78 |
|
79 private: |
|
80 // Make these private - since they must be accessed through a smart pointer from the |
|
81 // owner class, it's better if all access is restricted to this class |
|
82 QDateTime time; |
|
83 int utcOffset; |
|
84 }; |
|
85 |
|
86 QMailTimeStampPrivate::QMailTimeStampPrivate() |
|
87 : utcOffset(0) |
|
88 { |
|
89 } |
|
90 |
|
91 QMailTimeStampPrivate::QMailTimeStampPrivate(const QString& timeText) |
|
92 : utcOffset(0) |
|
93 { |
|
94 int month = -1, day = -1, year = -1; |
|
95 bool ok; |
|
96 QString timeStr, offsetStr; |
|
97 |
|
98 // Remove any comments from the text |
|
99 QString uncommented; |
|
100 uncommented.reserve( timeText.length() ); |
|
101 |
|
102 int commentDepth = 0; |
|
103 bool escaped = false; |
|
104 |
|
105 const QChar* it = timeText.constData(); |
|
106 const QChar* end = it + timeText.length(); |
|
107 for ( ; it != end; ++it ) { |
|
108 if ( !escaped && ( *it == '\\' ) ) { |
|
109 escaped = true; |
|
110 continue; |
|
111 } |
|
112 |
|
113 if ( *it == '(' && !escaped ) |
|
114 commentDepth += 1; |
|
115 else if ( *it == ')' && !escaped && ( commentDepth > 0 ) ) |
|
116 commentDepth -= 1; |
|
117 else if ( commentDepth == 0 ) { |
|
118 // Remove characters we don't want |
|
119 if ( *it != ',' && *it != '\n' && *it != '\r' ) |
|
120 uncommented.append( *it ); |
|
121 } |
|
122 |
|
123 escaped = false; |
|
124 } |
|
125 |
|
126 // Extract the date/time elements |
|
127 uncommented = uncommented.trimmed(); |
|
128 QStringList tokens = uncommented.split(' ', QString::SkipEmptyParts); |
|
129 |
|
130 int tokenCount = tokens.count(); |
|
131 if ( tokenCount > 0 ) { |
|
132 static const QString Days("sunmontuewedthufrisat"); |
|
133 if (Days.indexOf(tokens[0].left(3).toLower()) != -1) { |
|
134 tokens.removeAt(0); |
|
135 tokenCount -= 1; |
|
136 } |
|
137 if (tokenCount > 0) { |
|
138 int value = tokens[0].toInt(&ok); |
|
139 if ( ok ) |
|
140 day = value; |
|
141 } |
|
142 } |
|
143 if ( tokenCount > 1 ) { |
|
144 static const QString Months("janfebmaraprmayjunjulaugsepoctnovdec"); |
|
145 int value = Months.indexOf( tokens[1].left( 3 ).toLower() ); |
|
146 if ( value != -1 ) |
|
147 month = (value + 3) / 3; |
|
148 } |
|
149 if ( tokenCount > 2 ) { |
|
150 int value = tokens[2].toInt(&ok); |
|
151 if ( ok ) { |
|
152 // Interpret year according to RFC2822 |
|
153 year = value; |
|
154 if ( year < 100 ) { |
|
155 year += ( year <= 49 ? 2000 : 1900 ); |
|
156 } |
|
157 else if ( year < 1000 ) { |
|
158 year += 1900; |
|
159 } |
|
160 } |
|
161 } |
|
162 if ( tokenCount > 3 ) { |
|
163 timeStr = tokens[3].trimmed(); |
|
164 } |
|
165 if ( tokenCount > 4 ) { |
|
166 offsetStr = tokens[4].trimmed(); |
|
167 } |
|
168 |
|
169 // Parse the text into UTC-adjusted time part and UTC-offset indicator |
|
170 if ( (day != -1) && (month != -1) && (year != -1) ) { |
|
171 QDate dateComponent(year, month, day); |
|
172 QTime timeComponent; |
|
173 |
|
174 QTime parsedTime; |
|
175 if ( timeStr.length() == 8 ) { |
|
176 parsedTime = QTime::fromString( timeStr, "hh:mm:ss" ); |
|
177 } |
|
178 else if ( timeStr.length() == 5 ) { |
|
179 // Is this legal? Either way, it seems desirable for robustness... |
|
180 parsedTime = QTime::fromString( timeStr, "hh:mm" ); |
|
181 } |
|
182 if ( parsedTime.isValid() ) |
|
183 timeComponent = parsedTime; |
|
184 |
|
185 time = QDateTime( dateComponent, timeComponent, Qt::UTC ); |
|
186 |
|
187 if ( offsetStr.length() == 5 ) { |
|
188 int h = offsetStr.left(3).toInt(&ok); |
|
189 if ( ok ) { |
|
190 int m = offsetStr.right(2).toInt(&ok); |
|
191 if ( ok ) { |
|
192 utcOffset = ( h * 3600 ) + ( m * 60 * ( h < 0 ? -1 : 1 ) ); |
|
193 |
|
194 time = time.addSecs( -utcOffset ); |
|
195 } |
|
196 } |
|
197 } |
|
198 } |
|
199 } |
|
200 |
|
201 QMailTimeStampPrivate::QMailTimeStampPrivate(const QDateTime& dateTime) |
|
202 { |
|
203 // Store the time as UTC |
|
204 if ( dateTime.timeSpec() == Qt::LocalTime ) { |
|
205 QDateTime original(dateTime); |
|
206 original.setTimeSpec( Qt::UTC ); |
|
207 |
|
208 time = dateTime.toUTC(); |
|
209 |
|
210 // Find the difference |
|
211 utcOffset = time.secsTo( original ); |
|
212 } |
|
213 else { |
|
214 // Time is already in UTC |
|
215 time = dateTime; |
|
216 |
|
217 // Is this the right thing to do? What does it mean if we get a UTC timestamp? |
|
218 utcOffset = 0; |
|
219 } |
|
220 |
|
221 // Since we can't include milliseconds in our textual representation, and we |
|
222 // need to ensure the following works: |
|
223 // assert( someTimeStamp == QMailTimeStamp(someTimeStamp.toString()) ) |
|
224 // then we need to make sure that we have no sub-second component |
|
225 qint64 ms = time.time().msec(); |
|
226 if (ms != 0) |
|
227 time = time.addMSecs(0 - ms); |
|
228 } |
|
229 |
|
230 QMailTimeStampPrivate::QMailTimeStampPrivate(const QMailTimeStampPrivate& other) |
|
231 : QSharedData(other) |
|
232 { |
|
233 this->operator=(other); |
|
234 } |
|
235 |
|
236 QMailTimeStampPrivate::~QMailTimeStampPrivate() |
|
237 { |
|
238 } |
|
239 |
|
240 const QMailTimeStampPrivate& QMailTimeStampPrivate::operator= (const QMailTimeStampPrivate& other) |
|
241 { |
|
242 time = other.time; |
|
243 utcOffset = other.utcOffset; |
|
244 return *this; |
|
245 } |
|
246 |
|
247 QString QMailTimeStampPrivate::toString(QMailTimeStamp::OutputFormat format) const |
|
248 { |
|
249 // We can't use QDateTime to provide day and month names, since they may get localized into UTF-8 |
|
250 static const char Days[] = "MonTueWedThuFriSatSun"; |
|
251 static const char Months[] = "JanFebMarAprMayJunJulAugSepOctNovDec"; |
|
252 |
|
253 if (time.isNull() || !time.isValid()) |
|
254 return QString(); |
|
255 |
|
256 QString result; |
|
257 |
|
258 QDateTime originalTime = time.addSecs( utcOffset ); |
|
259 QDate originalDate = originalTime.date(); |
|
260 |
|
261 int hOffset = utcOffset / 3600; |
|
262 int mOffset = ( abs(utcOffset) - abs(hOffset * 3600) ) / 60; |
|
263 |
|
264 if (format == QMailTimeStamp::Rfc2822) { |
|
265 result = QString( originalTime.toString( "%1, d %2 yyyy hh:mm:ss %3" ) ); |
|
266 result = result.arg( QString::fromAscii( Days + ( originalDate.dayOfWeek() - 1 ) * 3, 3 ) ); |
|
267 result = result.arg( QString::fromAscii( Months + ( originalDate.month() - 1 ) * 3, 3 ) ); |
|
268 result = result.arg( QString().sprintf( "%+.2d%.2d", hOffset, mOffset ) ); |
|
269 } else if (format == QMailTimeStamp::Rfc3501) { |
|
270 result = QString( originalTime.toString( "dd-%1-yyyy hh:mm:ss %2" ) ); |
|
271 result = result.arg( QString::fromAscii( Months + ( originalDate.month() - 1 ) * 3, 3 ) ); |
|
272 result = result.arg( QString().sprintf( "%+.2d%.2d", hOffset, mOffset ) ); |
|
273 |
|
274 // The day number should be space-padded |
|
275 if (result[0] == '0') { |
|
276 result[0] = ' '; |
|
277 } |
|
278 } else if (format == QMailTimeStamp::Rfc3339) { |
|
279 result = QString( originalTime.toString( "yyyy-MM-ddThh:mm:ss%1" ) ); |
|
280 result = result.arg( utcOffset == 0 ? QString("Z") : QString().sprintf( "%+.2d:%.2d", hOffset, mOffset ) ); |
|
281 } |
|
282 |
|
283 return result; |
|
284 } |
|
285 |
|
286 QDateTime QMailTimeStampPrivate::toLocalTime() const |
|
287 { |
|
288 return time.toLocalTime(); |
|
289 } |
|
290 |
|
291 QDateTime QMailTimeStampPrivate::toUTC() const |
|
292 { |
|
293 return time; |
|
294 } |
|
295 |
|
296 bool QMailTimeStampPrivate::isNull() const |
|
297 { |
|
298 return time.isNull(); |
|
299 } |
|
300 |
|
301 bool QMailTimeStampPrivate::isValid() const |
|
302 { |
|
303 return time.isValid(); |
|
304 } |
|
305 |
|
306 bool QMailTimeStampPrivate::operator== (const QMailTimeStampPrivate& other) const |
|
307 { |
|
308 return ( toUTC() == other.toUTC() ); |
|
309 } |
|
310 |
|
311 bool QMailTimeStampPrivate::operator!= (const QMailTimeStampPrivate& other) const |
|
312 { |
|
313 return !operator==(other); |
|
314 } |
|
315 |
|
316 bool QMailTimeStampPrivate::operator< (const QMailTimeStampPrivate& other) const |
|
317 { |
|
318 return ( toUTC() < other.toUTC() ); |
|
319 } |
|
320 |
|
321 bool QMailTimeStampPrivate::operator<= (const QMailTimeStampPrivate& other) const |
|
322 { |
|
323 return !operator>(other); |
|
324 } |
|
325 |
|
326 bool QMailTimeStampPrivate::operator> (const QMailTimeStampPrivate& other) const |
|
327 { |
|
328 return ( toUTC() > other.toUTC() ); |
|
329 } |
|
330 |
|
331 bool QMailTimeStampPrivate::operator>= (const QMailTimeStampPrivate& other) const |
|
332 { |
|
333 return !operator<(other); |
|
334 } |
|
335 |
|
336 |
|
337 /*! |
|
338 \class QMailTimeStamp |
|
339 |
|
340 \brief The QMailTimeStamp class manages message time stamps. |
|
341 \ingroup messaginglibrary |
|
342 |
|
343 QMailTimeStamp provides functions for creating and manipulating the time stamps of messages. |
|
344 QMailTimeStamp can be created from time stamp strings, or from QDateTime objects. The |
|
345 time stamp information can be extracted in UTC time, local time, or as a formatted |
|
346 string. |
|
347 |
|
348 QMailTimeStamp maintains the timezone information of a time stamp, so it can be used to |
|
349 convert time stamp information between UTC time and localized time values. |
|
350 |
|
351 \sa QDateTime, QMailMessage |
|
352 */ |
|
353 |
|
354 /*! |
|
355 \enum QMailTimeStamp::OutputFormat |
|
356 |
|
357 This enum type is used to select a format for timestamp output. |
|
358 |
|
359 \value Rfc2822 The format used in SMTP message format; example: "Wed, 17 May 2006 20:45:00 +0100". |
|
360 \value Rfc3501 The format used in IMAP message append; example: "17-May-2006 20:45:00 +0100". |
|
361 \value Rfc3339 The format specified for future protocols (a variant of ISO 8601); example: "2006-05-17T20:45:00+01:00". |
|
362 */ |
|
363 |
|
364 /*! |
|
365 Returns a QMailTimeStamp object initialised to contain the current |
|
366 date and time, in the local time zone. |
|
367 */ |
|
368 QMailTimeStamp QMailTimeStamp::currentDateTime() |
|
369 { |
|
370 return QMailTimeStamp(QDateTime::currentDateTime()); |
|
371 } |
|
372 |
|
373 /*! |
|
374 Constructs a null QMailTimeStamp object. A null timestamp is invalid. |
|
375 */ |
|
376 QMailTimeStamp::QMailTimeStamp() |
|
377 { |
|
378 d = new QMailTimeStampPrivate(); |
|
379 } |
|
380 |
|
381 /*! \internal */ |
|
382 QMailTimeStamp::QMailTimeStamp(const QMailTimeStamp& other) |
|
383 { |
|
384 this->operator=(other); |
|
385 } |
|
386 |
|
387 /*! |
|
388 Constructs a QMailTimeStamp object by parsing \a timeText. |
|
389 */ |
|
390 QMailTimeStamp::QMailTimeStamp(const QString& timeText) |
|
391 { |
|
392 d = new QMailTimeStampPrivate(timeText); |
|
393 } |
|
394 |
|
395 /*! |
|
396 Constructs a QMailTimeStamp object from the given \a dateTime. |
|
397 */ |
|
398 QMailTimeStamp::QMailTimeStamp(const QDateTime& dateTime) |
|
399 { |
|
400 d = new QMailTimeStampPrivate(dateTime); |
|
401 } |
|
402 |
|
403 /*! |
|
404 Destroys a QMailTimeStamp object. |
|
405 */ |
|
406 QMailTimeStamp::~QMailTimeStamp() |
|
407 { |
|
408 } |
|
409 |
|
410 /*! \internal */ |
|
411 const QMailTimeStamp& QMailTimeStamp::operator= (const QMailTimeStamp& other) |
|
412 { |
|
413 d = other.d; |
|
414 return *this; |
|
415 } |
|
416 |
|
417 /*! |
|
418 Returns the time stamp information in the format specified by \a format. |
|
419 */ |
|
420 QString QMailTimeStamp::toString(QMailTimeStamp::OutputFormat format) const |
|
421 { |
|
422 return d->toString(format); |
|
423 } |
|
424 |
|
425 /*! |
|
426 Returns a QDateTime containing the time stamp converted to the local time zone. |
|
427 */ |
|
428 QDateTime QMailTimeStamp::toLocalTime() const |
|
429 { |
|
430 return d->toLocalTime(); |
|
431 } |
|
432 |
|
433 /*! |
|
434 Returns a QDateTime containing the time stamp in UTC time. |
|
435 */ |
|
436 QDateTime QMailTimeStamp::toUTC() const |
|
437 { |
|
438 return d->toUTC(); |
|
439 } |
|
440 |
|
441 /*! |
|
442 Returns true if the timestamp has not been initialized to contain a value. |
|
443 */ |
|
444 bool QMailTimeStamp::isNull() const |
|
445 { |
|
446 return d->isNull(); |
|
447 } |
|
448 |
|
449 /*! |
|
450 Returns true if the timestamp is valid; otherwise returns false; |
|
451 */ |
|
452 bool QMailTimeStamp::isValid() const |
|
453 { |
|
454 return d->isValid(); |
|
455 } |
|
456 |
|
457 /*! |
|
458 Returns true if this time stamp is equal to \a other; otherwise returns false. |
|
459 |
|
460 \sa operator!=() |
|
461 */ |
|
462 bool QMailTimeStamp::operator== (const QMailTimeStamp& other) const |
|
463 { |
|
464 return d->operator==(*other.d); |
|
465 } |
|
466 |
|
467 /*! |
|
468 Returns true if this time stamp is different from \a other; otherwise returns false. |
|
469 |
|
470 Two time stamps are different if either the date, the time, or the time zone components are different. |
|
471 |
|
472 \sa operator==() |
|
473 */ |
|
474 bool QMailTimeStamp::operator!= (const QMailTimeStamp& other) const |
|
475 { |
|
476 return d->operator!=(*other.d); |
|
477 } |
|
478 |
|
479 /*! |
|
480 Returns true if this time stamp is earlier than \a other; otherwise returns false. |
|
481 */ |
|
482 bool QMailTimeStamp::operator< (const QMailTimeStamp& other) const |
|
483 { |
|
484 return d->operator<(*other.d); |
|
485 } |
|
486 |
|
487 /*! |
|
488 Returns true if this time stamp is earlier than or equal to \a other; otherwise returns false. |
|
489 */ |
|
490 bool QMailTimeStamp::operator<= (const QMailTimeStamp& other) const |
|
491 { |
|
492 return d->operator<=(*other.d); |
|
493 } |
|
494 |
|
495 /*! |
|
496 Returns true if this time stamp is later than \a other; otherwise returns false. |
|
497 */ |
|
498 bool QMailTimeStamp::operator> (const QMailTimeStamp& other) const |
|
499 { |
|
500 return d->operator>(*other.d); |
|
501 } |
|
502 |
|
503 /*! |
|
504 Returns true if this time stamp is later than or equal to \a other; otherwise returns false. |
|
505 */ |
|
506 bool QMailTimeStamp::operator>= (const QMailTimeStamp& other) const |
|
507 { |
|
508 return d->operator>=(*other.d); |
|
509 } |
|
510 |