OW_DateTime.cpp

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

Generated on Thu Feb 9 08:47:58 2006 for openwbem by  doxygen 1.4.6