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

Generated on Thu Feb 9 08:48:27 2006 for openwbem by  doxygen 1.4.6