Main Page | Namespace List | Class Hierarchy | Alphabetical List | Class List | Directories | File List | Namespace Members | Class Members | File Members

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

Generated on Mon Sep 12 23:56:34 2005 for blocxx by  doxygen 1.4.4