Bug Summary

File:OMPlot/qwt/src/qwt_date_scale_engine.cpp
Warning:line 562, column 17
Value stored to 'minStepDays' is never read

Annotated Source Code

[?] Use j/k keys for keyboard navigation

1#include "qwt_date_scale_engine.h"
2#include "qwt_math.h"
3#include "qwt_transform.h"
4#include <qdatetime.h>
5#include <limits.h>
6
7static inline double qwtMsecsForType( QwtDate::IntervalType type )
8{
9 static const double msecs[] =
10 {
11 1.0,
12 1000.0,
13 60.0 * 1000.0,
14 3600.0 * 1000.0,
15 24.0 * 3600.0 * 1000.0,
16 7.0 * 24.0 * 3600.0 * 1000.0,
17 30.0 * 24.0 * 3600.0 * 1000.0,
18 365.0 * 24.0 * 3600.0 * 1000.0,
19 };
20
21 if ( type < 0 || type >= static_cast<int>( sizeof( msecs ) / sizeof( msecs[0] ) ) )
22 return 1.0;
23
24 return msecs[ type ];
25}
26
27static inline int qwtAlignValue(
28 double value, double stepSize, bool up )
29{
30 double d = value / stepSize;
31 d = up ? ::ceil( d ) : ::floor( d );
32
33 return static_cast<int>( d * stepSize );
34}
35
36static double qwtIntervalWidth( const QDateTime &minDate,
37 const QDateTime &maxDate, QwtDate::IntervalType intervalType )
38{
39 switch( intervalType )
40 {
41 case QwtDate::Millisecond:
42 {
43 const double secsTo = minDate.secsTo( maxDate );
44 const double msecs = maxDate.time().msec() -
45 minDate.time().msec();
46
47 return secsTo * 1000 + msecs;
48 }
49 case QwtDate::Second:
50 {
51 return minDate.secsTo( maxDate );
52 }
53 case QwtDate::Minute:
54 {
55 const double secsTo = minDate.secsTo( maxDate );
56 return ::floor( secsTo / 60 );
57 }
58 case QwtDate::Hour:
59 {
60 const double secsTo = minDate.secsTo( maxDate );
61 return ::floor( secsTo / 3600 );
62 }
63 case QwtDate::Day:
64 {
65 return minDate.daysTo( maxDate );
66 }
67 case QwtDate::Week:
68 {
69 return ::floor( minDate.daysTo( maxDate ) / 7.0 );
70 }
71 case QwtDate::Month:
72 {
73 const double years =
74 double( maxDate.date().year() ) - minDate.date().year();
75
76 int months = maxDate.date().month() - minDate.date().month();
77 if ( maxDate.date().day() < minDate.date().day() )
78 months--;
79
80 return years * 12 + months;
81 }
82 case QwtDate::Year:
83 {
84 double years =
85 double( maxDate.date().year() ) - minDate.date().year();
86
87 if ( maxDate.date().month() < minDate.date().month() )
88 years -= 1.0;
89
90 return years;
91 }
92 }
93
94 return 0.0;
95}
96
97static double qwtRoundedIntervalWidth(
98 const QDateTime &minDate, const QDateTime &maxDate,
99 QwtDate::IntervalType intervalType )
100{
101 const QDateTime minD = QwtDate::floor( minDate, intervalType );
102 const QDateTime maxD = QwtDate::ceil( maxDate, intervalType );
103
104 return qwtIntervalWidth( minD, maxD, intervalType );
105}
106
107static inline int qwtStepCount( int intervalSize, int maxSteps,
108 const int limits[], size_t numLimits )
109{
110 for ( uint i = 0; i < numLimits; i++ )
111 {
112 const int numSteps = intervalSize / limits[ i ];
113
114 if ( numSteps > 1 && numSteps <= maxSteps &&
115 numSteps * limits[ i ] == intervalSize )
116 {
117 return numSteps;
118 }
119 }
120
121 return 0;
122}
123
124static int qwtStepSize( int intervalSize, int maxSteps, uint base )
125{
126 if ( maxSteps <= 0 )
127 return 0;
128
129 if ( maxSteps > 2 )
130 {
131 for ( int numSteps = maxSteps; numSteps > 1; numSteps-- )
132 {
133 const double stepSize = double( intervalSize ) / numSteps;
134
135 const double p = ::floor( ::log( stepSize ) / ::log( double( base ) ) );
136 const double fraction = qPow( base, p );
137
138 for ( uint n = base; n >= 1; n /= 2 )
139 {
140 if ( qFuzzyCompare( stepSize, n * fraction ) )
141 return qRound( stepSize );
142
143 if ( n == 3 && ( base % 2 ) == 0 )
144 {
145 if ( qFuzzyCompare( stepSize, 2 * fraction ) )
146 return qRound( stepSize );
147 }
148 }
149 }
150 }
151
152 return 0;
153}
154
155static int qwtDivideInterval( double intervalSize, int numSteps,
156 const int limits[], size_t numLimits )
157{
158 const int v = qCeil( intervalSize / double( numSteps ) );
159
160 for ( uint i = 0; i < numLimits - 1; i++ )
161 {
162 if ( v <= limits[i] )
163 return limits[i];
164 }
165
166 return limits[ numLimits - 1 ];
167}
168
169static double qwtDivideScale( double intervalSize, int numSteps,
170 QwtDate::IntervalType intervalType )
171{
172 if ( intervalType != QwtDate::Day )
173 {
174 if ( ( intervalSize > numSteps ) &&
175 ( intervalSize <= 2 * numSteps ) )
176 {
177 return 2.0;
178 }
179 }
180
181 double stepSize;
182
183 switch( intervalType )
184 {
185 case QwtDate::Second:
186 case QwtDate::Minute:
187 {
188 static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
189
190 stepSize = qwtDivideInterval( intervalSize, numSteps,
191 limits, sizeof( limits ) / sizeof( int ) );
192
193 break;
194 }
195 case QwtDate::Hour:
196 {
197 static int limits[] = { 1, 2, 3, 4, 6, 12, 24 };
198
199 stepSize = qwtDivideInterval( intervalSize, numSteps,
200 limits, sizeof( limits ) / sizeof( int ) );
201
202 break;
203 }
204 case QwtDate::Day:
205 {
206 const double v = intervalSize / double( numSteps );
207 if ( v <= 5.0 )
208 stepSize = qCeil( v );
209 else
210 stepSize = qCeil( v / 7 ) * 7;
211
212 break;
213 }
214 case QwtDate::Week:
215 {
216 static int limits[] = { 1, 2, 4, 8, 12, 26, 52 };
217
218 stepSize = qwtDivideInterval( intervalSize, numSteps,
219 limits, sizeof( limits ) / sizeof( int ) );
220
221 break;
222 }
223 case QwtDate::Month:
224 {
225 static int limits[] = { 1, 2, 3, 4, 6, 12 };
226
227 stepSize = qwtDivideInterval( intervalSize, numSteps,
228 limits, sizeof( limits ) / sizeof( int ) );
229
230 break;
231 }
232 case QwtDate::Year:
233 case QwtDate::Millisecond:
234 default:
235 {
236 stepSize = QwtScaleArithmetic::divideInterval(
237 intervalSize, numSteps, 10 );
238 }
239 }
240
241 return stepSize;
242}
243
244static double qwtDivideMajorStep( double stepSize, int maxMinSteps,
245 QwtDate::IntervalType intervalType )
246{
247 double minStepSize = 0.0;
248
249 switch( intervalType )
250 {
251 case QwtDate::Second:
252 {
253 minStepSize = qwtStepSize( stepSize, maxMinSteps, 10 );
254 if ( minStepSize == 0.0 )
255 minStepSize = 0.5 * stepSize;
256
257 break;
258 }
259 case QwtDate::Minute:
260 {
261 static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
262
263 int numSteps;
264
265 if ( stepSize > maxMinSteps )
266 {
267 numSteps = qwtStepCount( stepSize, maxMinSteps,
268 limits, sizeof( limits ) / sizeof( int ) );
269
270 }
271 else
272 {
273 numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
274 limits, sizeof( limits ) / sizeof( int ) );
275 }
276
277 if ( numSteps > 0 )
278 minStepSize = double( stepSize ) / numSteps;
279
280 break;
281 }
282 case QwtDate::Hour:
283 {
284 int numSteps = 0;
285
286 if ( stepSize > maxMinSteps )
287 {
288 static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
289
290 numSteps = qwtStepCount( stepSize, maxMinSteps,
291 limits, sizeof( limits ) / sizeof( int ) );
292 }
293 else
294 {
295 static int limits[] = { 1, 2, 5, 10, 15, 20, 30, 60 };
296
297 numSteps = qwtStepCount( stepSize * 60, maxMinSteps,
298 limits, sizeof( limits ) / sizeof( int ) );
299 }
300
301 if ( numSteps > 0 )
302 minStepSize = double( stepSize ) / numSteps;
303
304 break;
305 }
306 case QwtDate::Day:
307 {
308 int numSteps = 0;
309
310 if ( stepSize > maxMinSteps )
311 {
312 static int limits[] = { 1, 2, 3, 7, 14, 28 };
313
314 numSteps = qwtStepCount( stepSize, maxMinSteps,
315 limits, sizeof( limits ) / sizeof( int ) );
316 }
317 else
318 {
319 static int limits[] = { 1, 2, 3, 4, 6, 12, 24, 48, 72 };
320
321 numSteps = qwtStepCount( stepSize * 24, maxMinSteps,
322 limits, sizeof( limits ) / sizeof( int ) );
323 }
324
325 if ( numSteps > 0 )
326 minStepSize = double( stepSize ) / numSteps;
327
328 break;
329 }
330 case QwtDate::Week:
331 {
332 const int daysInStep = stepSize * 7;
333
334 if ( maxMinSteps >= daysInStep )
335 {
336 // we want to have one tick per day
337 minStepSize = 1.0 / 7.0;
338 }
339 else
340 {
341 // when the stepSize is more than a week we want to
342 // have a tick for each week
343
344 const int stepSizeInWeeks = stepSize;
345
346 if ( stepSizeInWeeks <= maxMinSteps )
347 {
348 minStepSize = 1;
349 }
350 else
351 {
352 minStepSize = QwtScaleArithmetic::divideInterval(
353 stepSizeInWeeks, maxMinSteps, 10 );
354 }
355 }
356 break;
357 }
358 case QwtDate::Month:
359 {
360 // fractions of months doesn't make any sense
361
362 if ( stepSize < maxMinSteps )
363 maxMinSteps = static_cast<int>( stepSize );
364
365 static int limits[] = { 1, 2, 3, 4, 6, 12 };
366
367 int numSteps = qwtStepCount( stepSize, maxMinSteps,
368 limits, sizeof( limits ) / sizeof( int ) );
369
370 if ( numSteps > 0 )
371 minStepSize = double( stepSize ) / numSteps;
372
373 break;
374 }
375 case QwtDate::Year:
376 {
377 if ( stepSize >= maxMinSteps )
378 {
379 minStepSize = QwtScaleArithmetic::divideInterval(
380 stepSize, maxMinSteps, 10 );
381 }
382 else
383 {
384 // something in months
385
386 static int limits[] = { 1, 2, 3, 4, 6, 12 };
387
388 int numSteps = qwtStepCount( 12 * stepSize, maxMinSteps,
389 limits, sizeof( limits ) / sizeof( int ) );
390
391 if ( numSteps > 0 )
392 minStepSize = double( stepSize ) / numSteps;
393 }
394
395 break;
396 }
397 default:
398 break;
399 }
400
401 if ( intervalType != QwtDate::Month
402 && minStepSize == 0.0 )
403 {
404 minStepSize = 0.5 * stepSize;
405 }
406
407 return minStepSize;
408}
409
410static QList<double> qwtDstTicks( const QDateTime &dateTime,
411 int secondsMajor, int secondsMinor )
412{
413 if ( secondsMinor <= 0 )
414 QList<double>();
415
416 QDateTime minDate = dateTime.addSecs( -secondsMajor );
417 minDate = QwtDate::floor( minDate, QwtDate::Hour );
418
419 const double utcOffset = QwtDate::utcOffset( dateTime );
420
421 // find the hours where daylight saving time happens
422
423 double dstMin = QwtDate::toDouble( minDate );
424 while ( minDate < dateTime &&
425 QwtDate::utcOffset( minDate ) != utcOffset )
426 {
427 minDate = minDate.addSecs( 3600 );
428 dstMin += 3600 * 1000.0;
429 }
430
431 QList<double> ticks;
432 for ( int i = 0; i < 3600; i += secondsMinor )
433 ticks += dstMin + i * 1000.0;
434
435 return ticks;
436}
437
438static QwtScaleDiv qwtDivideToSeconds(
439 const QDateTime &minDate, const QDateTime &maxDate,
440 double stepSize, int maxMinSteps,
441 QwtDate::IntervalType intervalType )
442{
443 // calculate the min step size
444 double minStepSize = 0;
445
446 if ( maxMinSteps > 1 )
447 {
448 minStepSize = qwtDivideMajorStep( stepSize,
449 maxMinSteps, intervalType );
450 }
451
452 bool daylightSaving = false;
453 if ( minDate.timeSpec() == Qt::LocalTime )
454 {
455 daylightSaving = intervalType > QwtDate::Hour;
456 if ( intervalType == QwtDate::Hour )
457 {
458 daylightSaving = stepSize > 1;
459 }
460 }
461
462 const double s = qwtMsecsForType( intervalType ) / 1000;
463 const int secondsMajor = static_cast<int>( stepSize * s );
464 const double secondsMinor = minStepSize * s;
465
466 // UTC excludes daylight savings. So from the difference
467 // of a date and its UTC counterpart we can find out
468 // the daylight saving hours
469
470 const double utcOffset = QwtDate::utcOffset( minDate );
471 double dstOff = 0;
472
473 QList<double> majorTicks;
474 QList<double> mediumTicks;
475 QList<double> minorTicks;
476
477 for ( QDateTime dt = minDate; dt <= maxDate;
478 dt = dt.addSecs( secondsMajor ) )
479 {
480 if ( !dt.isValid() )
481 break;
482
483 double majorValue = QwtDate::toDouble( dt );
484
485 if ( daylightSaving )
486 {
487 const double offset = utcOffset - QwtDate::utcOffset( dt );
488 majorValue += offset * 1000.0;
489
490 if ( offset > dstOff )
491 {
492 // we add some minor ticks for the DST hour,
493 // otherwise the ticks will be unaligned: 0, 2, 3, 5 ...
494 minorTicks += qwtDstTicks(
495 dt, secondsMajor, qRound( secondsMinor ) );
496 }
497
498 dstOff = offset;
499 }
500
501 if ( majorTicks.isEmpty() || majorTicks.last() != majorValue )
502 majorTicks += majorValue;
503
504 if ( secondsMinor > 0.0 )
505 {
506 const int numMinorSteps = qFloor( secondsMajor / secondsMinor );
507
508 for ( int i = 1; i < numMinorSteps; i++ )
509 {
510 const QDateTime mt = dt.addMSecs(
511 qRound64( i * secondsMinor * 1000 ) );
512
513 double minorValue = QwtDate::toDouble( mt );
514 if ( daylightSaving )
515 {
516 const double offset = utcOffset - QwtDate::utcOffset( mt );
517 minorValue += offset * 1000.0;
518 }
519
520 if ( minorTicks.isEmpty() || minorTicks.last() != minorValue )
521 {
522 const bool isMedium = ( numMinorSteps % 2 == 0 )
523 && ( i != 1 ) && ( i == numMinorSteps / 2 );
524
525 if ( isMedium )
526 mediumTicks += minorValue;
527 else
528 minorTicks += minorValue;
529 }
530 }
531 }
532 }
533
534 QwtScaleDiv scaleDiv;
535
536 scaleDiv.setInterval( QwtDate::toDouble( minDate ),
537 QwtDate::toDouble( maxDate ) );
538
539 scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
540 scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
541 scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
542
543 return scaleDiv;
544}
545
546static QwtScaleDiv qwtDivideToMonths(
547 QDateTime &minDate, const QDateTime &maxDate,
548 double stepSize, int maxMinSteps )
549{
550 // months are intervals with non
551 // equidistant ( in ms ) steps: we have to build the
552 // scale division manually
553
554 int minStepDays = 0;
555 int minStepSize = 0.0;
556
557 if ( maxMinSteps > 1 )
558 {
559 if ( stepSize == 1 )
560 {
561 if ( maxMinSteps >= 30 )
562 minStepDays = 1;
Value stored to 'minStepDays' is never read
563 else if ( maxMinSteps >= 6 )
564 minStepDays = 5;
565 else if ( maxMinSteps >= 3 )
566 minStepDays = 10;
567
568 minStepDays = 15;
569 }
570 else
571 {
572 minStepSize = qwtDivideMajorStep(
573 stepSize, maxMinSteps, QwtDate::Month );
574 }
575 }
576
577 QList<double> majorTicks;
578 QList<double> mediumTicks;
579 QList<double> minorTicks;
580
581 for ( QDateTime dt = minDate;
582 dt <= maxDate; dt = dt.addMonths( stepSize ) )
583 {
584 if ( !dt.isValid() )
585 break;
586
587 majorTicks += QwtDate::toDouble( dt );
588
589 if ( minStepDays > 0 )
590 {
591 for ( int days = minStepDays;
592 days < 30; days += minStepDays )
593 {
594 const double tick = QwtDate::toDouble( dt.addDays( days ) );
595
596 if ( days == 15 && minStepDays != 15 )
597 mediumTicks += tick;
598 else
599 minorTicks += tick;
600 }
601 }
602 else if ( minStepSize > 0.0 )
603 {
604 const int numMinorSteps = qRound( stepSize / (double) minStepSize );
605
606 for ( int i = 1; i < numMinorSteps; i++ )
607 {
608 const double minorValue =
609 QwtDate::toDouble( dt.addMonths( i * minStepSize ) );
610
611 if ( ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 ) )
612 mediumTicks += minorValue;
613 else
614 minorTicks += minorValue;
615 }
616 }
617 }
618
619 QwtScaleDiv scaleDiv;
620 scaleDiv.setInterval( QwtDate::toDouble( minDate ),
621 QwtDate::toDouble( maxDate ) );
622
623 scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
624 scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
625 scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
626
627 return scaleDiv;
628}
629
630static QwtScaleDiv qwtDivideToYears(
631 const QDateTime &minDate, const QDateTime &maxDate,
632 double stepSize, int maxMinSteps )
633{
634 QList<double> majorTicks;
635 QList<double> mediumTicks;
636 QList<double> minorTicks;
637
638 double minStepSize = 0.0;
639
640 if ( maxMinSteps > 1 )
641 {
642 minStepSize = qwtDivideMajorStep(
643 stepSize, maxMinSteps, QwtDate::Year );
644 }
645
646 int numMinorSteps = 0;
647 if ( minStepSize > 0.0 )
648 numMinorSteps = qFloor( stepSize / minStepSize );
649
650 bool dateBC = minDate.date().year() < -1;
651
652 for ( QDateTime dt = minDate; dt <= maxDate;
653 dt = dt.addYears( stepSize ) )
654 {
655 if ( dateBC && dt.date().year() > 1 )
656 {
657 // there is no year 0 in the Julian calendar
658 dt = dt.addYears( -1 );
659 dateBC = false;
660 }
661
662 if ( !dt.isValid() )
663 break;
664
665 majorTicks += QwtDate::toDouble( dt );
666
667 for ( int i = 1; i < numMinorSteps; i++ )
668 {
669 QDateTime tickDate;
670
671 const double years = qRound( i * minStepSize );
672 if ( years >= INT_MAX2147483647 / 12 )
673 {
674 tickDate = dt.addYears( years );
675 }
676 else
677 {
678 tickDate = dt.addMonths( qRound( years * 12 ) );
679 }
680
681 const bool isMedium = ( numMinorSteps > 2 ) &&
682 ( numMinorSteps % 2 == 0 ) && ( i == numMinorSteps / 2 );
683
684 const double minorValue = QwtDate::toDouble( tickDate );
685 if ( isMedium )
686 mediumTicks += minorValue;
687 else
688 minorTicks += minorValue;
689 }
690
691 if ( QwtDate::maxDate().addYears( -stepSize ) < dt.date() )
692 {
693 break;
694 }
695 }
696
697 QwtScaleDiv scaleDiv;
698 scaleDiv.setInterval( QwtDate::toDouble( minDate ),
699 QwtDate::toDouble( maxDate ) );
700
701 scaleDiv.setTicks( QwtScaleDiv::MajorTick, majorTicks );
702 scaleDiv.setTicks( QwtScaleDiv::MediumTick, mediumTicks );
703 scaleDiv.setTicks( QwtScaleDiv::MinorTick, minorTicks );
704
705 return scaleDiv;
706}
707
708class QwtDateScaleEngine::PrivateData
709{
710public:
711 PrivateData( Qt::TimeSpec spec ):
712 timeSpec( spec ),
713 utcOffset( 0 ),
714 week0Type( QwtDate::FirstThursday ),
715 maxWeeks( 4 )
716 {
717 }
718
719 Qt::TimeSpec timeSpec;
720 int utcOffset;
721 QwtDate::Week0Type week0Type;
722 int maxWeeks;
723};
724
725
726/*!
727 \brief Constructor
728
729 The engine is initialized to build scales for the
730 given time specification. It classifies intervals > 4 weeks
731 as >= Qt::Month. The first week of a year is defined like
732 for QwtDate::FirstThursday.
733
734 \param timeSpec Time specification
735
736 \sa setTimeSpec(), setMaxWeeks(), setWeek0Type()
737 */
738QwtDateScaleEngine::QwtDateScaleEngine( Qt::TimeSpec timeSpec ):
739 QwtLinearScaleEngine( 10 )
740{
741 d_data = new PrivateData( timeSpec );
742}
743
744//! Destructor
745QwtDateScaleEngine::~QwtDateScaleEngine()
746{
747 delete d_data;
748}
749
750/*!
751 Set the time specification used by the engine
752
753 \param timeSpec Time specification
754 \sa timeSpec(), setUtcOffset(), toDateTime()
755 */
756void QwtDateScaleEngine::setTimeSpec( Qt::TimeSpec timeSpec )
757{
758 d_data->timeSpec = timeSpec;
759}
760
761/*!
762 \return Time specification used by the engine
763 \sa setTimeSpec(), utcOffset(), toDateTime()
764 */
765Qt::TimeSpec QwtDateScaleEngine::timeSpec() const
766{
767 return d_data->timeSpec;
768}
769
770/*!
771 Set the offset in seconds from Coordinated Universal Time
772
773 \param seconds Offset in seconds
774
775 \note The offset has no effect beside for the time specification
776 Qt::OffsetFromUTC.
777
778 \sa QDate::utcOffset(), setTimeSpec(), toDateTime()
779 */
780void QwtDateScaleEngine::setUtcOffset( int seconds )
781{
782 d_data->utcOffset = seconds;
783}
784
785/*!
786 \return Offset in seconds from Coordinated Universal Time
787 \note The offset has no effect beside for the time specification
788 Qt::OffsetFromUTC.
789
790 \sa QDate::setUtcOffset(), setTimeSpec(), toDateTime()
791 */
792int QwtDateScaleEngine::utcOffset() const
793{
794 return d_data->utcOffset;
795}
796
797/*!
798 Sets how to identify the first week of a year.
799
800 \param week0Type Mode how to identify the first week of a year
801
802 \sa week0Type(), setMaxWeeks()
803 \note week0Type has no effect beside for intervals classified as
804 QwtDate::Week.
805 */
806void QwtDateScaleEngine::setWeek0Type( QwtDate::Week0Type week0Type )
807{
808 d_data->week0Type = week0Type;
809}
810
811/*!
812 \return Setting how to identify the first week of a year.
813 \sa setWeek0Type(), maxWeeks()
814 */
815QwtDate::Week0Type QwtDateScaleEngine::week0Type() const
816{
817 return d_data->week0Type;
818}
819
820/*!
821 Set a upper limit for the number of weeks, when an interval
822 can be classified as Qt::Week.
823
824 The default setting is 4 weeks.
825
826 \param weeks Upper limit for the number of weeks
827
828 \note In business charts a year is often devided
829 into weeks [1-52]
830 \sa maxWeeks(), setWeek0Type()
831 */
832void QwtDateScaleEngine::setMaxWeeks( int weeks )
833{
834 d_data->maxWeeks = qMax( weeks, 0 );
835}
836
837/*!
838 \return Upper limit for the number of weeks, when an interval
839 can be classified as Qt::Week.
840 \sa setMaxWeeks(), week0Type()
841 */
842int QwtDateScaleEngine::maxWeeks() const
843{
844 return d_data->maxWeeks;
845}
846
847/*!
848 Classification of a date/time interval division
849
850 \param minDate Minimum ( = earlier ) of the interval
851 \param maxDate Maximum ( = later ) of the interval
852 \param maxSteps Maximum for the number of steps
853
854 \return Interval classification
855 */
856QwtDate::IntervalType QwtDateScaleEngine::intervalType(
857 const QDateTime &minDate, const QDateTime &maxDate,
858 int maxSteps ) const
859{
860 const double jdMin = minDate.date().toJulianDay();
861 const double jdMax = maxDate.date().toJulianDay();
862
863 if ( ( jdMax - jdMin ) / 365 > maxSteps )
864 return QwtDate::Year;
865
866 const int months = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Month );
867 if ( months > maxSteps * 6 )
868 return QwtDate::Year;
869
870 const int days = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Day );
871 const int weeks = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Week );
872
873 if ( weeks > d_data->maxWeeks )
874 {
875 if ( days > 4 * maxSteps * 7 )
876 return QwtDate::Month;
877 }
878
879 if ( days > maxSteps * 7 )
880 return QwtDate::Week;
881
882 const int hours = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Hour );
883 if ( hours > maxSteps * 24 )
884 return QwtDate::Day;
885
886 const int seconds = qwtRoundedIntervalWidth( minDate, maxDate, QwtDate::Second );
887
888 if ( seconds >= maxSteps * 3600 )
889 return QwtDate::Hour;
890
891 if ( seconds >= maxSteps * 60 )
892 return QwtDate::Minute;
893
894 if ( seconds >= maxSteps )
895 return QwtDate::Second;
896
897 return QwtDate::Millisecond;
898}
899
900/*!
901 Align and divide an interval
902
903 The algorithm aligns and divides the interval into steps.
904
905 Datetime interval divisions are usually not equidistant and the
906 calculated stepSize can only be used as an approximation
907 for the steps calculated by divideScale().
908
909 \param maxNumSteps Max. number of steps
910 \param x1 First limit of the interval (In/Out)
911 \param x2 Second limit of the interval (In/Out)
912 \param stepSize Step size (Out)
913
914 \sa QwtScaleEngine::setAttribute()
915*/
916void QwtDateScaleEngine::autoScale( int maxNumSteps,
917 double &x1, double &x2, double &stepSize ) const
918{
919 stepSize = 0.0;
920
921 QwtInterval interval( x1, x2 );
922 interval = interval.normalized();
923
924 interval.setMinValue( interval.minValue() - lowerMargin() );
925 interval.setMaxValue( interval.maxValue() + upperMargin() );
926
927 if ( testAttribute( QwtScaleEngine::Symmetric ) )
928 interval = interval.symmetrize( reference() );
929
930 if ( testAttribute( QwtScaleEngine::IncludeReference ) )
931 interval = interval.extend( reference() );
932
933 if ( interval.width() == 0.0 )
934 interval = buildInterval( interval.minValue() );
935
936 const QDateTime from = toDateTime( interval.minValue() );
937 const QDateTime to = toDateTime( interval.maxValue() );
938
939 if ( from.isValid() && to.isValid() )
940 {
941 if ( maxNumSteps < 1 )
942 maxNumSteps = 1;
943
944 const QwtDate::IntervalType intvType =
945 intervalType( from, to, maxNumSteps );
946
947 const double width = qwtIntervalWidth( from, to, intvType );
948
949 const double stepWidth = qwtDivideScale( width, maxNumSteps, intvType );
950 if ( stepWidth != 0.0 && !testAttribute( QwtScaleEngine::Floating ) )
951 {
952 const QDateTime d1 = alignDate( from, stepWidth, intvType, false );
953 const QDateTime d2 = alignDate( to, stepWidth, intvType, true );
954
955 interval.setMinValue( QwtDate::toDouble( d1 ) );
956 interval.setMaxValue( QwtDate::toDouble( d2 ) );
957 }
958
959 stepSize = stepWidth * qwtMsecsForType( intvType );
960 }
961
962 x1 = interval.minValue();
963 x2 = interval.maxValue();
964
965 if ( testAttribute( QwtScaleEngine::Inverted ) )
966 {
967 qSwap( x1, x2 );
968 stepSize = -stepSize;
969 }
970}
971
972/*!
973 \brief Calculate a scale division for a date/time interval
974
975 \param x1 First interval limit
976 \param x2 Second interval limit
977 \param maxMajorSteps Maximum for the number of major steps
978 \param maxMinorSteps Maximum number of minor steps
979 \param stepSize Step size. If stepSize == 0, the scaleEngine
980 calculates one.
981 \return Calculated scale division
982*/
983QwtScaleDiv QwtDateScaleEngine::divideScale( double x1, double x2,
984 int maxMajorSteps, int maxMinorSteps, double stepSize ) const
985{
986 if ( maxMajorSteps < 1 )
987 maxMajorSteps = 1;
988
989 const double min = qMin( x1, x2 );
990 const double max = qMax( x1, x2 );
991
992 const QDateTime from = toDateTime( min );
993 const QDateTime to = toDateTime( max );
994
995 if ( from == to )
996 return QwtScaleDiv();
997
998 stepSize = qAbs( stepSize );
999 if ( stepSize > 0.0 )
1000 {
1001 // as interval types above hours are not equidistant
1002 // ( even days might have 23/25 hours because of daylight saving )
1003 // the stepSize is used as a hint only
1004
1005 maxMajorSteps = qCeil( ( max - min ) / stepSize );
1006 }
1007
1008 const QwtDate::IntervalType intvType =
1009 intervalType( from, to, maxMajorSteps );
1010
1011 QwtScaleDiv scaleDiv;
1012
1013 if ( intvType == QwtDate::Millisecond )
1014 {
1015 // for milliseconds and below we can use the decimal system
1016 scaleDiv = QwtLinearScaleEngine::divideScale( min, max,
1017 maxMajorSteps, maxMinorSteps, stepSize );
1018 }
1019 else
1020 {
1021 const QDateTime minDate = QwtDate::floor( from, intvType );
1022 const QDateTime maxDate = QwtDate::ceil( to, intvType );
1023
1024 scaleDiv = buildScaleDiv( minDate, maxDate,
1025 maxMajorSteps, maxMinorSteps, intvType );
1026
1027 // scaleDiv has been calculated from an extended interval
1028 // adjusted to the step size. We have to shrink it again.
1029
1030 scaleDiv = scaleDiv.bounded( min, max );
1031 }
1032
1033 if ( x1 > x2 )
1034 scaleDiv.invert();
1035
1036 return scaleDiv;
1037}
1038
1039QwtScaleDiv QwtDateScaleEngine::buildScaleDiv(
1040 const QDateTime &minDate, const QDateTime &maxDate,
1041 int maxMajorSteps, int maxMinorSteps,
1042 QwtDate::IntervalType intervalType ) const
1043{
1044 // calculate the step size
1045 const double stepSize = qwtDivideScale(
1046 qwtIntervalWidth( minDate, maxDate, intervalType ),
1047 maxMajorSteps, intervalType );
1048
1049 // align minDate to the step size
1050 QDateTime dt0 = alignDate( minDate, stepSize, intervalType, false );
1051 if ( !dt0.isValid() )
1052 {
1053 // the floored date is out of the range of a
1054 // QDateTime - we ceil instead.
1055 dt0 = alignDate( minDate, stepSize, intervalType, true );
1056 }
1057
1058 QwtScaleDiv scaleDiv;
1059
1060 if ( intervalType <= QwtDate::Week )
1061 {
1062 scaleDiv = qwtDivideToSeconds( dt0, maxDate,
1063 stepSize, maxMinorSteps, intervalType );
1064 }
1065 else
1066 {
1067 if( intervalType == QwtDate::Month )
1068 {
1069 scaleDiv = qwtDivideToMonths( dt0, maxDate,
1070 stepSize, maxMinorSteps );
1071 }
1072 else if ( intervalType == QwtDate::Year )
1073 {
1074 scaleDiv = qwtDivideToYears( dt0, maxDate,
1075 stepSize, maxMinorSteps );
1076 }
1077 }
1078
1079
1080 return scaleDiv;
1081}
1082
1083/*!
1084 Align a date/time value for a step size
1085
1086 For Qt::Day alignments there is no "natural day 0" -
1087 instead the first day of the year is used to avoid jumping
1088 major ticks positions when panning a scale. For other alignments
1089 ( f.e according to the first day of the month ) alignDate()
1090 has to be overloaded.
1091
1092 \param dateTime Date/time value
1093 \param stepSize Step size
1094 \param intervalType Interval type
1095 \param up When true dateTime is ceiled - otherwise it is floored
1096
1097 \return Aligned date/time value
1098 */
1099QDateTime QwtDateScaleEngine::alignDate(
1100 const QDateTime &dateTime, double stepSize,
1101 QwtDate::IntervalType intervalType, bool up ) const
1102{
1103 // what about: (year == 1582 && month == 10 && day > 4 && day < 15) ??
1104
1105 QDateTime dt = dateTime;
1106
1107 if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
1108 {
1109 dt.setUtcOffset( 0 );
1110 }
1111
1112 switch( intervalType )
1113 {
1114 case QwtDate::Millisecond:
1115 {
1116 const int ms = qwtAlignValue(
1117 dt.time().msec(), stepSize, up ) ;
1118
1119 dt = QwtDate::floor( dateTime, QwtDate::Second );
1120 dt = dt.addMSecs( ms );
1121
1122 break;
1123 }
1124 case QwtDate::Second:
1125 {
1126 int second = dt.time().second();
1127 if ( up )
1128 {
1129 if ( dt.time().msec() > 0 )
1130 second++;
1131 }
1132
1133 const int s = qwtAlignValue( second, stepSize, up );
1134
1135 dt = QwtDate::floor( dt, QwtDate::Minute );
1136 dt = dt.addSecs( s );
1137
1138 break;
1139 }
1140 case QwtDate::Minute:
1141 {
1142 int minute = dt.time().minute();
1143 if ( up )
1144 {
1145 if ( dt.time().msec() > 0 || dt.time().second() > 0 )
1146 minute++;
1147 }
1148
1149 const int m = qwtAlignValue( minute, stepSize, up );
1150
1151 dt = QwtDate::floor( dt, QwtDate::Hour );
1152 dt = dt.addSecs( m * 60 );
1153
1154 break;
1155 }
1156 case QwtDate::Hour:
1157 {
1158 int hour = dt.time().hour();
1159 if ( up )
1160 {
1161 if ( dt.time().msec() > 0 || dt.time().second() > 0
1162 || dt.time().minute() > 0 )
1163 {
1164 hour++;
1165 }
1166 }
1167 const int h = qwtAlignValue( hour, stepSize, up );
1168
1169 dt = QwtDate::floor( dt, QwtDate::Day );
1170 dt = dt.addSecs( h * 3600 );
1171
1172 break;
1173 }
1174 case QwtDate::Day:
1175 {
1176 // What date do we expect f.e. from an alignment of 5 days ??
1177 // Aligning them to the beginning of the year avoids at least
1178 // jumping major ticks when panning
1179
1180 int day = dt.date().dayOfYear();
1181 if ( up )
1182 {
1183 if ( dt.time() > QTime( 0, 0 ) )
1184 day++;
1185 }
1186
1187 const int d = qwtAlignValue( day, stepSize, up );
1188
1189 dt = QwtDate::floor( dt, QwtDate::Year );
1190 dt = dt.addDays( d - 1 );
1191
1192 break;
1193 }
1194 case QwtDate::Week:
1195 {
1196 const QDate date = QwtDate::dateOfWeek0(
1197 dt.date().year(), d_data->week0Type );
1198
1199 int numWeeks = date.daysTo( dt.date() ) / 7;
1200 if ( up )
1201 {
1202 if ( dt.time() > QTime( 0, 0 ) ||
1203 date.daysTo( dt.date() ) % 7 )
1204 {
1205 numWeeks++;
1206 }
1207 }
1208
1209 const int d = qwtAlignValue( numWeeks, stepSize, up ) * 7;
1210
1211 dt = QwtDate::floor( dt, QwtDate::Day );
1212 dt.setDate( date );
1213 dt = dt.addDays( d );
1214
1215 break;
1216 }
1217 case QwtDate::Month:
1218 {
1219 int month = dt.date().month();
1220 if ( up )
1221 {
1222 if ( dt.date().day() > 1 ||
1223 dt.time() > QTime( 0, 0 ) )
1224 {
1225 month++;
1226 }
1227 }
1228
1229 const int m = qwtAlignValue( month - 1, stepSize, up );
1230
1231 dt = QwtDate::floor( dt, QwtDate::Year );
1232 dt = dt.addMonths( m );
1233
1234 break;
1235 }
1236 case QwtDate::Year:
1237 {
1238 int year = dateTime.date().year();
1239 if ( up )
1240 {
1241 if ( dateTime.date().dayOfYear() > 1 ||
1242 dt.time() > QTime( 0, 0 ) )
1243 {
1244 year++;
1245 }
1246 }
1247
1248 const int y = qwtAlignValue( year, stepSize, up );
1249
1250 dt = QwtDate::floor( dt, QwtDate::Day );
1251 if ( y == 0 )
1252 {
1253 // there is no year 0 in the Julian calendar
1254 dt.setDate( QDate( stepSize, 1, 1 ).addYears( -stepSize ) );
1255 }
1256 else
1257 {
1258 dt.setDate( QDate( y, 1, 1 ) );
1259 }
1260
1261 break;
1262 }
1263 }
1264
1265 if ( dateTime.timeSpec() == Qt::OffsetFromUTC )
1266 {
1267 dt.setUtcOffset( dateTime.utcOffset() );
1268 }
1269
1270 return dt;
1271}
1272
1273/*!
1274 Translate a double value into a QDateTime object.
1275
1276 For QDateTime result is bounded by QwtDate::minDate() and QwtDate::maxDate()
1277
1278 \return QDateTime object initialized with timeSpec() and utcOffset().
1279 \sa timeSpec(), utcOffset(), QwtDate::toDateTime()
1280 */
1281QDateTime QwtDateScaleEngine::toDateTime( double value ) const
1282{
1283 QDateTime dt = QwtDate::toDateTime( value, d_data->timeSpec );
1284 if ( !dt.isValid() )
1285 {
1286 const QDate date = ( value <= 0.0 )
1287 ? QwtDate::minDate() : QwtDate::maxDate();
1288
1289 dt = QDateTime( date, QTime( 0, 0 ), d_data->timeSpec );
1290 }
1291
1292 if ( d_data->timeSpec == Qt::OffsetFromUTC )
1293 {
1294 dt = dt.addSecs( d_data->utcOffset );
1295 dt.setUtcOffset( d_data->utcOffset );
1296 }
1297
1298 return dt;
1299}
1300