Qwt User's Guide: qwt_date.cpp Source File

Qwt User's Guide 6.2.0

qwt_date.cpp
1 /******************************************************************************
2 * Qwt Widget Library
3 * Copyright (C) 1997 Josef Wilgen
4 * Copyright (C) 2002 Uwe Rathmann
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the Qwt License, Version 1.0
8 *****************************************************************************/
9
10 #include "qwt_date.h"
11 #include "qwt_math.h"
12
13 #include <qdebug.h>
14 #include <qlocale.h>
15
16 #include <limits>
17
18 #if QT_VERSION >= 0x050000
19
20 typedef qint64 QwtJulianDay;
21 static const QwtJulianDay minJulianDayD = Q_INT64_C( -784350574879 );
22 static const QwtJulianDay maxJulianDayD = Q_INT64_C( 784354017364 );
23
24 #else
25
26 // QDate stores the Julian day as unsigned int, but
27 // there is QDate::fromJulianDay( int ). That's why
28 // we have the range [ 1, INT_MAX ]
29
30 typedef int QwtJulianDay;
31 static const QwtJulianDay minJulianDayD = 1;
32 static const QwtJulianDay maxJulianDayD = std::numeric_limits< int >::max();
33
34 #endif
35
36 static QString qwtExpandedFormat( const QString& format,
37 const QDateTime& dateTime, QwtDate::Week0Type week0Type )
38 {
39 const int week = QwtDate::weekNumber( dateTime.date(), week0Type );
40
41 QString weekNo;
42 weekNo.setNum( week );
43
44 QString weekNoWW;
45 if ( weekNo.length() == 1 )
46 weekNoWW += QLatin1Char( '0' );
47
48 weekNoWW += weekNo;
49
50 QString fmt = format;
51 fmt.replace( QLatin1String( "ww" ), weekNoWW );
52 fmt.replace( QLatin1Char( 'w' ), weekNo );
53
54 if ( week == 1 && dateTime.date().month() != 1 )
55 {
56 // in case of week 1, we might need to increment the year
57
58 QLatin1String s_yyyy( "yyyy" );
59 QLatin1String s_yy( "yy" );
60
61 // week 1 might start in the previous year
62
63 bool doReplaceYear = fmt.contains( s_yy );
64
65 if ( doReplaceYear )
66 {
67 if ( fmt.contains( 'M' ) )
68 {
69 // in case of also having 'M' we have a conflict about
70 // which year to show
71
72 doReplaceYear = false;
73 }
74 else
75 {
76 // in case of also having 'd' or 'dd' we have a conflict about
77 // which year to show
78
79 int numD = 0;
80
81 for ( int i = 0; i < fmt.size(); i++ )
82 {
83 if ( fmt[i] == 'd' )
84 {
85 numD++;
86 }
87 else
88 {
89 if ( numD > 0 && numD <= 2 )
90 break;
91
92 numD = 0;
93 }
94 }
95
96 if ( numD > 0 && numD <= 2 )
97 doReplaceYear = false;
98 }
99 }
100
101 if ( doReplaceYear )
102 {
103 const QDate dt( dateTime.date().year() + 1, 1, 1 );
104 const QString dtString = QLocale().toString( dt, s_yyyy );
105
106 if ( fmt.contains( s_yyyy ) )
107 {
108 fmt.replace( s_yyyy, dtString );
109 }
110 else
111 {
112 fmt.replace( s_yy, dtString );
113 }
114 }
115 }
116
117 return fmt;
118 }
119
120 static inline Qt::DayOfWeek qwtFirstDayOfWeek()
121 {
122 return QLocale().firstDayOfWeek();
123 }
124
125 static inline void qwtFloorTime(
126 QwtDate::IntervalType intervalType, QDateTime& dt )
127 {
128 // when dt is inside the special hour where DST is ending
129 // an hour is no unique. Therefore we have to
130 // use UTC time.
131
132 const Qt::TimeSpec timeSpec = dt.timeSpec();
133
134 if ( timeSpec == Qt::LocalTime )
135 dt = dt.toTimeSpec( Qt::UTC );
136
137 const QTime t = dt.time();
138 switch( intervalType )
139 {
140 case QwtDate::Second:
141 {
142 dt.setTime( QTime( t.hour(), t.minute(), t.second() ) );
143 break;
144 }
145 case QwtDate::Minute:
146 {
147 dt.setTime( QTime( t.hour(), t.minute(), 0 ) );
148 break;
149 }
150 case QwtDate::Hour:
151 {
152 dt.setTime( QTime( t.hour(), 0, 0 ) );
153 break;
154 }
155 default:
156 break;
157 }
158
159 if ( timeSpec == Qt::LocalTime )
160 dt = dt.toTimeSpec( Qt::LocalTime );
161 }
162
163 static inline QDateTime qwtToTimeSpec(
164 const QDateTime& dt, Qt::TimeSpec spec )
165 {
166 if ( dt.timeSpec() == spec )
167 return dt;
168
169 const qint64 jd = dt.date().toJulianDay();
170 if ( jd < 0 || jd >= std::numeric_limits< int >::max() )
171 {
172 // the conversion between local time and UTC
173 // is internally limited. To avoid
174 // overflows we simply ignore the difference
175 // for those dates
176
177 QDateTime dt2 = dt;
178 dt2.setTimeSpec( spec );
179 return dt2;
180 }
181
182 return dt.toTimeSpec( spec );
183 }
184
185 #if 0
186
187 static inline double qwtToJulianDay( int year, int month, int day )
188 {
189 // code from QDate but using doubles to avoid overflows
190 // for large values
191
192 const int m1 = ( month - 14 ) / 12;
193 const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
194 const double y1 = std::floor( ( 4900.0 + year + m1 ) / 100 );
195
196 return std::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
197 - std::floor( ( 3 * y1 ) / 4 ) + day - 32075;
198 }
199
200 static inline qint64 qwtFloorDiv64( qint64 a, int b )
201 {
202 if ( a < 0 )
203 a -= b - 1;
204
205 return a / b;
206 }
207
208 static inline qint64 qwtFloorDiv( int a, int b )
209 {
210 if ( a < 0 )
211 a -= b - 1;
212
213 return a / b;
214 }
215
216 #endif
217
218 static inline QDate qwtToDate( int year, int month = 1, int day = 1 )
219 {
220 #if QT_VERSION >= 0x050000
221 return QDate( year, month, day );
222 #else
223 if ( year > 100000 )
224 {
225 // code from QDate but using doubles to avoid overflows
226 // for large values
227
228 const int m1 = ( month - 14 ) / 12;
229 const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
230 const double y1 = std::floor( ( 4900.0 + year + m1 ) / 100 );
231
232 const double jd = std::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
233 - std::floor( ( 3 * y1 ) / 4 ) + day - 32075;
234
235 if ( jd > maxJulianDayD )
236 {
237 qWarning() << "qwtToDate: overflow";
238 return QDate();
239 }
240
241 return QDate::fromJulianDay( static_cast< QwtJulianDay >( jd ) );
242 }
243 else
244 {
245 return QDate( year, month, day );
246 }
247 #endif
248 }
249
261 QDateTime QwtDate::toDateTime( double value, Qt::TimeSpec timeSpec )
262 {
263 const int msecsPerDay = 86400000;
264
265 const double days = static_cast< qint64 >( std::floor( value / msecsPerDay ) );
266
267 const double jd = QwtDate::JulianDayForEpoch + days;
268 if ( ( jd > maxJulianDayD ) || ( jd < minJulianDayD ) )
269 {
270 qWarning() << "QwtDate::toDateTime: overflow";
271 return QDateTime();
272 }
273
274 const QDate d = QDate::fromJulianDay( static_cast< QwtJulianDay >( jd ) );
275
276 const int msecs = static_cast< int >( value - days * msecsPerDay );
277
278 static const QTime timeNull( 0, 0, 0, 0 );
279
280 QDateTime dt( d, timeNull.addMSecs( msecs ), Qt::UTC );
281
282 if ( timeSpec == Qt::LocalTime )
283 dt = qwtToTimeSpec( dt, timeSpec );
284
285 return dt;
286 }
287
298 double QwtDate::toDouble( const QDateTime& dateTime )
299 {
300 const int msecsPerDay = 86400000;
301
302 const QDateTime dt = qwtToTimeSpec( dateTime, Qt::UTC );
303
304 const double days = dt.date().toJulianDay() - QwtDate::JulianDayForEpoch;
305
306 const QTime time = dt.time();
307 const double secs = 3600.0 * time.hour() +
308 60.0 * time.minute() + time.second();
309
310 return days * msecsPerDay + time.msec() + 1000.0 * secs;
311 }
312
323 QDateTime QwtDate::ceil( const QDateTime& dateTime, IntervalType intervalType )
324 {
325 if ( dateTime.date() >= QwtDate::maxDate() )
326 return dateTime;
327
328 QDateTime dt = dateTime;
329
330 switch ( intervalType )
331 {
332 case QwtDate::Millisecond:
333 {
334 break;
335 }
336 case QwtDate::Second:
337 {
338 qwtFloorTime( QwtDate::Second, dt );
339 if ( dt < dateTime )
340 dt = dt.addSecs( 1 );
341
342 break;
343 }
344 case QwtDate::Minute:
345 {
346 qwtFloorTime( QwtDate::Minute, dt );
347 if ( dt < dateTime )
348 dt = dt.addSecs( 60 );
349
350 break;
351 }
352 case QwtDate::Hour:
353 {
354 qwtFloorTime( QwtDate::Hour, dt );
355 if ( dt < dateTime )
356 dt = dt.addSecs( 3600 );
357
358 break;
359 }
360 case QwtDate::Day:
361 {
362 dt.setTime( QTime( 0, 0 ) );
363 if ( dt < dateTime )
364 dt = dt.addDays( 1 );
365
366 break;
367 }
368 case QwtDate::Week:
369 {
370 dt.setTime( QTime( 0, 0 ) );
371 if ( dt < dateTime )
372 dt = dt.addDays( 1 );
373
374 int days = qwtFirstDayOfWeek() - dt.date().dayOfWeek();
375 if ( days < 0 )
376 days += 7;
377
378 dt = dt.addDays( days );
379
380 break;
381 }
382 case QwtDate::Month:
383 {
384 dt.setTime( QTime( 0, 0 ) );
385 dt.setDate( qwtToDate( dateTime.date().year(),
386 dateTime.date().month() ) );
387
388 if ( dt < dateTime )
389 dt = dt.addMonths( 1 );
390
391 break;
392 }
393 case QwtDate::Year:
394 {
395 dt.setTime( QTime( 0, 0 ) );
396
397 const QDate d = dateTime.date();
398
399 int year = d.year();
400 if ( d.month() > 1 || d.day() > 1 || !dateTime.time().isNull() )
401 year++;
402
403 if ( year == 0 )
404 year++; // there is no year 0
405
406 dt.setDate( qwtToDate( year ) );
407 break;
408 }
409 }
410
411 return dt;
412 }
413
425 QDateTime QwtDate::floor( const QDateTime& dateTime,
426 IntervalType intervalType )
427 {
428 if ( dateTime.date() <= QwtDate::minDate() )
429 return dateTime;
430
431 QDateTime dt = dateTime;
432
433 switch ( intervalType )
434 {
435 case QwtDate::Millisecond:
436 {
437 break;
438 }
439 case QwtDate::Second:
440 case QwtDate::Minute:
441 case QwtDate::Hour:
442 {
443 qwtFloorTime( intervalType, dt );
444 break;
445 }
446 case QwtDate::Day:
447 {
448 dt.setTime( QTime( 0, 0 ) );
449 break;
450 }
451 case QwtDate::Week:
452 {
453 dt.setTime( QTime( 0, 0 ) );
454
455 int days = dt.date().dayOfWeek() - qwtFirstDayOfWeek();
456 if ( days < 0 )
457 days += 7;
458
459 dt = dt.addDays( -days );
460
461 break;
462 }
463 case QwtDate::Month:
464 {
465 dt.setTime( QTime( 0, 0 ) );
466
467 const QDate date = qwtToDate( dt.date().year(),
468 dt.date().month() );
469 dt.setDate( date );
470
471 break;
472 }
473 case QwtDate::Year:
474 {
475 dt.setTime( QTime( 0, 0 ) );
476
477 const QDate date = qwtToDate( dt.date().year() );
478 dt.setDate( date );
479
480 break;
481 }
482 }
483
484 return dt;
485 }
486
499 QDate QwtDate::minDate()
500 {
501 static QDate date;
502 if ( !date.isValid() )
503 date = QDate::fromJulianDay( minJulianDayD );
504
505 return date;
506 }
507
521 QDate QwtDate::maxDate()
522 {
523 static QDate date;
524 if ( !date.isValid() )
525 date = QDate::fromJulianDay( maxJulianDayD );
526
527 return date;
528 }
529
542 QDate QwtDate::dateOfWeek0( int year, Week0Type type )
543 {
544 const Qt::DayOfWeek firstDayOfWeek = qwtFirstDayOfWeek();
545
546 QDate dt0( year, 1, 1 );
547
548 // floor to the first day of the week
549 int days = dt0.dayOfWeek() - firstDayOfWeek;
550 if ( days < 0 )
551 days += 7;
552
553 dt0 = dt0.addDays( -days );
554
555 if ( type == QwtDate::FirstThursday )
556 {
557 // according to ISO 8601 the first week is defined
558 // by the first Thursday.
559
560 int d = Qt::Thursday - firstDayOfWeek;
561 if ( d < 0 )
562 d += 7;
563
564 if ( dt0.addDays( d ).year() < year )
565 dt0 = dt0.addDays( 7 );
566 }
567
568 return dt0;
569 }
570
585 int QwtDate::weekNumber( const QDate& date, Week0Type type )
586 {
587 int weekNo;
588
589 if ( type == QwtDate::FirstDay )
590 {
591 QDate day0;
592
593 if ( date.month() == 12 && date.day() >= 24 )
594 {
595 // week 1 usually starts in the previous years.
596 // and we have to check if we are already there
597
598 day0 = dateOfWeek0( date.year() + 1, type );
599 if ( day0.daysTo( date ) < 0 )
600 day0 = dateOfWeek0( date.year(), type );
601 }
602 else
603 {
604 day0 = dateOfWeek0( date.year(), type );
605 }
606
607 weekNo = day0.daysTo( date ) / 7 + 1;
608 }
609 else
610 {
611 weekNo = date.weekNumber();
612 }
613
614 return weekNo;
615 }
616
635 int QwtDate::utcOffset( const QDateTime& dateTime )
636 {
637 int seconds = 0;
638
639 switch( dateTime.timeSpec() )
640 {
641 case Qt::UTC:
642 {
643 break;
644 }
645 case Qt::OffsetFromUTC:
646 {
647 #if QT_VERSION >= 0x050200
648 seconds = dateTime.offsetFromUtc();
649 #else
650 seconds = dateTime.utcOffset();
651 #endif
652 break;
653 }
654 default:
655 {
656 const QDateTime dt1( dateTime.date(), dateTime.time(), Qt::UTC );
657 seconds = dateTime.secsTo( dt1 );
658 }
659 }
660
661 return seconds;
662 }
663
686 QString QwtDate::toString( const QDateTime& dateTime,
687 const QString& format, Week0Type week0Type )
688 {
689 QString fmt = format;
690 if ( fmt.contains( 'w' ) )
691 {
692 fmt = qwtExpandedFormat( fmt, dateTime, week0Type );
693 }
694
695 return QLocale().toString( dateTime, fmt );
696 }
QwtDate::floor
static QDateTime floor(const QDateTime &, IntervalType)
Definition: qwt_date.cpp:425
QwtDate::toString
static QString toString(const QDateTime &, const QString &format, Week0Type)
Definition: qwt_date.cpp:686
QwtDate::JulianDayForEpoch
@ JulianDayForEpoch
The Julian day of "The Epoch".
Definition: qwt_date.h:105
QwtDate::toDateTime
static QDateTime toDateTime(double value, Qt::TimeSpec=Qt::UTC)
Definition: qwt_date.cpp:261
QwtDate::weekNumber
static int weekNumber(const QDate &, Week0Type)
Definition: qwt_date.cpp:585
QwtDate::minDate
static QDate minDate()
Definition: qwt_date.cpp:499
QwtDate::ceil
static QDateTime ceil(const QDateTime &, IntervalType)
Definition: qwt_date.cpp:323
QwtDate::Week0Type
Week0Type
Definition: qwt_date.h:50
QwtDate::FirstThursday
@ FirstThursday
Definition: qwt_date.h:58
QwtDate::FirstDay
@ FirstDay
Definition: qwt_date.h:66
QwtDate::dateOfWeek0
static QDate dateOfWeek0(int year, Week0Type)
Date of the first day of the first week for a year.
Definition: qwt_date.cpp:542
QwtDate::IntervalType
IntervalType
Definition: qwt_date.h:76
QwtDate::Month
@ Month
The interval is related to months.
Definition: qwt_date.h:96
QwtDate::Day
@ Day
The interval is related to days.
Definition: qwt_date.h:90
QwtDate::Millisecond
@ Millisecond
The interval is related to milliseconds.
Definition: qwt_date.h:78
QwtDate::Minute
@ Minute
The interval is related to minutes.
Definition: qwt_date.h:84
QwtDate::Hour
@ Hour
The interval is related to hours.
Definition: qwt_date.h:87
QwtDate::Second
@ Second
The interval is related to seconds.
Definition: qwt_date.h:81
QwtDate::Week
@ Week
The interval is related to weeks.
Definition: qwt_date.h:93
QwtDate::Year
@ Year
The interval is related to years.
Definition: qwt_date.h:99
QwtDate::utcOffset
static int utcOffset(const QDateTime &)
Definition: qwt_date.cpp:635
QwtDate::maxDate
static QDate maxDate()
Definition: qwt_date.cpp:521
QwtDate::toDouble
static double toDouble(const QDateTime &)
Definition: qwt_date.cpp:298