DateTime.cpp

Go to the documentation of this file.
00001 /*******************************************************************************
00002 * Copyright (C) 2004 Vintela, Inc. All rights reserved.
00003 * Copyright (C) 2005 Novell, Inc. All rights reserved.
00004 *
00005 * Redistribution and use in source and binary forms, with or without
00006 * modification, are permitted provided that the following conditions are met:
00007 *
00008 *  - Redistributions of source code must retain the above copyright notice,
00009 *    this list of conditions and the following disclaimer.
00010 *
00011 *  - Redistributions in binary form must reproduce the above copyright notice,
00012 *    this list of conditions and the following disclaimer in the documentation
00013 *    and/or other materials provided with the distribution.
00014 *
00015 *  - Neither the name of Vintela, Inc., Novell, Inc., nor the names of its
00016 *    contributors may be used to endorse or promote products derived from this
00017 *    software without specific prior written permission.
00018 *
00019 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS''
00020 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
00021 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
00022 * ARE DISCLAIMED. IN NO EVENT SHALL Vintela, Inc., Novell, Inc., OR THE 
00023 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 
00024 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 
00025 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 
00026 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 
00027 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 
00028 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 
00029 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
00030 *******************************************************************************/
00031 
00032 
00038 #include "blocxx/BLOCXX_config.h"
00039 #include "blocxx/DateTime.hpp"
00040 #include "blocxx/String.hpp"
00041 #include "blocxx/BinarySerialization.hpp"
00042 #include "blocxx/Format.hpp"
00043 #include "blocxx/Mutex.hpp"
00044 #include "blocxx/MutexLock.hpp"
00045 #include "blocxx/ExceptionIds.hpp"
00046 
00047 #if defined(BLOCXX_HAVE_ISTREAM) && defined(BLOCXX_HAVE_OSTREAM)
00048 #include <istream>
00049 #include <ostream>
00050 #else
00051 #include <iostream>
00052 #endif
00053 
00054 #include <time.h>
00055 #ifdef BLOCXX_HAVE_SYS_TIME_H
00056 #include <sys/time.h>
00057 #endif
00058 
00059 #include <cctype>
00060 
00061 
00062 #ifndef BLOCXX_HAVE_LOCALTIME_R
00063 namespace
00064 {
00065    BLOCXX_NAMESPACE::Mutex localtimeMutex;
00066 }
00067 struct tm *localtime_r(const time_t *timep, struct tm *result)
00068 {
00069    BLOCXX_NAMESPACE::MutexLock lock(localtimeMutex);
00070    struct tm *p = localtime(timep);
00071    
00072    if (p)
00073    {
00074       *(result) = *p;
00075    }
00076    
00077    return p;
00078 }
00079 #endif
00080 
00081 #ifndef BLOCXX_HAVE_GMTIME_R
00082 namespace
00083 {
00084    BLOCXX_NAMESPACE::Mutex gmtimeMutex;
00085 }
00086 struct tm *gmtime_r(const time_t *timep, struct tm *result)
00087 {
00088    BLOCXX_NAMESPACE::MutexLock lock(gmtimeMutex);
00089    struct tm *p = gmtime(timep);
00090    
00091    if (p)
00092    {
00093       *(result) = *p;
00094    }
00095    
00096    return p;
00097 }
00098 #endif
00099 
00100 #ifndef BLOCXX_HAVE_ASCTIME_R
00101 namespace
00102 {
00103    BLOCXX_NAMESPACE::Mutex asctimeMutex;
00104 }
00105 char *asctime_r(const struct tm *tm, char *result)
00106 {
00107    BLOCXX_NAMESPACE::MutexLock lock(asctimeMutex);
00108    char *p = asctime(tm);
00109    
00110    if (p)
00111    {
00112       //asctime_r requires a buffer to be at least 26 chars in size
00113       ::strncpy(result,p,25);
00114       result[25] = 0;
00115    }
00116    
00117    return result;
00118 }
00119 #endif
00120 
00121 namespace BLOCXX_NAMESPACE
00122 {
00123 
00124 using std::istream;
00125 using std::ostream;
00126 
00128 BLOCXX_DEFINE_EXCEPTION_WITH_ID(DateTime);
00129 
00131 DateTime::DateTime()
00132    : m_time(0)
00133    , m_microseconds(0)
00134 
00135 {
00136 }
00138 namespace
00139 {
00140 
00141 inline void badDateTime(const String& str)
00142 {
00143    BLOCXX_THROW(DateTimeException, Format("Invalid DateTime: %1", str).c_str());
00144 }
00145 
00146 inline void validateRanges(Int32 year, Int32 month, Int32 day, Int32 hour,
00147 Int32 minute, Int32 second, Int32 microseconds, const String& str)
00148 {
00149    if (year < 0 || year > 9999 ||
00150    month < 1 || month > 12 ||
00151    day < 1 || day > 31 ||
00152    hour < 0 || hour > 23 ||
00153    minute < 0 || minute > 59 ||
00154    second < 0 || second > 60 ||
00155    microseconds < 0 || microseconds > 999999)
00156    {
00157       badDateTime(str);
00158    }
00159 }
00160 
00161 inline bool isDOWValid(const char* str)
00162 {
00163    // a little FSM to validate the day of the week
00164    bool good = true;
00165    if (str[0] == 'S') // Sun, Sat
00166    {
00167       if (str[1] == 'u')
00168       {
00169          if (str[2] != 'n') // Sun
00170          {
00171             good = false;
00172          }
00173       }
00174       else if (str[1] ==  'a')
00175       {
00176          if (str[2] != 't') // Sat
00177          {
00178             good = false;
00179          }
00180       }
00181       else
00182       {
00183          good = false;
00184       }
00185    }
00186    else if (str[0] == 'M') // Mon
00187    {
00188       if (str[1] == 'o')
00189       {
00190          if (str[2] != 'n')
00191          {
00192             good = false;
00193          }
00194       }
00195       else
00196       {
00197          good = false;
00198       }
00199    }
00200    else if (str[0] == 'T') // Tue, Thu
00201    {
00202       if (str[1] == 'u')
00203       {
00204          if (str[2] != 'e') // Tue
00205          {
00206             good = false;
00207          }
00208       }
00209       else if (str[1] ==  'h')
00210       {
00211          if (str[2] != 'u') // Thu
00212          {
00213             good = false;
00214          }
00215       }
00216       else
00217       {
00218          good = false;
00219       }
00220    }
00221    else if (str[0] == 'W') // Wed
00222    {
00223       if (str[1] == 'e')
00224       {
00225          if (str[2] != 'd')
00226          {
00227             good = false;
00228          }
00229       }
00230       else
00231       {
00232          good = false;
00233       }
00234    }
00235    else if (str[0] == 'F') // Fri
00236    {
00237       if (str[1] == 'r')
00238       {
00239          if (str[2] != 'i')
00240          {
00241             good = false;
00242          }
00243       }
00244       else
00245       {
00246          good = false;
00247       }
00248    }
00249    else
00250    {
00251       good = false;
00252    }
00253 
00254    return good;
00255 }
00256 
00257 inline bool isLongDOWValid(const String& s)
00258 {
00259    if ( (s == "Sunday") ||
00260    (s == "Monday") ||
00261    (s == "Tuesday") ||
00262    (s == "Wednesday") ||
00263    (s == "Thursday") ||
00264    (s == "Friday") ||
00265    (s == "Saturday") )
00266    {
00267       return true;
00268    }
00269    return false;
00270 }
00271 
00272 // returns -1 if the month is invalid, 1-12 otherwise
00273 inline int decodeShortMonth(const char* str)
00274 {
00275    // a little FSM to calculate the month
00276    if (str[0] == 'J') // Jan, Jun, Jul
00277    {
00278       if (str[1] == 'a')
00279       {
00280          if (str[2] == 'n') // Jan
00281          {
00282             return 1;
00283          }
00284       }
00285       else if (str[1] ==  'u')
00286       {
00287          if (str[2] == 'n') // Jun
00288          {
00289             return 6;
00290          }
00291          else if (str[2] == 'l') // Jul
00292          {
00293             return 7;
00294          }
00295       }
00296    }
00297    else if (str[0] == 'F') // Feb
00298    {
00299       if (str[1] == 'e' && str[2] == 'b')
00300       {
00301          return 2;
00302       }
00303    }
00304    else if (str[0] == 'M') // Mar, May
00305    {
00306       if (str[1] == 'a')
00307       {
00308          if (str[2] == 'r') // Mar
00309          {
00310             return 3;
00311          }
00312          else if (str[2] == 'y') // May
00313          {
00314             return 5;
00315          }
00316       }
00317    }
00318    else if (str[0] == 'A') // Apr, Aug
00319    {
00320       if (str[1] == 'p')
00321       {
00322          if (str[2] == 'r') // Apr
00323          {
00324             return 4;
00325          }
00326       }
00327       else if (str[1] == 'u')
00328       {
00329          if (str[2] == 'g') // Aug
00330          {
00331             return 8;
00332          }
00333       }
00334    }
00335    else if (str[0] == 'S') // Sep
00336    {
00337       if (str[1] == 'e' && str[2] == 'p')
00338       {
00339          return 9;
00340       }
00341    }
00342    else if (str[0] == 'O') // Oct
00343    {
00344       if (str[1] == 'c' && str[2] == 't')
00345       {
00346          return 10;
00347       }
00348    }
00349    else if (str[0] == 'N') // Nov
00350    {
00351       if (str[1] == 'o' && str[2] == 'v')
00352       {
00353          return 11;
00354       }
00355    }
00356    else if (str[0] == 'D') // Dec
00357    {
00358       if (str[1] == 'e' && str[2] == 'c')
00359       {
00360          return 12;
00361       }
00362    }
00363 
00364    return -1;
00365 }
00366 
00367 // returns -1 if the month is invalid, 1-12 otherwise
00368 inline int decodeLongMonth(const String& str)
00369 {
00370    if ( str.equals("January") )
00371    {
00372       return 1;
00373    }
00374    else if ( str.equals("February") )
00375    {
00376       return 2;
00377    }
00378    else if ( str.equals("March") )
00379    {
00380       return 3;
00381    }
00382    else if ( str.equals("April") )
00383    {
00384       return 4;
00385    }
00386    else if ( str.equals("May") )
00387    {
00388       return 5;
00389    }
00390    else if ( str.equals("June") )
00391    {
00392       return 6;
00393    }
00394    else if ( str.equals("July") )
00395    {
00396       return 7;
00397    }
00398    else if ( str.equals("August") )
00399    {
00400       return 8;
00401    }
00402    else if ( str.equals("September") )
00403    {
00404       return 9;
00405    }
00406    else if ( str.equals("October") )
00407    {
00408       return 10;
00409    }
00410    else if ( str.equals("November") )
00411    {
00412       return 11;
00413    }
00414    else if ( str.equals("December") )
00415    {
00416       return 12;
00417    }
00418    return -1;
00419 }
00420 
00421 // Get the timezone offset (from UTC) for the given timezone.  Valid results
00422 // are in the range -12 to 12, except for the case where LOCAL_TIME_OFFSET is
00423 // returned, in which case UTC should not be used.
00424 const int LOCAL_TIME_OFFSET = -24;
00425 bool getTimeZoneOffset(const String& timezone, int& offset)
00426 {
00427    int temp_offset = LOCAL_TIME_OFFSET -1;
00428    if ( timezone.length() == 1 )
00429    {
00430       // Single-letter abbrev.
00431       // This could be simplified into a couple of if statements with some
00432       // character math, but this should work for now.
00433       switch ( timezone[0] )
00434       {
00435          case 'Y': // Yankee   UTC-12
00436             temp_offset = -12;
00437             break;
00438          case 'X': // Xray     UTC-11
00439             temp_offset = -11;
00440             break;
00441          case 'W': // Whiskey  UTC-10
00442             temp_offset = -10;
00443             break;
00444          case 'V': // Victor   UTC-9
00445             temp_offset = -9;
00446             break;
00447          case 'U': // Uniform  UTC-8
00448             temp_offset = -8;
00449             break;
00450          case 'T': // Tango    UTC-7
00451             temp_offset = -7;
00452             break;
00453          case 'S': // Sierra   UTC-6
00454             temp_offset = -6;
00455             break;
00456          case 'R': // Romeo    UTC-5
00457             temp_offset = -5;
00458             break;
00459          case 'Q': // Quebec   UTC-4
00460             temp_offset = -4;
00461             break;
00462          case 'P': // Papa     UTC-3
00463             temp_offset = -3;
00464             break;
00465          case 'O': // Oscar    UTC-2
00466             temp_offset = -2;
00467             break;
00468          case 'N': // November UTC-1
00469             temp_offset = -1;
00470             break;
00471          case 'Z': // Zulu     UTC
00472             temp_offset = 0;
00473             break;
00474          case 'A': // Aplpha   UTC+1
00475             temp_offset = 1;
00476             break;
00477          case 'B': // Bravo    UTC+2
00478             temp_offset = 2;
00479             break;
00480          case 'C': // Charlie  UTC+3
00481             temp_offset = 3;
00482             break;
00483          case 'D': // Delta    UTC+4
00484             temp_offset = 4;
00485             break;
00486          case 'E': // Echo     UTC+5
00487             temp_offset = 5;
00488             break;
00489          case 'F': // Foxtrot  UTC+6
00490             temp_offset = 6;
00491             break;
00492          case 'G': // Golf     UTC+7
00493             temp_offset = 7;
00494             break;
00495          case 'H': // Hotel    UTC+8
00496             temp_offset = 8;
00497             break;
00498          case 'I': // India    UTC+9
00499             temp_offset = 9;
00500             break;
00501          case 'K': // Kilo     UTC+10
00502             temp_offset = 10;
00503             break;
00504          case 'L': // Lima     UTC+11
00505             temp_offset = 11;
00506             break;
00507          case 'M': // Mike     UTC+12
00508             temp_offset = 12;
00509             break;
00510          case 'J': // Juliet   Always local time
00511             temp_offset = LOCAL_TIME_OFFSET;
00512             break;
00513          default:
00514             break;
00515       }
00516    }
00517    else if ( timezone == "UTC" ) // Universal Time Coordinated, civil time
00518    {
00519       temp_offset = 0;
00520    }
00521    // European timezones
00522    else if ( timezone == "GMT" ) // Greenwich Mean Time   UTC
00523    {
00524       temp_offset = 0;
00525    }
00526    else if ( timezone == "BST" ) // British Summer Time   UTC+1
00527    {
00528       temp_offset = 1;
00529    }
00530    else if ( timezone == "IST" ) // Irish Summer Time     UTC+1
00531    {
00532       temp_offset = 1;
00533    }
00534    else if ( timezone == "WET" ) // Western Europe Time   UTC
00535    {
00536       temp_offset = 0;
00537    }
00538    else if ( timezone == "WEST" ) // Western Europe Summer Time   UTC+1
00539    {
00540       temp_offset = 1;
00541    }
00542    else if ( timezone == "CET" ) // Central Europe Time   UTC+1
00543    {
00544       temp_offset = 1;
00545    }
00546    else if ( timezone == "CEST" ) // Central Europe Summer Time   UTC+2
00547    {
00548       temp_offset = 2;
00549    }
00550    else if ( timezone == "EET" ) // Eastern Europe Time   UTC+2
00551    {
00552       temp_offset = 2;
00553    }
00554    else if ( timezone == "EEST" ) // Eastern Europe Summer Time   UTC+3
00555    {
00556       temp_offset = 3;
00557    }
00558    else if ( timezone == "MSK" ) // Moscow Time   UTC+3
00559    {
00560       temp_offset = 3;
00561    }
00562    else if ( timezone == "MSD" ) // Moscow Summer Time    UTC+4
00563    {
00564       temp_offset = 4;
00565    }
00566    // US and Canada
00567    else if ( timezone == "AST" ) // Atlantic Standard Time        UTC-4
00568    {
00569       temp_offset = -4;
00570    }
00571    else if ( timezone == "ADT" ) // Atlantic Daylight Saving Time UTC-3
00572    {
00573       temp_offset = -3;
00574    }
00575    else if ( timezone == "EST" ) // Eastern Standard Time         UTC-5
00576    {
00577       // CHECKME! This can also be Australian Eastern Standard Time UTC+10
00578       // (UTC+11 in Summer)
00579       temp_offset = -5;
00580    }
00581    else if ( timezone == "EDT" ) // Eastern Daylight Saving Time  UTC-4
00582    {
00583       temp_offset = -4;
00584    }
00585    else if ( timezone == "ET" ) // Eastern Time, either as EST or EDT
00586    // depending on place and time of year
00587    {
00588       // CHECKME! Assuming standard time.
00589       temp_offset = -5;
00590    }
00591    else if ( timezone == "CST" ) // Central Standard Time         UTC-6
00592    {
00593       // CHECKME! This can also be Australian Central Standard Time UTC+9.5
00594       temp_offset = -6;
00595    }
00596    else if ( timezone == "CDT" ) // Central Daylight Saving Time  UTC-5
00597    {
00598       temp_offset = -5;
00599    }
00600    else if ( timezone == "CT" ) // Central Time, either as CST or CDT
00601    // depending on place and time of year
00602    {
00603       // CHECKME! Assuming standard time.
00604       temp_offset = -6;
00605    }
00606    else if ( timezone == "MST" ) // Mountain Standard Time        UTC-7
00607    {
00608       temp_offset = -7;
00609    }
00610    else if ( timezone == "MDT" ) // Mountain Daylight Saving Time UTC-6
00611    {
00612       temp_offset = -6;
00613    }
00614    else if ( timezone == "MT" ) // Mountain Time, either as MST or MDT
00615    // depending on place and time of year
00616    {
00617       // CHECKME! Assuming standard time.
00618       temp_offset = -7;
00619    }
00620    else if ( timezone == "PST" ) // Pacific Standard Time         UTC-8
00621    {
00622       temp_offset = -8;
00623    }
00624    else if ( timezone == "PDT" ) // Pacific Daylight Saving Time  UTC-7
00625    {
00626       temp_offset = -7;
00627    }
00628    else if ( timezone == "PT" ) // Pacific Time, either as PST or PDT
00629    // depending on place and time of year
00630    {
00631       // CHECKME! Assuming standard time.
00632       temp_offset = -8;
00633    }
00634    else if ( timezone == "HST" ) // Hawaiian Standard Time        UTC-10
00635    {
00636       temp_offset = -10;
00637    }
00638    else if ( timezone == "AKST" ) // Alaska Standard Time         UTC-9
00639    {
00640       temp_offset = -9;
00641    }
00642    else if ( timezone == "AKDT" ) // Alaska Standard Daylight Saving Time UTC-8
00643    {
00644       temp_offset = -8;
00645    }
00646    // Australia
00647    else if ( timezone == "WST" ) // Western Standard Time         UTC+8
00648    {
00649       temp_offset = 8;
00650    }
00651 
00652    // Check the results of that huge mess.
00653    if ( temp_offset >= LOCAL_TIME_OFFSET )
00654    {
00655       offset = temp_offset;
00656       return true;
00657    }
00658    return false;
00659 }
00660 
00661 Int32 getDaysPerMonth(Int32 year, Int32 month)
00662 {
00663    const Int32 normal_days_per_month[12] =
00664    { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
00665 
00666    if ( (month >= 1) && (month <= 12) )
00667    {
00668       if ( month != 2 )
00669       {
00670          return normal_days_per_month[month - 1];
00671       }
00672 
00673       int leap_year_adjust = 0;
00674 
00675       if ( (year % 4) == 0 )
00676       {
00677          // Possibly a leap year.
00678          if ( (year % 100) == 0 )
00679          {
00680             if ( (year % 400) == 0 )
00681             {
00682                leap_year_adjust = 1;
00683             }
00684          }
00685          else
00686          {
00687             leap_year_adjust = 1;
00688          }
00689       }
00690 
00691       return normal_days_per_month[month - 1] + leap_year_adjust;
00692       // Check to see if it's a leap year.
00693    }
00694    return 0;
00695 }
00696 
00697 // Adjust the time given (year, month, day, hour) for the given timezone
00698 // offset.
00699 // Note: this is converting FROM local time to UTC, so the timezone offset is
00700 // subtracted instead of added.
00701 void adjustTimeForTimeZone(Int32 timezone_offset, Int32& year, Int32& month,
00702 Int32& day, Int32& hour)
00703 {
00704    if ( timezone_offset < 0 )
00705    {
00706       hour -= timezone_offset;
00707 
00708       if ( hour > 23 )
00709       {
00710          ++day;
00711          hour -= 24;
00712       }
00713       // This assumes that the timezone will not shove a date by more than one day.
00714       if ( day > getDaysPerMonth(year, month) )
00715       {
00716          ++month;
00717          day = 1;
00718       }
00719       if ( month > 12 )
00720       {
00721          month -= 12;
00722          ++year;
00723       }
00724    }
00725    else if ( timezone_offset > 0 )
00726    {
00727       hour -= timezone_offset;
00728 
00729       if ( hour < 0 )
00730       {
00731          --day;
00732          hour += 24;
00733       }
00734       // This assumes that the timezone will not shove a date by more than one day.
00735       if ( day < 1 )
00736       {
00737          --month;
00738          day += getDaysPerMonth(year, month);
00739       }
00740       if ( month < 1 )
00741       {
00742          month += 12;
00743          --year;
00744       }
00745    }
00746 }
00747 
00748 
00749 } // end anonymous namespace
00750 
00752 DateTime::DateTime(const String& str)
00753 {
00754    // CIM format
00755    if ( str.length() == 25 )
00756    {
00757       // validate required characters
00758       if (  !(str[14] != '.' || (str[21] != '+' && str[21] != '-')) )
00759       {
00760          try
00761          {
00762             // in CIM, "Fields which are not significant must be
00763             // replaced with asterisk characters."  We'll convert
00764             // asterisks to 0s so we can process them.
00765             String strNoAsterisks(str);
00766             for (size_t i = 0; i < strNoAsterisks.length(); ++i)
00767             {
00768                if (strNoAsterisks[i] == '*')
00769                {
00770                   strNoAsterisks[i] = '0';
00771                }
00772             }
00773             Int32 year = strNoAsterisks.substring(0, 4).toInt32();
00774             Int32 month = strNoAsterisks.substring(4, 2).toInt32();
00775             Int32 day = strNoAsterisks.substring(6, 2).toInt32();
00776             Int32 hour = strNoAsterisks.substring(8, 2).toInt32();
00777             Int32 minute = strNoAsterisks.substring(10, 2).toInt32();
00778             Int32 second = strNoAsterisks.substring(12, 2).toInt32();
00779             Int32 microseconds = strNoAsterisks.substring(15, 6).toInt32();
00780 
00781             validateRanges(year, month, day, hour, minute, second, microseconds, str);
00782 
00783             Int32 utc = strNoAsterisks.substring(22, 3).toInt32();
00784             // adjust the time to utc.  According to the CIM spec:
00785             // "utc is the offset from UTC in minutes"
00786             if (str[21] == '+')
00787             {
00788                utc = 0 - utc;
00789             }
00790             minute += utc;
00791 
00792             set(year, month, day, hour, minute, second,
00793             microseconds, E_UTC_TIME);
00794             return;
00795          }
00796          catch (StringConversionException&)
00797          {
00798             // Instead of throwing another exception here, we'll try to parse it in
00799             // a more general way below.
00800          }
00801       }
00802    }
00803 
00804    // It didn't return from above, so it's not a CIM datetime.  Try to parse
00805    // it as a free-form date string.
00806    if ( !str.empty() )
00807    {
00808       // This is a general method of extracting the date.
00809       // It still assumes english names for months and days of week.
00810 
00811       String weekday;
00812       String day;
00813       String time;
00814       int timezone_number = LOCAL_TIME_OFFSET - 1;
00815       Int32 month_number = -1;
00816       String year;
00817 
00818       StringArray tokenized_date = str.tokenize();
00819 
00820       // Attempt to fill in the above list of strings...
00821       for ( StringArray::const_iterator date_token = tokenized_date.begin();
00822       date_token != tokenized_date.end();
00823       ++date_token )
00824       {
00825          // Check to see if it's a day of the week.
00826          if ( isDOWValid( date_token->c_str() ) )
00827          {
00828             if ( weekday.empty() )
00829             {
00830                if ( date_token->length() > 3 )
00831                {
00832                   if ( isLongDOWValid( *date_token ) )
00833                   {
00834                      weekday = *date_token;
00835                   }
00836                   else
00837                   {
00838                      // Invalid long day of week
00839                      badDateTime(str);
00840                   }
00841                }
00842                else
00843                {
00844                   weekday = *date_token;
00845                }
00846             }
00847             else
00848             {
00849                // Multiple weekdays.
00850                badDateTime(str);
00851             }
00852          }
00853          // Only do this comparison if a month has not already been found.
00854          else if ( (month_number == -1) &&
00855          (month_number = decodeShortMonth( date_token->c_str() ) ) != -1 )
00856          {
00857             if ( date_token->length() > 3 )
00858             {
00859                month_number = decodeLongMonth( date_token->c_str() );
00860 
00861                if ( month_number == -1 )
00862                {
00863                   // Invalid characters in the long version of the month.
00864                   badDateTime(str);
00865                }
00866             }
00867          }
00868          // Get the time, if the time wasn't already set.
00869          else if ( time.empty() && (date_token->indexOf(":") != String::npos) )
00870          {
00871             // This will be checked below... Assume it's correct.
00872             time = *date_token;
00873          }
00874          // If a day hasn't been found, and this is a number, assume it's the day.
00875          else if ( day.empty() && isdigit((*date_token)[0]) )
00876          {
00877             day = *date_token;
00878          }
00879          // If a year hasn't been found, and this is a number, assume it's the year.
00880          else if ( year.empty() && isdigit((*date_token)[0]) )
00881          {
00882             year = *date_token;
00883          }
00884          else if ( (timezone_number <= LOCAL_TIME_OFFSET) &&
00885          (date_token->length() >= 1) &&
00886          (date_token->length() <= 4) &&
00887          getTimeZoneOffset(*date_token, timezone_number) )
00888          {
00889             // Matched the timezone (nothing to do, it's already been set).
00890          }
00891          else
00892          {
00893             badDateTime(str);
00894          }
00895 
00896       } // for each token.
00897 
00898 
00899       // Done looking at tokens.  Verify that all the required fields are present.
00900       if ( (month_number >= 1) && !day.empty() && !time.empty() && !year.empty() )
00901       {
00902          // We've got enough to construct the date.
00903 
00904          // Parse the time
00905          StringArray time_fields = time.tokenize(":");
00906 
00907          // We need at least the hour and minute, anything other than H:M:S should
00908          // be in error.
00909          if ( (time_fields.size() < 2) || (time_fields.size() > 3) )
00910          {
00911             badDateTime(str);
00912          }
00913 
00914          try
00915          {
00916 
00917             Int32 hour;
00918             Int32 minute;
00919             Int32 second = 0;
00920             UInt32 microseconds = 0;
00921             Int32 year_number = year.toInt32();
00922             Int32 day_number = day.toInt32();
00923 
00924             hour = time_fields[0].toInt32();
00925             minute = time_fields[1].toInt32();
00926 
00927             if ( time_fields.size() == 3 )
00928             {
00929                second = time_fields[2].toInt32();
00930             }
00931 
00932             validateRanges(year_number, month_number, day_number,
00933             hour, minute, second, microseconds, str);
00934 
00935             if ( timezone_number <= LOCAL_TIME_OFFSET )
00936             {
00937                set(year_number, month_number, day_number, hour,
00938                minute, second, microseconds, E_LOCAL_TIME);
00939             }
00940             else
00941             {
00942                // Adjust the time for the timezone.
00943                // The current numbers have already been validated, so any changes
00944                // should not do anything unexpected.
00945 
00946                adjustTimeForTimeZone(timezone_number, year_number, month_number, day_number, hour);
00947 
00948                // Check again.
00949                validateRanges(year_number, month_number, day_number, hour,
00950                   minute, second, microseconds, str);
00951 
00952                set(year_number, month_number, day_number, hour,
00953                   minute, second, microseconds, E_UTC_TIME);
00954             }
00955          }
00956          catch (const StringConversionException&)
00957          {
00958             badDateTime(str);
00959          }
00960       }
00961       else
00962       {
00963          // Not all required fields available.
00964          badDateTime(str);
00965       }
00966    }
00967    else
00968    {
00969       // An empty string.
00970       badDateTime(str);
00971    }
00972 }
00974 DateTime::DateTime(time_t t, UInt32 microseconds)
00975    : m_time(t)
00976    , m_microseconds(microseconds)
00977 {
00978 }
00980 DateTime::DateTime(int year, int month, int day, int hour, int minute,
00981    int second, UInt32 microseconds, ETimeOffset timeOffset)
00982 {
00983    set(year, month, day, hour, minute, second, microseconds, timeOffset);
00984 }
00986 DateTime::~DateTime()
00987 {
00988 }
00990 inline tm
00991 DateTime::getTm(ETimeOffset timeOffset) const
00992 {
00993    if (timeOffset == E_LOCAL_TIME)
00994    {
00995       tm theTime;
00996       localtime_r(&m_time, &theTime);
00997       return theTime;
00998    }
00999    else // timeOffset == E_UTC_TIME
01000    {
01001       tm theTime;
01002       gmtime_r(&m_time, &theTime);
01003       return theTime;
01004    }
01005 }
01006 
01008 inline void
01009 DateTime::setTime(tm& tmarg, ETimeOffset timeOffset)
01010 {
01011    if (timeOffset == E_LOCAL_TIME)
01012    {
01013       m_time = ::mktime(&tmarg);
01014    }
01015    else // timeOffset == E_UTC_TIME
01016    {
01017 #ifdef BLOCXX_HAVE_TIMEGM
01018       m_time = ::timegm(&tmarg);
01019 #else
01020       // timezone is a global that is set by mktime() which is "the
01021       // difference, in seconds, between Coordinated Universal Time
01022       // (UTC) and local standard time."
01023 #ifdef BLOCXX_NETWARE
01024       m_time = ::mktime(&tmarg) - _timezone;
01025 #else
01026       m_time = ::mktime(&tmarg) - ::timezone;
01027 #endif
01028 #endif
01029    }
01030    // apparently some implementations of timegm return something other than -1 on error, but still < 0...
01031    if (m_time < 0)
01032    {
01033       char buff[30];
01034       String extraError;
01035 
01036       if( tmarg.tm_wday < 0 || tmarg.tm_wday > 6 )
01037       {
01038          extraError += Format("Invalid weekday: %1. ", tmarg.tm_wday);
01039          tmarg.tm_wday = 0;
01040       }
01041 
01042       if( tmarg.tm_mon < 0 || tmarg.tm_mon > 11 )
01043       {
01044          extraError += Format("Invalid month: %1. ", tmarg.tm_mon);
01045          tmarg.tm_mon = 0;
01046       }
01047 
01048       asctime_r(&tmarg, buff);
01049 
01050       BLOCXX_THROW(DateTimeException, Format("Unable to represent time \"%1\" as a time_t. %2", buff, extraError).toString().rtrim().c_str());
01051    }
01052 }
01054 int
01055 DateTime::getHour(ETimeOffset timeOffset) const
01056 {
01057    return getTm(timeOffset).tm_hour;
01058 }
01060 int
01061 DateTime::getMinute(ETimeOffset timeOffset) const
01062 {
01063    return getTm(timeOffset).tm_min;
01064 }
01066 int
01067 DateTime::getSecond(ETimeOffset timeOffset) const
01068 {
01069    return getTm(timeOffset).tm_sec;
01070 }
01072 UInt32
01073 DateTime::getMicrosecond() const
01074 {
01075    return m_microseconds;
01076 }
01078 int
01079 DateTime::getDay(ETimeOffset timeOffset) const
01080 {
01081    return getTm(timeOffset).tm_mday;
01082 }
01084 int
01085 DateTime::getDow(ETimeOffset timeOffset) const
01086 {
01087    return getTm(timeOffset).tm_wday;
01088 }
01090 int
01091 DateTime::getMonth(ETimeOffset timeOffset) const
01092 {
01093    return getTm(timeOffset).tm_mon+1;
01094 }
01096 int
01097 DateTime::getYear(ETimeOffset timeOffset) const
01098 {
01099    return (getTm(timeOffset).tm_year + 1900);
01100 }
01102 time_t
01103 DateTime::get() const
01104 {
01105    return m_time;
01106 }
01108 void
01109 DateTime::setHour(int hour, ETimeOffset timeOffset)
01110 {
01111    tm theTime = getTm(timeOffset);
01112    theTime.tm_hour = hour;
01113    setTime(theTime, timeOffset);
01114 }
01116 void
01117 DateTime::setMinute(int minute, ETimeOffset timeOffset)
01118 {
01119    tm theTime = getTm(timeOffset);
01120    theTime.tm_min = minute;
01121    setTime(theTime, timeOffset);
01122 }
01124 void
01125 DateTime::setSecond(int second, ETimeOffset timeOffset)
01126 {
01127    tm theTime = getTm(timeOffset);
01128    theTime.tm_sec = second;
01129    setTime(theTime, timeOffset);
01130 }
01132 void
01133 DateTime::setMicrosecond(UInt32 microseconds)
01134 {
01135    if (microseconds > 999999)
01136    {
01137       BLOCXX_THROW(DateTimeException, Format("invalid microseconds: %1", microseconds).c_str());
01138    }
01139    m_microseconds = microseconds;
01140 }
01142 void
01143 DateTime::setTime(int hour, int minute, int second, ETimeOffset timeOffset)
01144 {
01145    tm theTime = getTm(timeOffset);
01146    theTime.tm_hour = hour;
01147    theTime.tm_min = minute;
01148    theTime.tm_sec = second;
01149    setTime(theTime, timeOffset);
01150 }
01152 void
01153 DateTime::setDay(int day, ETimeOffset timeOffset)
01154 {
01155    tm theTime = getTm(timeOffset);
01156    theTime.tm_mday = day;
01157    setTime(theTime, timeOffset);
01158 }
01160 void
01161 DateTime::setMonth(int month, ETimeOffset timeOffset)
01162 {
01163    if (month == 0)
01164    {
01165       BLOCXX_THROW(DateTimeException, "invalid month: 0");
01166    }
01167 
01168    tm theTime = getTm(timeOffset);
01169    theTime.tm_mon = month-1;
01170    setTime(theTime, timeOffset);
01171 }
01173 void
01174 DateTime::setYear(int year, ETimeOffset timeOffset)
01175 {
01176    tm theTime = getTm(timeOffset);
01177    theTime.tm_year = year - 1900;
01178    setTime(theTime, timeOffset);
01179 }
01181 void
01182 DateTime::set(int year, int month, int day, int hour, int minute, int second,
01183    UInt32 microseconds, ETimeOffset timeOffset)
01184 {
01185    tm tmarg;
01186    memset(&tmarg, 0, sizeof(tmarg));
01187    tmarg.tm_year = (year >= 1900) ? year - 1900 : year;
01188    tmarg.tm_mon = month-1;
01189    tmarg.tm_mday = day;
01190    tmarg.tm_hour = hour;
01191    tmarg.tm_min = minute;
01192    tmarg.tm_sec = second;
01193    if (timeOffset == E_UTC_TIME)
01194    {
01195       tmarg.tm_isdst = 0; // don't want dst applied to utc time!
01196    }
01197    else
01198    {
01199       tmarg.tm_isdst = -1; // don't know about daylight savings time
01200    }
01201    setTime(tmarg, timeOffset);
01202    m_microseconds = microseconds;
01203 }
01205 void
01206 DateTime::setToCurrent()
01207 {
01208 #ifdef BLOCXX_HAVE_GETTIMEOFDAY
01209    timeval tv;
01210    gettimeofday(&tv, NULL);
01211    m_time = tv.tv_sec;
01212    m_microseconds = tv.tv_usec;
01213 #else
01214    m_time = time(NULL);
01215    m_microseconds = 0;
01216 #endif
01217 }
01219 void
01220 DateTime::addDays(int days)
01221 {
01222    tm theTime = getTm(E_UTC_TIME);
01223    theTime.tm_mday += days;
01224    setTime(theTime, E_UTC_TIME);
01225 }
01227 void
01228 DateTime::addYears(int years)
01229 {
01230    tm theTime = getTm(E_UTC_TIME);
01231    theTime.tm_year += years;
01232    setTime(theTime, E_UTC_TIME);
01233 }
01235 void
01236 DateTime::addMonths(int months)
01237 {
01238    tm theTime = getTm(E_UTC_TIME);
01239    theTime.tm_mon += months;
01240    setTime(theTime, E_UTC_TIME);
01241 }
01243 String
01244 DateTime::toString(ETimeOffset timeOffset) const
01245 {
01246    tm theTime = getTm(timeOffset);
01247    char buff[30];
01248    asctime_r(&theTime, buff);
01249    String s(buff);
01250    return s;
01251 }
01252 
01254 String DateTime::toString(char const * format, ETimeOffset timeOffset) const
01255 {
01256    tm theTime = getTm(timeOffset);
01257    size_t const BUFSZ = 1024;
01258    char buf[BUFSZ];
01259    size_t n = strftime(buf, BUFSZ, format, &theTime);
01260    buf[n >= BUFSZ ? 0 : n] = '\0';
01261    return String(buf);
01262 }
01263 
01265 char const DateTime::DEFAULT_FORMAT[] = "%c";
01266 
01268 Int16 DateTime::localTimeAndOffset(time_t t, struct tm & t_loc)
01269 {
01270    struct tm t_utc;
01271    struct tm * ptm_utc = ::gmtime_r(&t, &t_utc);
01272    struct tm * ptm_loc = ::localtime_r(&t, &t_loc);
01273    if (!ptm_utc || !ptm_loc)
01274    {
01275       BLOCXX_THROW(DateTimeException, Format("Invalid time_t: %1", t).c_str());
01276    }
01277    int min_diff =
01278       (t_loc.tm_min - t_utc.tm_min) + 60 * (t_loc.tm_hour - t_utc.tm_hour);
01279    // Note: UTC offsets can be greater than 12 hours, but are guaranteed to
01280    // be less than 24 hours.
01281    int day_diff = t_loc.tm_mday - t_utc.tm_mday;
01282    int const one_day = 24 * 60;
01283    if (day_diff == 0)
01284    {
01285       return min_diff;
01286    }
01287    else if (day_diff == 1 || day_diff < -1)
01288    {
01289       // if day_diff < -1, then UTC day is last day of month and local day
01290       // is 1st of next month.
01291       return min_diff + one_day;
01292    }
01293    else /* day_diff == -1 || day_diff > 1 */
01294    {
01295       // if day_diff > 1, then UTC day is 1st of month and local day is last
01296       // day of previous month.
01297       return min_diff - one_day;
01298    }
01299 }
01300 
01302 void
01303 DateTime::set(time_t t, UInt32 microseconds)
01304 {
01305    if (t == static_cast<time_t>(-1) || microseconds > 999999)
01306    {
01307       BLOCXX_THROW(DateTimeException, "Either t == -1 or microseconds > 999999");
01308    }
01309 
01310    m_time = t;
01311    m_microseconds = microseconds;
01312 }
01313 
01315 // static
01316 DateTime
01317 DateTime::getCurrent()
01318 {
01319    DateTime current;
01320    current.setToCurrent();
01321    return current;
01322 }
01323 
01325 DateTime operator-(DateTime const & x, DateTime const & y)
01326 {
01327    time_t diff = x.get() - y.get();
01328    Int32 microdiff = (Int32)x.getMicrosecond() - (Int32)y.getMicrosecond();
01329    if (microdiff < 0)
01330    {
01331       --diff;
01332       microdiff += 1000000;
01333    }
01334    return DateTime(diff, (UInt32)microdiff);
01335 }
01336 
01337 } // end namespace BLOCXX_NAMESPACE
01338 

Generated on Fri Jun 16 15:39:08 2006 for blocxx by  doxygen 1.4.6