00001
00002
00003
00004
00005
00006
00007
00008
00009
00010 #include "qwt_date.h"
00011 #include <qdebug.h>
00012 #include <qlocale.h>
00013 #include <math.h>
00014 #include <limits>
00015 #include <limits.h>
00016
00017 #if QT_VERSION >= 0x050000
00018
00019 typedef qint64 QwtJulianDay;
00020 static const QwtJulianDay minJulianDayD = Q_INT64_C( -784350574879 );
00021 static const QwtJulianDay maxJulianDayD = Q_INT64_C( 784354017364 );
00022
00023 #else
00024
00025
00026
00027
00028 typedef int QwtJulianDay;
00029 static const QwtJulianDay minJulianDayD = 1;
00030 static const QwtJulianDay maxJulianDayD = std::numeric_limits<int>::max();
00031
00032 #endif
00033
00034 static QString qwtExpandedFormat( const QString & format,
00035 const QDateTime &dateTime, QwtDate::Week0Type week0Type )
00036 {
00037 const int week = QwtDate::weekNumber( dateTime.date(), week0Type );
00038
00039 QString weekNo;
00040 weekNo.setNum( week );
00041
00042 QString weekNoWW;
00043 if ( weekNo.length() == 1 )
00044 weekNoWW += "0";
00045
00046 weekNoWW += weekNo;
00047
00048 QString fmt = format;
00049 fmt.replace( "ww", weekNoWW );
00050 fmt.replace( "w", weekNo );
00051
00052 if ( week == 1 && dateTime.date().month() != 1 )
00053 {
00054
00055
00056 static QString s_yyyy = "yyyy";
00057 static QString s_yy = "yy";
00058
00059
00060
00061 bool doReplaceYear = fmt.contains( s_yy );
00062
00063 if ( doReplaceYear )
00064 {
00065 if ( fmt.contains( 'M' ) )
00066 {
00067
00068
00069
00070 doReplaceYear = false;
00071 }
00072 else
00073 {
00074
00075
00076
00077 int numD = 0;
00078
00079 for ( int i = 0; i < fmt.size(); i++ )
00080 {
00081 if ( fmt[i] == 'd' )
00082 {
00083 numD++;
00084 }
00085 else
00086 {
00087 if ( numD > 0 && numD <= 2 )
00088 break;
00089
00090 numD = 0;
00091 }
00092 }
00093
00094 if ( numD > 0 && numD <= 2 )
00095 doReplaceYear = false;
00096 }
00097 }
00098
00099 if ( doReplaceYear )
00100 {
00101 const QDate dt( dateTime.date().year() + 1, 1, 1 );
00102
00103 if ( fmt.contains( s_yyyy ) )
00104 {
00105 fmt.replace( s_yyyy, dt.toString( s_yyyy ) );
00106 }
00107 else
00108 {
00109 fmt.replace( s_yy, dt.toString( s_yyyy ) );
00110 }
00111 }
00112 }
00113
00114 return fmt;
00115 }
00116
00117 static inline Qt::DayOfWeek qwtFirstDayOfWeek()
00118 {
00119 #if QT_VERSION >= 0x040800
00120 return QLocale().firstDayOfWeek();
00121 #else
00122
00123 switch( QLocale().country() )
00124 {
00125 case QLocale::Maldives:
00126 return Qt::Friday;
00127
00128 case QLocale::Afghanistan:
00129 case QLocale::Algeria:
00130 case QLocale::Bahrain:
00131 case QLocale::Djibouti:
00132 case QLocale::Egypt:
00133 case QLocale::Eritrea:
00134 case QLocale::Ethiopia:
00135 case QLocale::Iran:
00136 case QLocale::Iraq:
00137 case QLocale::Jordan:
00138 case QLocale::Kenya:
00139 case QLocale::Kuwait:
00140 case QLocale::LibyanArabJamahiriya:
00141 case QLocale::Morocco:
00142 case QLocale::Oman:
00143 case QLocale::Qatar:
00144 case QLocale::SaudiArabia:
00145 case QLocale::Somalia:
00146 case QLocale::Sudan:
00147 case QLocale::Tunisia:
00148 case QLocale::Yemen:
00149 return Qt::Saturday;
00150
00151 case QLocale::AmericanSamoa:
00152 case QLocale::Argentina:
00153 case QLocale::Azerbaijan:
00154 case QLocale::Botswana:
00155 case QLocale::Canada:
00156 case QLocale::China:
00157 case QLocale::FaroeIslands:
00158 case QLocale::Georgia:
00159 case QLocale::Greenland:
00160 case QLocale::Guam:
00161 case QLocale::HongKong:
00162 case QLocale::Iceland:
00163 case QLocale::India:
00164 case QLocale::Ireland:
00165 case QLocale::Israel:
00166 case QLocale::Jamaica:
00167 case QLocale::Japan:
00168 case QLocale::Kyrgyzstan:
00169 case QLocale::Lao:
00170 case QLocale::Malta:
00171 case QLocale::MarshallIslands:
00172 case QLocale::Macau:
00173 case QLocale::Mongolia:
00174 case QLocale::NewZealand:
00175 case QLocale::NorthernMarianaIslands:
00176 case QLocale::Pakistan:
00177 case QLocale::Philippines:
00178 case QLocale::RepublicOfKorea:
00179 case QLocale::Singapore:
00180 case QLocale::SyrianArabRepublic:
00181 case QLocale::Taiwan:
00182 case QLocale::Thailand:
00183 case QLocale::TrinidadAndTobago:
00184 case QLocale::UnitedStates:
00185 case QLocale::UnitedStatesMinorOutlyingIslands:
00186 case QLocale::USVirginIslands:
00187 case QLocale::Uzbekistan:
00188 case QLocale::Zimbabwe:
00189 return Qt::Sunday;
00190
00191 default:
00192 return Qt::Monday;
00193 }
00194 #endif
00195 }
00196
00197 static inline void qwtFloorTime(
00198 QwtDate::IntervalType intervalType, QDateTime &dt )
00199 {
00200
00201
00202
00203
00204 const Qt::TimeSpec timeSpec = dt.timeSpec();
00205
00206 if ( timeSpec == Qt::LocalTime )
00207 dt = dt.toTimeSpec( Qt::UTC );
00208
00209 const QTime t = dt.time();
00210 switch( intervalType )
00211 {
00212 case QwtDate::Second:
00213 {
00214 dt.setTime( QTime( t.hour(), t.minute(), t.second() ) );
00215 break;
00216 }
00217 case QwtDate::Minute:
00218 {
00219 dt.setTime( QTime( t.hour(), t.minute(), 0 ) );
00220 break;
00221 }
00222 case QwtDate::Hour:
00223 {
00224 dt.setTime( QTime( t.hour(), 0, 0 ) );
00225 break;
00226 }
00227 default:
00228 break;
00229 }
00230
00231 if ( timeSpec == Qt::LocalTime )
00232 dt = dt.toTimeSpec( Qt::LocalTime );
00233 }
00234
00235 static inline QDateTime qwtToTimeSpec(
00236 const QDateTime &dt, Qt::TimeSpec spec )
00237 {
00238 if ( dt.timeSpec() == spec )
00239 return dt;
00240
00241 const qint64 jd = dt.date().toJulianDay();
00242 if ( jd < 0 || jd >= INT_MAX )
00243 {
00244
00245
00246
00247
00248
00249 QDateTime dt2 = dt;
00250 dt2.setTimeSpec( spec );
00251 return dt2;
00252 }
00253
00254 return dt.toTimeSpec( spec );
00255 }
00256
00257 static inline double qwtToJulianDay( int year, int month, int day )
00258 {
00259
00260
00261
00262 const int m1 = ( month - 14 ) / 12;
00263 const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
00264 const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 );
00265
00266 return ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
00267 - ::floor( ( 3 * y1 ) / 4 ) + day - 32075;
00268 }
00269
00270 static inline qint64 qwtFloorDiv64( qint64 a, int b )
00271 {
00272 if ( a < 0 )
00273 a -= b - 1;
00274
00275 return a / b;
00276 }
00277
00278 static inline qint64 qwtFloorDiv( int a, int b )
00279 {
00280 if ( a < 0 )
00281 a -= b - 1;
00282
00283 return a / b;
00284 }
00285
00286 static inline QDate qwtToDate( int year, int month = 1, int day = 1 )
00287 {
00288 #if QT_VERSION >= 0x050000
00289 return QDate( year, month, day );
00290 #else
00291 if ( year > 100000 )
00292 {
00293
00294
00295
00296 const int m1 = ( month - 14 ) / 12;
00297 const int m2 = ( 367 * ( month - 2 - 12 * m1 ) ) / 12;
00298 const double y1 = ::floor( ( 4900.0 + year + m1 ) / 100 );
00299
00300 const double jd = ::floor( ( 1461.0 * ( year + 4800 + m1 ) ) / 4 ) + m2
00301 - ::floor( ( 3 * y1 ) / 4 ) + day - 32075;
00302
00303 if ( jd > maxJulianDayD )
00304 {
00305 qWarning() << "qwtToDate: overflow";
00306 return QDate();
00307 }
00308
00309 return QDate::fromJulianDay( static_cast<QwtJulianDay>( jd ) );
00310 }
00311 else
00312 {
00313 return QDate( year, month, day );
00314 }
00315 #endif
00316 }
00317
00329 QDateTime QwtDate::toDateTime( double value, Qt::TimeSpec timeSpec )
00330 {
00331 const int msecsPerDay = 86400000;
00332
00333 const double days = static_cast<qint64>( ::floor( value / msecsPerDay ) );
00334
00335 const double jd = QwtDate::JulianDayForEpoch + days;
00336 if ( ( jd > maxJulianDayD ) || ( jd < minJulianDayD ) )
00337 {
00338 qWarning() << "QwtDate::toDateTime: overflow";
00339 return QDateTime();
00340 }
00341
00342 const QDate d = QDate::fromJulianDay( static_cast<QwtJulianDay>( jd ) );
00343
00344 const int msecs = static_cast<int>( value - days * msecsPerDay );
00345
00346 static const QTime timeNull( 0, 0, 0, 0 );
00347
00348 QDateTime dt( d, timeNull.addMSecs( msecs ), Qt::UTC );
00349
00350 if ( timeSpec == Qt::LocalTime )
00351 dt = qwtToTimeSpec( dt, timeSpec );
00352
00353 return dt;
00354 }
00355
00366 double QwtDate::toDouble( const QDateTime &dateTime )
00367 {
00368 const int msecsPerDay = 86400000;
00369
00370 const QDateTime dt = qwtToTimeSpec( dateTime, Qt::UTC );
00371
00372 const double days = dt.date().toJulianDay() - QwtDate::JulianDayForEpoch;
00373
00374 const QTime time = dt.time();
00375 const double secs = 3600.0 * time.hour() +
00376 60.0 * time.minute() + time.second();
00377
00378 return days * msecsPerDay + time.msec() + 1000.0 * secs;
00379 }
00380
00391 QDateTime QwtDate::ceil( const QDateTime &dateTime, IntervalType intervalType )
00392 {
00393 if ( dateTime.date() >= QwtDate::maxDate() )
00394 return dateTime;
00395
00396 QDateTime dt = dateTime;
00397
00398 switch ( intervalType )
00399 {
00400 case QwtDate::Millisecond:
00401 {
00402 break;
00403 }
00404 case QwtDate::Second:
00405 {
00406 qwtFloorTime( QwtDate::Second, dt );
00407 if ( dt < dateTime )
00408 dt = dt.addSecs( 1 );
00409
00410 break;
00411 }
00412 case QwtDate::Minute:
00413 {
00414 qwtFloorTime( QwtDate::Minute, dt );
00415 if ( dt < dateTime )
00416 dt = dt.addSecs( 60 );
00417
00418 break;
00419 }
00420 case QwtDate::Hour:
00421 {
00422 qwtFloorTime( QwtDate::Hour, dt );
00423 if ( dt < dateTime )
00424 dt = dt.addSecs( 3600 );
00425
00426 break;
00427 }
00428 case QwtDate::Day:
00429 {
00430 dt.setTime( QTime( 0, 0 ) );
00431 if ( dt < dateTime )
00432 dt = dt.addDays( 1 );
00433
00434 break;
00435 }
00436 case QwtDate::Week:
00437 {
00438 dt.setTime( QTime( 0, 0 ) );
00439 if ( dt < dateTime )
00440 dt = dt.addDays( 1 );
00441
00442 int days = qwtFirstDayOfWeek() - dt.date().dayOfWeek();
00443 if ( days < 0 )
00444 days += 7;
00445
00446 dt = dt.addDays( days );
00447
00448 break;
00449 }
00450 case QwtDate::Month:
00451 {
00452 dt.setTime( QTime( 0, 0 ) );
00453 dt.setDate( qwtToDate( dateTime.date().year(),
00454 dateTime.date().month() ) );
00455
00456 if ( dt < dateTime )
00457 dt = dt.addMonths( 1 );
00458
00459 break;
00460 }
00461 case QwtDate::Year:
00462 {
00463 dt.setTime( QTime( 0, 0 ) );
00464
00465 const QDate d = dateTime.date();
00466
00467 int year = d.year();
00468 if ( d.month() > 1 || d.day() > 1 || !dateTime.time().isNull() )
00469 year++;
00470
00471 if ( year == 0 )
00472 year++;
00473
00474 dt.setDate( qwtToDate( year ) );
00475 break;
00476 }
00477 }
00478
00479 return dt;
00480 }
00481
00493 QDateTime QwtDate::floor( const QDateTime &dateTime,
00494 IntervalType intervalType )
00495 {
00496 if ( dateTime.date() <= QwtDate::minDate() )
00497 return dateTime;
00498
00499 QDateTime dt = dateTime;
00500
00501 switch ( intervalType )
00502 {
00503 case QwtDate::Millisecond:
00504 {
00505 break;
00506 }
00507 case QwtDate::Second:
00508 case QwtDate::Minute:
00509 case QwtDate::Hour:
00510 {
00511 qwtFloorTime( intervalType, dt );
00512 break;
00513 }
00514 case QwtDate::Day:
00515 {
00516 dt.setTime( QTime( 0, 0 ) );
00517 break;
00518 }
00519 case QwtDate::Week:
00520 {
00521 dt.setTime( QTime( 0, 0 ) );
00522
00523 int days = dt.date().dayOfWeek() - qwtFirstDayOfWeek();
00524 if ( days < 0 )
00525 days += 7;
00526
00527 dt = dt.addDays( -days );
00528
00529 break;
00530 }
00531 case QwtDate::Month:
00532 {
00533 dt.setTime( QTime( 0, 0 ) );
00534
00535 const QDate date = qwtToDate( dt.date().year(),
00536 dt.date().month() );
00537 dt.setDate( date );
00538
00539 break;
00540 }
00541 case QwtDate::Year:
00542 {
00543 dt.setTime( QTime( 0, 0 ) );
00544
00545 const QDate date = qwtToDate( dt.date().year() );
00546 dt.setDate( date );
00547
00548 break;
00549 }
00550 }
00551
00552 return dt;
00553 }
00554
00567 QDate QwtDate::minDate()
00568 {
00569 static QDate date;
00570 if ( !date.isValid() )
00571 date = QDate::fromJulianDay( minJulianDayD );
00572
00573 return date;
00574 }
00575
00589 QDate QwtDate::maxDate()
00590 {
00591 static QDate date;
00592 if ( !date.isValid() )
00593 date = QDate::fromJulianDay( maxJulianDayD );
00594
00595 return date;
00596 }
00597
00610 QDate QwtDate::dateOfWeek0( int year, Week0Type type )
00611 {
00612 const Qt::DayOfWeek firstDayOfWeek = qwtFirstDayOfWeek();
00613
00614 QDate dt0( year, 1, 1 );
00615
00616
00617 int days = dt0.dayOfWeek() - firstDayOfWeek;
00618 if ( days < 0 )
00619 days += 7;
00620
00621 dt0 = dt0.addDays( -days );
00622
00623 if ( type == QwtDate::FirstThursday )
00624 {
00625
00626
00627
00628 int d = Qt::Thursday - firstDayOfWeek;
00629 if ( d < 0 )
00630 d += 7;
00631
00632 if ( dt0.addDays( d ).year() < year )
00633 dt0 = dt0.addDays( 7 );
00634 }
00635
00636 return dt0;
00637 }
00638
00653 int QwtDate::weekNumber( const QDate &date, Week0Type type )
00654 {
00655 int weekNo;
00656
00657 if ( type == QwtDate::FirstDay )
00658 {
00659 QDate day0;
00660
00661 if ( date.month() == 12 && date.day() >= 24 )
00662 {
00663
00664
00665
00666 day0 = dateOfWeek0( date.year() + 1, type );
00667 if ( day0.daysTo( date ) < 0 )
00668 day0 = dateOfWeek0( date.year(), type );
00669 }
00670 else
00671 {
00672 day0 = dateOfWeek0( date.year(), type );
00673 }
00674
00675 weekNo = day0.daysTo( date ) / 7 + 1;
00676 }
00677 else
00678 {
00679 weekNo = date.weekNumber();
00680 }
00681
00682 return weekNo;
00683 }
00684
00703 int QwtDate::utcOffset( const QDateTime &dateTime )
00704 {
00705 int seconds = 0;
00706
00707 switch( dateTime.timeSpec() )
00708 {
00709 case Qt::UTC:
00710 {
00711 break;
00712 }
00713 case Qt::OffsetFromUTC:
00714 {
00715 seconds = dateTime.utcOffset();
00716 break;
00717 }
00718 default:
00719 {
00720 const QDateTime dt1( dateTime.date(), dateTime.time(), Qt::UTC );
00721 seconds = dateTime.secsTo( dt1 );
00722 }
00723 }
00724
00725 return seconds;
00726 }
00727
00750 QString QwtDate::toString( const QDateTime &dateTime,
00751 const QString & format, Week0Type week0Type )
00752 {
00753 QString fmt = format;
00754 if ( fmt.contains( 'w' ) )
00755 {
00756 fmt = qwtExpandedFormat( fmt, dateTime, week0Type );
00757 }
00758
00759 return dateTime.toString( fmt );
00760 }