Main Page   Namespace List   Class Hierarchy   Compound List   File List   Namespace Members   Compound Members   File Members  

BdbTime.cc

Go to the documentation of this file.
00001 //-----------------------------------------------------------------------------
00002 //
00003 // File and Version Information:
00004 //      $Id: BdbTime.cc,v 1.24 2004/09/09 04:41:01 gowdy Exp $
00005 //
00006 // Description:
00007 //      Class BdbTime.
00008 //      This is a persistent time class.
00009 //
00010 // Environment:
00011 //      Software developed for the BaBar Detector at the SLAC B-Factory.
00012 //
00013 // Author List:
00014 //      J. Ohnemus                      Original Author
00015 //      Gregory Dubois-Felsmann         Rogue Wave migration, 2002/2003
00016 //
00017 // Copyright Information:
00018 //      Copyright (C) 1994, 1995        Lawrence Berkeley Laboratory
00019 //      Copyright (c) 2002, 2003        California Institute of Technology
00020 //
00021 //-----------------------------------------------------------------------------
00022 #include "BaBar/BaBar.hh"
00023 
00024 //-----------------------
00025 // This Class's Header --
00026 //-----------------------
00027 #include "BdbTime/BdbTime.hh"
00028 
00029 //-----------------
00030 // C/C++ Headers --
00031 //-----------------
00032 #include <assert.h>
00033 #include <errno.h>
00034 #include <strings.h>
00035 #include <iomanip>
00036 
00037 //-------------------------------
00038 // Collaborating Class Headers --
00039 //-------------------------------
00040 #include "BdbTime/BdbTimeInput.hh"
00041 #include "BbrStdUtils/Tokenize.hh"
00042 using std::ostream;
00043 
00044 //---------------------
00045 // static declarations
00046 //---------------------
00047 const BdbTime BdbTime::minusInfinity = BdbTime( BdbTimeConst::minusInfinity );
00048 const BdbTime BdbTime::plusInfinity  = BdbTime( BdbTimeConst::plusInfinity  );
00049 
00050 //------------------------------------------------------------------------
00051 // Local Macros, Typedefs, Structures, Unions, and Forward Declarations --
00052 //------------------------------------------------------------------------
00053 static const char rcsid[] = "$Id: BdbTime.cc,v 1.24 2004/09/09 04:41:01 gowdy Exp $";
00054 
00055 
00056 // This function is intended to handle the renormalization of the POSIX
00057 // 1970 epoch to the BdbTime (formerly Rogue Wave) epoch of 1901.  Since the
00058 // conversion constant is an unsigned quantity too large to be represented
00059 // in a signed 32-bit number like an ILP32 time_t, this should be done
00060 // carefully to avoid overflows, sign extensions, etc.
00061 //
00062 // Note that we don't attempt to deal with the problem that there are
00063 // some 11.5 months' worth of 32-bit POSIX time_t's in 2037-2038 that
00064 // would require more than 32 bits to represent in the 1901 epoch.
00065 
00066 static inline d_ULong POSIXto1901( time_t t )
00067 {
00068   return ( t >= 0 
00069            // After 1970, no problems.
00070            ? ( BdbTimeConst::seconds_1901_to_1970 + d_ULong(t) )
00071            // Before 1970.  Flip sign first.
00072            : ( BdbTimeConst::seconds_1901_to_1970 - d_ULong(-t) ) );
00073 }
00074 
00075 // Need the number of seconds from 1901.01.01 to 1907.01.01.  This 
00076 // includes one leap year.
00077 static const d_ULong fix1901_offset = ( 6*365 + 1 ) * 24 * 60 * 60;
00078 
00079 // This function, used in several places, takes us from a POSIX struct
00080 // tm to a 1901-epoch time.  The return value tells us whether there
00081 // was a problem with the conversion.  t1901 is not modified if there is
00082 // a problem.
00083 //
00084 // It has a really nasty problem to deal with.  POSIX mktime() is considered
00085 // "non-portable" for times before 1970.  It _can_ work for such times if
00086 // time_t is a 32-bit signed integer, as it is on Linux and Solaris.  But
00087 // it can under no circumstances work for times earlier than 2^31 seconds
00088 // before the POSIX epoch -- and that time happens to be in December 1901.
00089 // Hence, there is an 11+ month period at the beginning of the 1901 epoch
00090 // that mktime() can't handle.
00091 // So, we have to kludge this.  Please don't watch.
00092 static bool TMto1901( struct tm& stm, BdbTime::Zone zone, d_ULong& t1901 )
00093 {
00094   const bool fix1901 = ( stm.tm_year == 1 ); // i.e., 1900+1 == 1901
00095   if ( fix1901 ) {
00096     stm.tm_year = 7;       // 1907 has the same weekday pattern as 1901
00097                            // and is also not a leap year.
00098   }
00099 
00100   // Build a POSIX time_t from the broken-down time.  mktime() uses the
00101   // current time zone, so we have to take that into account.
00102   //
00103   // mktime() can modify its struct tm argument, so it can't be const.
00104   //
00105   // POSIX specifies that mktime() should return (time_t)-1 if the struct tm
00106   // cannot be represented as a time_t, with errno set to EOVERFLOW.  If the
00107   // time conversion is successful, mktime() is required not to change errno.
00108   // Because -1 is also a legal value for the "extended" time range
00109   // supported on platforms (all of ours) where time_t is signed, we test only
00110   // on errno.
00111   errno = 0;
00112   time_t t = mktime( &stm );
00113   // This assertion is here to catch problems during development.  This
00114   // function should not assert() in production and this line should be
00115   // removed.                                                          FIXME
00116   assert( errno == 0 );
00117   if ( errno != 0 ) {
00118     return false;
00119   }
00120 
00121   // The Mac OSX doesn't have the timezone variable needed below,  so
00122   // we'll need to use its non-standard timegm() method to work this out.
00123   // We do this now to benefit from the fix1901
00124 #ifdef __APPLE_CC__
00125     d_Long timezone=0;
00126     errno = 0;
00127     time_t otherT = timegm( &stm );
00128     if ( errno != 0 ) return false;
00129     timezone = t - otherT;
00130 #endif
00131 
00132 
00133   // Offset the time to the 1901 epoch.  Do this carefully to avoid 
00134   // overflows and sign-extension headaches.
00135   d_ULong ut = POSIXto1901( t );
00136 
00137   // Now, if we relocated the time to 1907 above, in order to get around
00138   // the limitations of POSIX time_t, we have to put it back:
00139   if ( fix1901 ) {
00140     assert( ut >= fix1901_offset );
00141     ut -= fix1901_offset;
00142     stm.tm_year = 1;
00143   }
00144 
00145   // If the user actually wanted to work in UTC, we need to correct the
00146   // resulting time for the timezone offset.
00147   if ( BdbTime::UTC == zone ) {
00148     // mktime() is defined by POSIX to have called tzset(), so we can
00149     // use the external variable 'timezone'.
00150 
00151     // Subtract the number of seconds between UTC and the current timezone.
00152     // Justification for the sign: POSIX defines 'timezone' as positive for
00153     // locations west of the meridian.  A given broken-down time, interpreted
00154     // as local time west of the meridian, is a later absolute time than that
00155     // broken-down time interpreted as UTC.  Thus, to get from mktime()'s
00156     // local time interpretation to a UTC interpretation, we must subtract
00157     // the value of 'timezone'.
00158     ut -= timezone;
00159 
00160     // Correct for daylight savings time.  POSIX appears to assume that
00161     // the DST offset is always exactly one hour, so that's all we can do.
00162     // Justification for the sign: a time, say, "noon" interpreted as
00163     // DST produces the same time_t value as "11am" non-DST, 60*60 seconds
00164     // less than the value produced for "noon" non-DST -- so we have to add
00165     // that number back.
00166 
00167     // We have to decide on DST/non-DST _for the time in question_, not
00168     // for "now", so we need to use the decision that mktime() made.  Make
00169     // sure it made it, and changed the negative value of tm_isdst that we
00170     // originally set.
00171     // This assertion merely validates a POSIX-guaranteed invariant, so it
00172     // can stay in place in production.
00173     assert( 0 <= stm.tm_isdst );
00174     if ( 0 < stm.tm_isdst )  ut += ( 60*60 );
00175     else if ( 0 > stm.tm_isdst ) {
00176       // If assert() has been compiled away, make the time value invalid.
00177       return false;
00178     }
00179   }
00180 
00181   t1901 = ut;
00182   return true;
00183 }
00184 
00185 
00186 //              ----------------------------------------
00187 //              -- Public Function Member Definitions --
00188 //              ----------------------------------------
00189 
00190 //----------------
00191 // Constructors --
00192 //----------------
00193 
00194 BdbTime::BdbTime( )                         // default: construct present time
00195   : _gmtSec(0), _gmtNsec(0)
00196 {
00197   struct timespec ts;
00198   int gettimeStatus = clock_gettime( CLOCK_REALTIME, &ts );
00199   // This should never fail on a normal general-purpose Unix computer.
00200   assert( 0 == gettimeStatus );
00201   if ( 0 == gettimeStatus ) {
00202     _gmtSec  = POSIXto1901( ts.tv_sec );
00203     // NB: The original Rogue Wave implementation of BdbTime() failed to
00204     // set the _gmtNsec to reflect the current time.  This was a bug, but
00205     // in order not to change the behavior of existing programs, this
00206     // behavior is being retained here.  The following line is therefore
00207     // commented out.  This could be reconsidered.                    // FIXME
00208     // _gmtNsec = ts.tv_nsec;
00209   }
00210 }
00211 
00212 BdbTime::BdbTime( const BdbTime& t )
00213   : _gmtSec(t._gmtSec), _gmtNsec(t._gmtNsec)
00214 {
00215 }
00216 
00217 BdbTime::BdbTime( d_ULong sec, d_ULong nsec  )
00218   : _gmtSec(sec), _gmtNsec(nsec)
00219 {
00220   renormalizeNanoseconds();
00221 }
00222 
00223 BdbTime::BdbTime( d_ULong year,
00224                   d_ULong month,
00225                   d_ULong day,
00226                   d_ULong hour,
00227                   d_ULong minute,
00228                   d_ULong second,
00229                   d_ULong nanosecond,
00230                   BdbTime::Zone zone )
00231   : _gmtSec(0), _gmtNsec(nanosecond)
00232 {
00233   // Construct a POSIX broken-down time from the components supplied.
00234   // The definitions of the constructor arguments' meaning are inherited
00235   // from Rogue Wave's specifications, so they need to be renormalized
00236   // for POSIX.
00237   struct tm stm;
00238   stm.tm_year  = year - 1900;
00239   stm.tm_mon   = month - 1;   // Rogue Wave/BdbTime: [1,12], POSIX: [0,11]
00240   stm.tm_mday  = day; 
00241   stm.tm_hour  = hour;
00242   stm.tm_min   = minute;
00243   stm.tm_sec   = second;
00244 
00245   stm.tm_isdst = -1;          // Let mktime figure out whether DST is in force
00246 
00247   d_ULong ut = 0;
00248   bool ok = TMto1901( stm, zone, ut );
00249 
00250   if ( ok ) {
00251     _gmtSec = ut;
00252     renormalizeNanoseconds();
00253   }
00254   else {
00255     // Invalidate the time -- (0,0) is a reserved value.
00256     _gmtNsec = 0;
00257   }
00258 }
00259 
00260 
00261 BdbTime::BdbTime( const std::string& sdate, const std::string& stime,
00262                   Zone zone ) :
00263   _gmtSec( 0 ), _gmtNsec( 0 ) 
00264 {
00265   // This is ugly because no error checking can be performed.
00266 
00267   BdbTime t( 0, 0 );
00268 
00269   bool status = parseTime( sdate, stime, zone, t );
00270 
00271   if ( status ) {
00272     _gmtSec  = t.getGmtSec();
00273     _gmtNsec = t.getGmtNsec();
00274   }
00275   // else leave it at -Infinity = (0,0).
00276 }
00277 
00278 
00279 BdbTime::BdbTime( const std::string& sdatetime, Zone bzone ) :
00280   _gmtSec( 0 ), _gmtNsec( 0 ) 
00281 {
00282   // This is ugly because no error checking can be performed.
00283 
00284   BdbTime t( 0, 0 );
00285 
00286   bool status = parseTime( sdatetime, bzone, t );
00287 
00288   if ( status ) {
00289     _gmtSec  = t.getGmtSec();
00290     _gmtNsec = t.getGmtNsec();
00291   }
00292   // else leave it at -Infinity = (0,0).
00293 }
00294 
00295 BdbTime::BdbTime( struct tm& stm, Zone zone )
00296   : _gmtSec(0), _gmtNsec(0)
00297 {
00298   d_ULong ut = 0;
00299   bool ok = TMto1901( stm, zone, ut );
00300 
00301   if ( ok ) {
00302     _gmtSec = ut;
00303   }
00304   // else time is already invalidated -- (0,0) is a reserved value.
00305 }
00306 
00307 
00308 BdbTime::BdbTime( const struct timespec& ts )
00309   // shift from POSIX 1970 epoch to BdbTime 1901 epoch
00310   : _gmtSec(POSIXto1901(ts.tv_sec)), _gmtNsec(ts.tv_nsec)
00311 {
00312   renormalizeNanoseconds();
00313 }
00314 
00315 int
00316 BdbTime::timeSpec( struct timespec* ts ) const 
00317 {
00318   // See if the time is earlier than the minimum representable signed
00319   // 32-bit POSIX time: (1970.01.01 00:00:00 UTC) - (2^31 - 1) seconds
00320   const d_ULong maxSigned = 0x7fffffff;  // maximum signed number of seconds
00321   assert( BdbTimeConst::seconds_1901_to_1970 > maxSigned );
00322   const d_ULong minPOSIX = BdbTimeConst::seconds_1901_to_1970 - maxSigned;
00323 
00324   if ( _gmtSec < minPOSIX ) {
00325     return -1;
00326   }
00327 
00328   // Be absolutely sure about sign extension.  Both the _gmtSec and
00329   // BdbTimeConst::seconds_1901_to_1970 are usually going to be greater
00330   // than the maximum signed 32-bit integer.
00331   if ( _gmtSec < BdbTimeConst::seconds_1901_to_1970 ) {
00332     d_ULong diff = BdbTimeConst::seconds_1901_to_1970 - _gmtSec;
00333     ts->tv_sec = -( (time_t) diff );
00334   }
00335   else {
00336     ts->tv_sec = (time_t) ( _gmtSec - BdbTimeConst::seconds_1901_to_1970 );
00337   }
00338   ts->tv_nsec = _gmtNsec;
00339 
00340   return 0;
00341 }
00342 
00343 
00344 struct tm*
00345 BdbTime::tm( struct tm* stm, Zone zone ) const
00346 {
00347   if ( stm == 0 ) return 0;
00348 
00349   // See if the time is earlier than the minimum representable signed
00350   // 32-bit POSIX time: (1970.01.01 00:00:00 UTC) - (2^31 - 1) seconds
00351   const d_ULong maxSigned = 0x7fffffff;  // maximum signed number of seconds
00352   assert( BdbTimeConst::seconds_1901_to_1970 > maxSigned );
00353   const d_ULong minPOSIX = BdbTimeConst::seconds_1901_to_1970 - maxSigned;
00354 
00355   const bool fix1901 = ( _gmtSec < minPOSIX );
00356 
00357   d_ULong fixedSec = _gmtSec;
00358   // Shift to the similar year 1907 if the time is in the pre-signed-POSIX
00359   // range where we can't trust gmtime_r().
00360   if ( fix1901 ) fixedSec += fix1901_offset;
00361 
00362   // Shift to POSIX epoch without triggering signedness conversions:
00363   time_t posixt = ( fixedSec >= BdbTimeConst::seconds_1901_to_1970 
00364                     ? ( fixedSec - BdbTimeConst::seconds_1901_to_1970 )
00365                     : -(time_t)( BdbTimeConst::seconds_1901_to_1970 - fixedSec ) );
00366 
00367   struct tm* stmout = 0;
00368   if ( zone == UTC ) {
00369     stmout = gmtime_r( &posixt, stm );
00370   }
00371   else { // Local
00372     stmout = localtime_r( &posixt, stm );
00373   }
00374 
00375   // Post-condition for gm/localtime_r: either success or failure.
00376   assert( stmout == stm || stmout == 0 );
00377 
00378   if ( fix1901 && stmout != 0 ) {
00379     // Go back from 1907 to 1901.
00380     stmout->tm_year -= 6;
00381     // Validate that it really is 1901.  It is also possible that for
00382     // very small BdbTime values (shortly after midnight UTC on 1901.1.1),
00383     // the broken-down time might be late in the last day of 1900 if a
00384     // local time zone is being used.  So allow for that (NB: Dec = 11).
00385     assert( stmout->tm_year == 1 
00386             || ( zone != UTC 
00387                  && stmout->tm_year == 0 
00388                  && stmout->tm_mon == 11 && stmout->tm_mday == 31 ) );  
00389   }
00390 
00391   return stmout;
00392 }
00393 
00394 
00395 std::string
00396 BdbTime::asString( const char* fmt, Zone zone ) const
00397 {
00398   // In order to use strftime(), we must have a "struct tm" containing
00399   // the time.
00400   struct tm stm;
00401   struct tm* pstm = tm( &stm, zone );
00402 
00403   if ( pstm != &stm ) {
00404     return std::string( "(time conversion failed)" );
00405   }
00406 
00407   // Reserve an output buffer for the conversion, and do it.
00408   // Note that the size argument includes room for the terminating
00409   // null byte, but the return value does not account for it.
00410   char buffer[1024];
00411   size_t nstr = strftime( buffer, sizeof(buffer), fmt, &stm );
00412   
00413   if ( nstr >= sizeof(buffer) || ( nstr == 0 && strlen(fmt) != 0 ) ) {
00414     return std::string( "(time conversion failed)" );
00415   }
00416 
00417   return std::string( buffer );
00418 }
00419 
00420 //-------------
00421 // Operators --
00422 //-------------
00423 
00424 // t2 = t1
00425 BdbTime& BdbTime::operator=( const BdbTime& t1 )
00426 {
00427     if ( this == &t1 )  return *this;
00428 
00429     _gmtSec  = t1._gmtSec;
00430     _gmtNsec = t1._gmtNsec;
00431 
00432     return *this;
00433 }
00434 
00435 // d = |t2 - t1|
00436 BdbDuration BdbTime::operator-( const BdbTime& t1 ) const
00437 {
00438     // This code forms |t2-t1| without having to use signed intergers.
00439 
00440     d_ULong t2Sec;
00441     d_ULong t2Nsec;
00442     d_ULong t1Sec;
00443     d_ULong t1Nsec;
00444 
00445     if ( *this > t1 )
00446         {
00447             t2Sec  = _gmtSec;
00448             t2Nsec = _gmtNsec;
00449             t1Sec  = t1._gmtSec;
00450             t1Nsec = t1._gmtNsec;
00451         }
00452     else
00453         {
00454             t2Sec  = t1._gmtSec;
00455             t2Nsec = t1._gmtNsec;
00456             t1Sec  = _gmtSec;
00457             t1Nsec = _gmtNsec;
00458         }
00459 
00460     if ( t2Nsec < t1Nsec )
00461         {
00462             // borrow a second from t2Sec
00463             t2Nsec += BdbTimeConst::nsecInASec;
00464             t2Sec--;
00465         }
00466 
00467     d_ULong sec  = t2Sec  - t1Sec;
00468     d_ULong nsec = t2Nsec - t1Nsec;
00469 
00470     BdbDuration diff( sec, nsec );
00471 
00472     return diff;
00473 }
00474 
00475 // t = t1 - d
00476 BdbTime BdbTime::operator-( const BdbDuration& d ) const
00477 {
00478   return BdbTime(*this) -= d;
00479 }
00480 
00481 // t -= d
00482 BdbTime& BdbTime::operator-=( const BdbDuration& d )
00483 {
00484   // if t1 - d < 0 then return t = 0
00485   if ( ( _gmtSec  < d.getSec() ) ||
00486        ( _gmtSec == d.getSec()   && _gmtNsec < d.getNsec() ) ) {
00487     _gmtSec  = 0;
00488     _gmtNsec = 0;
00489   }
00490   else {
00491     d_ULong tempSec  = _gmtSec;
00492     d_ULong tempNsec = _gmtNsec;
00493     
00494     if ( tempNsec < d.getNsec() ) {
00495       // if t1._gmtNsec < d._nsec borrow a second from t1._gmtSec
00496       tempNsec += BdbTimeConst::nsecInASec;
00497       tempSec--;
00498     }
00499     
00500     _gmtSec  = tempSec  - d.getSec();
00501     _gmtNsec = tempNsec - d.getNsec();
00502   }
00503   
00504   return *this;
00505 }
00506 
00507 // t = t1 + d
00508 BdbTime BdbTime::operator+( const BdbDuration& d ) const
00509 {
00510   return BdbTime(*this) += d;
00511 }
00512 
00513 // t += d
00514 BdbTime& BdbTime::operator+=( const BdbDuration& d )
00515 {
00516   d_ULong totalSec  = _gmtSec  + d.getSec();
00517   d_ULong totalNsec = _gmtNsec + d.getNsec();
00518   
00519   if ( totalNsec >= BdbTimeConst::nsecInASec ) {
00520     // carry nanoseconds over into seconds
00521     d_ULong extraSec   = totalNsec / BdbTimeConst::nsecInASec;
00522     d_ULong remainNsec = totalNsec % BdbTimeConst::nsecInASec;
00523     totalSec          += extraSec;
00524     totalNsec          = remainNsec;
00525   }
00526   
00527   _gmtSec  = totalSec;
00528   _gmtNsec = totalNsec;
00529 
00530   return *this;
00531 }
00532 
00533 //
00534 //  Static functions
00535 //
00536 
00537 BdbTime
00538 BdbTime::now( )  // construct present time
00539 {
00540   struct timespec ts;
00541   int gettimeStatus = clock_gettime( CLOCK_REALTIME, &ts );
00542   // This should never fail on a normal general-purpose Unix computer.
00543   assert( 0 == gettimeStatus );
00544   if ( 0 != gettimeStatus ) {
00545     return BdbTime(0,0);    // NB: there is no "invalid" value
00546   }
00547 
00548   // Renormalize to BdbTime 1901 epoch.
00549   return BdbTime( POSIXto1901( ts.tv_sec ), ts.tv_nsec );
00550 }
00551 
00552 
00553 bool 
00554 BdbTime::parseTime( const std::string& sdate, const std::string& stime, 
00555                     Zone zone,
00556                     BdbTime& time )
00557 {
00558   bool status = false;
00559 
00560   if ( strcasecmp( sdate.c_str(), "-Infinity" ) == 0 ) {
00561     time = minusInfinity;
00562     status = true;
00563   }
00564   else if ( strcasecmp( sdate.c_str(), "+Infinity" ) == 0 ) {
00565     time = plusInfinity;
00566     status = true;
00567   }
00568   else {
00569     struct tm stm;
00570     memset( &stm, 0, sizeof(stm) );
00571     stm.tm_isdst = -1;                     // Flag DST state as unknown
00572 
00573     BdbTimeInput::Status s;
00574     s = BdbTimeInput::parseDate( sdate.c_str(), stm );
00575     if ( s == BdbTimeInput::Success ) {
00576       s = BdbTimeInput::parseTime( stime.c_str(), stm );
00577       if ( s == BdbTimeInput::Success ) {
00578         d_ULong ut = 0;
00579         bool ok = TMto1901( stm, zone, ut );
00580         if ( ok ) {
00581           status = true;
00582           time = BdbTime( ut, 0 );
00583         }
00584       }
00585     }
00586   }
00587 
00588   return status;
00589 }
00590 
00591 bool 
00592 BdbTime::parseTime( const std::string& sdatetime,
00593                     Zone zone,
00594                     BdbTime& time )
00595 {
00596   bool status = false;
00597 
00598   if ( strcasecmp( sdatetime.c_str(), "-Infinity" ) == 0 ) {
00599     time = minusInfinity;
00600     status = true;
00601   }
00602   else if ( strcasecmp( sdatetime.c_str(), "+Infinity" ) == 0 ) {
00603     time = plusInfinity;
00604     status = true;
00605   }
00606   else {
00607     babar::String::Tokenize tokens( sdatetime );
00608 
00609     std::string sdate = tokens();
00610     std::string stime = tokens();
00611 
00612     status = parseTime( sdate, stime, zone, time );
00613   }
00614 
00615   return status;
00616 }
00617 
00618 
00619 
00620 //--------------------
00621 // Friend functions --
00622 //--------------------
00623 
00624 // t = d + t1
00625 BdbTime operator+( const BdbDuration& d, const BdbTime& t1 )
00626 {
00627     return t1 + d;
00628 }
00629 
00630 ostream & operator <<( ostream& os, const BdbTime& t ) 
00631 {
00632   if ( t == BdbTime::minusInfinity )
00633     os << "-Infinity";
00634   else if ( t == BdbTime::plusInfinity )
00635     os << "+Infinity";
00636   else 
00637     {
00638       os << t.asString( "%c", BdbTime::Local ) << " (local time) " 
00639          << t.getGmtNsec( ) << " ns";
00640     }
00641   return os;
00642 }

Generated on Mon Dec 5 18:21:58 2005 for CDB by doxygen1.3-rc3