qwt_date.cpp
Go to the documentation of this file.
00001 /* -*- mode: C++ ; c-file-style: "stroustrup" -*- *****************************
00002  * Qwt Widget Library
00003  * Copyright (C) 1997   Josef Wilgen
00004  * Copyright (C) 2002   Uwe Rathmann
00005  *
00006  * This library is free software; you can redistribute it and/or
00007  * modify it under the terms of the Qwt License, Version 1.0
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 // QDate stores the Julian day as unsigned int, but
00026 // but it is QDate::fromJulianDay( int ). That's why
00027 // we have the range [ 1, INT_MAX ]
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         // in case of week 1, we might need to increment the year
00055 
00056         static QString s_yyyy = "yyyy";
00057         static QString s_yy = "yy";
00058 
00059         // week 1 might start in the previous year
00060 
00061         bool doReplaceYear = fmt.contains( s_yy );
00062 
00063         if ( doReplaceYear )
00064         {
00065             if ( fmt.contains( 'M' ) )
00066             {
00067                 // in case of also having 'M' we have a conflict about
00068                 // which year to show
00069 
00070                 doReplaceYear = false;
00071             }
00072             else
00073             {
00074                 // in case of also having 'd' or 'dd' we have a conflict about
00075                 // which year to show
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     // when dt is inside the special hour where DST is ending
00201     // an hour is no unique. Therefore we have to
00202     // use UTC time.
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         // the conversion between local time and UTC
00245         // is internally limited. To avoid
00246         // overflows we simply ignore the difference
00247         // for those dates
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     // code from QDate but using doubles to avoid overflows
00260     // for large values
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         // code from QDate but using doubles to avoid overflows
00294         // for large values
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++; // there is no year 0
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     // floor to the first day of the week
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         // according to ISO 8601 the first week is defined
00626         // by the first thursday. 
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             // week 1 usually starts in the previous years.
00664             // and we have to check if we are already there
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 }


plotjuggler
Author(s): Davide Faconti
autogenerated on Fri Sep 1 2017 02:41:56