qwt_date.cpp
Go to the documentation of this file.
1 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
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 <qdebug.h>
12 #include <qlocale.h>
13 #include <math.h>
14 #include <limits>
15 #include <limits.h>
16 
17 #if QT_VERSION >= 0x050000
18 
19 typedef qint64 QwtJulianDay;
20 static const QwtJulianDay minJulianDayD = Q_INT64_C( -784350574879 );
21 static const QwtJulianDay maxJulianDayD = Q_INT64_C( 784354017364 );
22 
23 #else
24 
25 // QDate stores the Julian day as unsigned int, but
26 // but it is QDate::fromJulianDay( int ). That's why
27 // we have the range [ 1, INT_MAX ]
28 typedef int QwtJulianDay;
29 static const QwtJulianDay minJulianDayD = 1;
30 static const QwtJulianDay maxJulianDayD = std::numeric_limits<int>::max();
31 
32 #endif
33 
34 static QString qwtExpandedFormat( const QString & format,
35  const QDateTime &dateTime, QwtDate::Week0Type week0Type )
36 {
37  const int week = QwtDate::weekNumber( dateTime.date(), week0Type );
38 
39  QString weekNo;
40  weekNo.setNum( week );
41 
42  QString weekNoWW;
43  if ( weekNo.length() == 1 )
44  weekNoWW += QLatin1Char( '0' );
45 
46  weekNoWW += weekNo;
47 
48  QString fmt = format;
49  fmt.replace( QLatin1String( "ww" ), weekNoWW );
50  fmt.replace( QLatin1Char( 'w' ), weekNo );
51 
52  if ( week == 1 && dateTime.date().month() != 1 )
53  {
54  // in case of week 1, we might need to increment the year
55 
56  QLatin1String s_yyyy( "yyyy" );
57  QLatin1String s_yy( "yy" );
58 
59  // week 1 might start in the previous year
60 
61  bool doReplaceYear = fmt.contains( s_yy );
62 
63  if ( doReplaceYear )
64  {
65  if ( fmt.contains( 'M' ) )
66  {
67  // in case of also having 'M' we have a conflict about
68  // which year to show
69 
70  doReplaceYear = false;
71  }
72  else
73  {
74  // in case of also having 'd' or 'dd' we have a conflict about
75  // which year to show
76 
77  int numD = 0;
78 
79  for ( int i = 0; i < fmt.size(); i++ )
80  {
81  if ( fmt[i] == 'd' )
82  {
83  numD++;
84  }
85  else
86  {
87  if ( numD > 0 && numD <= 2 )
88  break;
89 
90  numD = 0;
91  }
92  }
93 
94  if ( numD > 0 && numD <= 2 )
95  doReplaceYear = false;
96  }
97  }
98 
99  if ( doReplaceYear )
100  {
101  const QDate dt( dateTime.date().year() + 1, 1, 1 );
102 
103  if ( fmt.contains( s_yyyy ) )
104  {
105  fmt.replace( s_yyyy, dt.toString( s_yyyy ) );
106  }
107  else
108  {
109  fmt.replace( s_yy, dt.toString( s_yyyy ) );
110  }
111  }
112  }
113 
114  return fmt;
115 }
116 
117 static inline Qt::DayOfWeek qwtFirstDayOfWeek()
118 {
119 #if QT_VERSION >= 0x040800
120  return QLocale().firstDayOfWeek();
121 #else
122 
123  switch( QLocale().country() )
124  {
125  case QLocale::Maldives:
126  return Qt::Friday;
127 
128  case QLocale::Afghanistan:
129  case QLocale::Algeria:
130  case QLocale::Bahrain:
131  case QLocale::Djibouti:
132  case QLocale::Egypt:
133  case QLocale::Eritrea:
134  case QLocale::Ethiopia:
135  case QLocale::Iran:
136  case QLocale::Iraq:
137  case QLocale::Jordan:
138  case QLocale::Kenya:
139  case QLocale::Kuwait:
140  case QLocale::LibyanArabJamahiriya:
141  case QLocale::Morocco:
142  case QLocale::Oman:
143  case QLocale::Qatar:
144  case QLocale::SaudiArabia:
145  case QLocale::Somalia:
146  case QLocale::Sudan:
147  case QLocale::Tunisia:
148  case QLocale::Yemen:
149  return Qt::Saturday;
150 
151  case QLocale::AmericanSamoa:
152  case QLocale::Argentina:
153  case QLocale::Azerbaijan:
154  case QLocale::Botswana:
155  case QLocale::Canada:
156  case QLocale::China:
157  case QLocale::FaroeIslands:
158  case QLocale::Georgia:
159  case QLocale::Greenland:
160  case QLocale::Guam:
161  case QLocale::HongKong:
162  case QLocale::Iceland:
163  case QLocale::India:
164  case QLocale::Ireland:
165  case QLocale::Israel:
166  case QLocale::Jamaica:
167  case QLocale::Japan:
168  case QLocale::Kyrgyzstan:
169  case QLocale::Lao:
170  case QLocale::Malta:
171  case QLocale::MarshallIslands:
172  case QLocale::Macau:
173  case QLocale::Mongolia:
174  case QLocale::NewZealand:
175  case QLocale::NorthernMarianaIslands:
176  case QLocale::Pakistan:
177  case QLocale::Philippines:
178  case QLocale::RepublicOfKorea:
179  case QLocale::Singapore:
180  case QLocale::SyrianArabRepublic:
181  case QLocale::Taiwan:
182  case QLocale::Thailand:
183  case QLocale::TrinidadAndTobago:
184  case QLocale::UnitedStates:
185  case QLocale::UnitedStatesMinorOutlyingIslands:
186  case QLocale::USVirginIslands:
187  case QLocale::Uzbekistan:
188  case QLocale::Zimbabwe:
189  return Qt::Sunday;
190 
191  default:
192  return Qt::Monday;
193  }
194 #endif
195 }
196 
197 static inline void qwtFloorTime(
198  QwtDate::IntervalType intervalType, QDateTime &dt )
199 {
200  // when dt is inside the special hour where DST is ending
201  // an hour is no unique. Therefore we have to
202  // use UTC time.
203 
204  const Qt::TimeSpec timeSpec = dt.timeSpec();
205 
206  if ( timeSpec == Qt::LocalTime )
207  dt = dt.toTimeSpec( Qt::UTC );
208 
209  const QTime t = dt.time();
210  switch( intervalType )
211  {
212  case QwtDate::Second:
213  {
214  dt.setTime( QTime( t.hour(), t.minute(), t.second() ) );
215  break;
216  }
217  case QwtDate::Minute:
218  {
219  dt.setTime( QTime( t.hour(), t.minute(), 0 ) );
220  break;
221  }
222  case QwtDate::Hour:
223  {
224  dt.setTime( QTime( t.hour(), 0, 0 ) );
225  break;
226  }
227  default:
228  break;
229  }
230 
231  if ( timeSpec == Qt::LocalTime )
232  dt = dt.toTimeSpec( Qt::LocalTime );
233 }
234 
235 static inline QDateTime qwtToTimeSpec(
236  const QDateTime &dt, Qt::TimeSpec spec )
237 {
238  if ( dt.timeSpec() == spec )
239  return dt;
240 
241  const qint64 jd = dt.date().toJulianDay();
242  if ( jd < 0 || jd >= INT_MAX )
243  {
244  // the conversion between local time and UTC
245  // is internally limited. To avoid
246  // overflows we simply ignore the difference
247  // for those dates
248 
249  QDateTime dt2 = dt;
250  dt2.setTimeSpec( spec );
251  return dt2;
252  }
253 
254  return dt.toTimeSpec( spec );
255 }
256 
257 static inline double qwtToJulianDay( int year, int month, int day )
258 {
259  // code from QDate but using doubles to avoid overflows
260  // for large values
261 
262  const int m1 = ( month - 14 ) / 12;
263  const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
264  const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 );
265 
266  return ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
267  - ::floor( ( 3 * y1 ) / 4 ) + day - 32075;
268 }
269 
270 static inline qint64 qwtFloorDiv64( qint64 a, int b )
271 {
272  if ( a < 0 )
273  a -= b - 1;
274 
275  return a / b;
276 }
277 
278 static inline qint64 qwtFloorDiv( int a, int b )
279 {
280  if ( a < 0 )
281  a -= b - 1;
282 
283  return a / b;
284 }
285 
286 static inline QDate qwtToDate( int year, int month = 1, int day = 1 )
287 {
288 #if QT_VERSION >= 0x050000
289  return QDate( year, month, day );
290 #else
291  if ( year > 100000 )
292  {
293  // code from QDate but using doubles to avoid overflows
294  // for large values
295 
296  const int m1 = ( month - 14 ) / 12;
297  const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
298  const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 );
299 
300  const double jd = ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
301  - ::floor( ( 3 * y1 ) / 4 ) + day - 32075;
302 
303  if ( jd > maxJulianDayD )
304  {
305  qWarning() << "qwtToDate: overflow";
306  return QDate();
307  }
308 
309  return QDate::fromJulianDay( static_cast<QwtJulianDay>( jd ) );
310  }
311  else
312  {
313  return QDate( year, month, day );
314  }
315 #endif
316 }
317 
329 QDateTime QwtDate::toDateTime( double value, Qt::TimeSpec timeSpec )
330 {
331  const int msecsPerDay = 86400000;
332 
333  const double days = static_cast<qint64>( ::floor( value / msecsPerDay ) );
334 
335  const double jd = QwtDate::JulianDayForEpoch + days;
336  if ( ( jd > maxJulianDayD ) || ( jd < minJulianDayD ) )
337  {
338  qWarning() << "QwtDate::toDateTime: overflow";
339  return QDateTime();
340  }
341 
342  const QDate d = QDate::fromJulianDay( static_cast<QwtJulianDay>( jd ) );
343 
344  const int msecs = static_cast<int>( value - days * msecsPerDay );
345 
346  static const QTime timeNull( 0, 0, 0, 0 );
347 
348  QDateTime dt( d, timeNull.addMSecs( msecs ), Qt::UTC );
349 
350  if ( timeSpec == Qt::LocalTime )
351  dt = qwtToTimeSpec( dt, timeSpec );
352 
353  return dt;
354 }
355 
366 double QwtDate::toDouble( const QDateTime &dateTime )
367 {
368  const int msecsPerDay = 86400000;
369 
370  const QDateTime dt = qwtToTimeSpec( dateTime, Qt::UTC );
371 
372  const double days = dt.date().toJulianDay() - QwtDate::JulianDayForEpoch;
373 
374  const QTime time = dt.time();
375  const double secs = 3600.0 * time.hour() +
376  60.0 * time.minute() + time.second();
377 
378  return days * msecsPerDay + time.msec() + 1000.0 * secs;
379 }
380 
391 QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType )
392 {
393  if ( dateTime.date() >= QwtDate::maxDate() )
394  return dateTime;
395 
396  QDateTime dt = dateTime;
397 
398  switch ( intervalType )
399  {
401  {
402  break;
403  }
404  case QwtDate::Second:
405  {
407  if ( dt < dateTime )
408  dt = dt.addSecs( 1 );
409 
410  break;
411  }
412  case QwtDate::Minute:
413  {
415  if ( dt < dateTime )
416  dt = dt.addSecs( 60 );
417 
418  break;
419  }
420  case QwtDate::Hour:
421  {
423  if ( dt < dateTime )
424  dt = dt.addSecs( 3600 );
425 
426  break;
427  }
428  case QwtDate::Day:
429  {
430  dt.setTime( QTime( 0, 0 ) );
431  if ( dt < dateTime )
432  dt = dt.addDays( 1 );
433 
434  break;
435  }
436  case QwtDate::Week:
437  {
438  dt.setTime( QTime( 0, 0 ) );
439  if ( dt < dateTime )
440  dt = dt.addDays( 1 );
441 
442  int days = qwtFirstDayOfWeek() - dt.date().dayOfWeek();
443  if ( days < 0 )
444  days += 7;
445 
446  dt = dt.addDays( days );
447 
448  break;
449  }
450  case QwtDate::Month:
451  {
452  dt.setTime( QTime( 0, 0 ) );
453  dt.setDate( qwtToDate( dateTime.date().year(),
454  dateTime.date().month() ) );
455 
456  if ( dt < dateTime )
457  dt = dt.addMonths( 1 );
458 
459  break;
460  }
461  case QwtDate::Year:
462  {
463  dt.setTime( QTime( 0, 0 ) );
464 
465  const QDate d = dateTime.date();
466 
467  int year = d.year();
468  if ( d.month() > 1 || d.day() > 1 || !dateTime.time().isNull() )
469  year++;
470 
471  if ( year == 0 )
472  year++; // there is no year 0
473 
474  dt.setDate( qwtToDate( year ) );
475  break;
476  }
477  }
478 
479  return dt;
480 }
481 
493 QDateTime QwtDate::floor( const QDateTime &dateTime,
494  IntervalType intervalType )
495 {
496  if ( dateTime.date() <= QwtDate::minDate() )
497  return dateTime;
498 
499  QDateTime dt = dateTime;
500 
501  switch ( intervalType )
502  {
504  {
505  break;
506  }
507  case QwtDate::Second:
508  case QwtDate::Minute:
509  case QwtDate::Hour:
510  {
511  qwtFloorTime( intervalType, dt );
512  break;
513  }
514  case QwtDate::Day:
515  {
516  dt.setTime( QTime( 0, 0 ) );
517  break;
518  }
519  case QwtDate::Week:
520  {
521  dt.setTime( QTime( 0, 0 ) );
522 
523  int days = dt.date().dayOfWeek() - qwtFirstDayOfWeek();
524  if ( days < 0 )
525  days += 7;
526 
527  dt = dt.addDays( -days );
528 
529  break;
530  }
531  case QwtDate::Month:
532  {
533  dt.setTime( QTime( 0, 0 ) );
534 
535  const QDate date = qwtToDate( dt.date().year(),
536  dt.date().month() );
537  dt.setDate( date );
538 
539  break;
540  }
541  case QwtDate::Year:
542  {
543  dt.setTime( QTime( 0, 0 ) );
544 
545  const QDate date = qwtToDate( dt.date().year() );
546  dt.setDate( date );
547 
548  break;
549  }
550  }
551 
552  return dt;
553 }
554 
568 {
569  static QDate date;
570  if ( !date.isValid() )
571  date = QDate::fromJulianDay( minJulianDayD );
572 
573  return date;
574 }
575 
590 {
591  static QDate date;
592  if ( !date.isValid() )
593  date = QDate::fromJulianDay( maxJulianDayD );
594 
595  return date;
596 }
597 
611 {
612  const Qt::DayOfWeek firstDayOfWeek = qwtFirstDayOfWeek();
613 
614  QDate dt0( year, 1, 1 );
615 
616  // floor to the first day of the week
617  int days = dt0.dayOfWeek() - firstDayOfWeek;
618  if ( days < 0 )
619  days += 7;
620 
621  dt0 = dt0.addDays( -days );
622 
623  if ( type == QwtDate::FirstThursday )
624  {
625  // according to ISO 8601 the first week is defined
626  // by the first thursday.
627 
628  int d = Qt::Thursday - firstDayOfWeek;
629  if ( d < 0 )
630  d += 7;
631 
632  if ( dt0.addDays( d ).year() < year )
633  dt0 = dt0.addDays( 7 );
634  }
635 
636  return dt0;
637 }
638 
653 int QwtDate::weekNumber( const QDate &date, Week0Type type )
654 {
655  int weekNo;
656 
657  if ( type == QwtDate::FirstDay )
658  {
659  QDate day0;
660 
661  if ( date.month() == 12 && date.day() >= 24 )
662  {
663  // week 1 usually starts in the previous years.
664  // and we have to check if we are already there
665 
666  day0 = dateOfWeek0( date.year() + 1, type );
667  if ( day0.daysTo( date ) < 0 )
668  day0 = dateOfWeek0( date.year(), type );
669  }
670  else
671  {
672  day0 = dateOfWeek0( date.year(), type );
673  }
674 
675  weekNo = day0.daysTo( date ) / 7 + 1;
676  }
677  else
678  {
679  weekNo = date.weekNumber();
680  }
681 
682  return weekNo;
683 }
684 
703 int QwtDate::utcOffset( const QDateTime &dateTime )
704 {
705  int seconds = 0;
706 
707  switch( dateTime.timeSpec() )
708  {
709  case Qt::UTC:
710  {
711  break;
712  }
713  case Qt::OffsetFromUTC:
714  {
715  seconds = dateTime.utcOffset();
716  break;
717  }
718  default:
719  {
720  const QDateTime dt1( dateTime.date(), dateTime.time(), Qt::UTC );
721  seconds = dateTime.secsTo( dt1 );
722  }
723  }
724 
725  return seconds;
726 }
727 
750 QString QwtDate::toString( const QDateTime &dateTime,
751  const QString & format, Week0Type week0Type )
752 {
753  QString fmt = format;
754  if ( fmt.contains( 'w' ) )
755  {
756  fmt = qwtExpandedFormat( fmt, dateTime, week0Type );
757  }
758 
759  return dateTime.toString( fmt );
760 }
d
static Qt::DayOfWeek qwtFirstDayOfWeek()
Definition: qwt_date.cpp:117
The interval is related to weeks.
Definition: qwt_date.h:93
The interval is related to years.
Definition: qwt_date.h:99
The interval is related to months.
Definition: qwt_date.h:96
static QDateTime qwtToTimeSpec(const QDateTime &dt, Qt::TimeSpec spec)
Definition: qwt_date.cpp:235
static QDate maxDate()
Definition: qwt_date.cpp:589
static QDateTime floor(const QDateTime &, IntervalType)
Definition: qwt_date.cpp:493
static double toDouble(const QDateTime &)
Definition: qwt_date.cpp:366
static const QwtJulianDay maxJulianDayD
Definition: qwt_date.cpp:30
int QwtJulianDay
Definition: qwt_date.cpp:28
std::chrono::duration< std::int_fast64_t > seconds
IntervalType
Definition: qwt_date.h:75
The interval is related to minutes.
Definition: qwt_date.h:84
static const QwtJulianDay minJulianDayD
Definition: qwt_date.cpp:29
The interval is related to hours.
Definition: qwt_date.h:87
static qint64 qwtFloorDiv64(qint64 a, int b)
Definition: qwt_date.cpp:270
static int utcOffset(const QDateTime &)
Definition: qwt_date.cpp:703
The Julian day of "The Epoch".
Definition: qwt_date.h:105
The interval is related to milliseconds.
Definition: qwt_date.h:78
Week0Type
Definition: qwt_date.h:49
static QDateTime ceil(const QDateTime &, IntervalType)
Definition: qwt_date.cpp:391
static QString qwtExpandedFormat(const QString &format, const QDateTime &dateTime, QwtDate::Week0Type week0Type)
Definition: qwt_date.cpp:34
static int weekNumber(const QDate &, Week0Type)
Definition: qwt_date.cpp:653
static QDate dateOfWeek0(int year, Week0Type)
Date of the first day of the first week for a year.
Definition: qwt_date.cpp:610
static QDateTime toDateTime(double value, Qt::TimeSpec=Qt::UTC)
Definition: qwt_date.cpp:329
static QDate minDate()
Definition: qwt_date.cpp:567
static double qwtToJulianDay(int year, int month, int day)
Definition: qwt_date.cpp:257
static QString toString(const QDateTime &, const QString &format, Week0Type)
Definition: qwt_date.cpp:750
int i
The interval is related to seconds.
Definition: qwt_date.h:81
static void qwtFloorTime(QwtDate::IntervalType intervalType, QDateTime &dt)
Definition: qwt_date.cpp:197
static qint64 qwtFloorDiv(int a, int b)
Definition: qwt_date.cpp:278
static QDate qwtToDate(int year, int month=1, int day=1)
Definition: qwt_date.cpp:286
The interval is related to days.
Definition: qwt_date.h:90


plotjuggler
Author(s): Davide Faconti
autogenerated on Sat Jul 6 2019 03:44:17