NCBI C++ ToolKit
ncbitime.cpp
Go to the documentation of this file.
00001 /*  $Id: ncbitime.cpp 53372 2012-03-12 18:05:01Z ivanovp $
00002  * ===========================================================================
00003  *
00004  *                            PUBLIC DOMAIN NOTICE
00005  *               National Center for Biotechnology Information
00006  *
00007  *  This software/database is a "United States Government Work" under the
00008  *  terms of the United States Copyright Act.  It was written as part of
00009  *  the author's official duties as a United States Government employee and
00010  *  thus cannot be copyrighted.  This software/database is freely available
00011  *  to the public for use. The National Library of Medicine and the U.S.
00012  *  Government have not placed any restriction on its use or reproduction.
00013  *
00014  *  Although all reasonable efforts have been taken to ensure the accuracy
00015  *  and reliability of the software and data, the NLM and the U.S.
00016  *  Government do not and cannot warrant the performance or results that
00017  *  may be obtained by using this software or data. The NLM and the U.S.
00018  *  Government disclaim all warranties, express or implied, including
00019  *  warranties of performance, merchantability or fitness for any particular
00020  *  purpose.
00021  *
00022  *  Please cite the author in any work or product based on this material.
00023  *
00024  * ===========================================================================
00025  *
00026  * Authors:  Anton Butanayev, Denis Vakatov, Vladimir Ivanov
00027  *
00028  *
00029  */
00030 
00031 #include <ncbi_pch.hpp>
00032 #include <corelib/ncbitime.hpp>
00033 #include <corelib/ncbimtx.hpp>
00034 #include <corelib/ncbithr.hpp>
00035 #include <corelib/ncbi_safe_static.hpp>
00036 #include <corelib/ncbi_limits.h>
00037 #include <corelib/error_codes.hpp>
00038 #include <stdlib.h>
00039 
00040 #if defined(NCBI_OS_MSWIN)
00041 #  include <sys/timeb.h>
00042 #  include <windows.h>
00043 #elif defined(NCBI_OS_UNIX)
00044 #  include <sys/time.h>
00045 #endif
00046 
00047 #if defined(__CYGWIN__)
00048 #  define TimeZone() _timezone
00049 #  define Daylight() _daylight
00050 #else
00051 #  define TimeZone()  timezone
00052 #  define Daylight()  daylight
00053 #endif
00054 
00055 #if defined(NCBI_OS_DARWIN)  ||  defined(NCBI_OS_BSD)
00056 #  define TIMEZONE_IS_UNDEFINED  1
00057 #endif
00058 
00059 
00060 #define NCBI_USE_ERRCODE_X   Corelib_Util
00061 
00062 
00063 BEGIN_NCBI_SCOPE
00064 
00065 
00066 // Protective mutex
00067 DEFINE_STATIC_FAST_MUTEX(s_TimeMutex);
00068 DEFINE_STATIC_FAST_MUTEX(s_TimeAdjustMutex);
00069 DEFINE_STATIC_FAST_MUTEX(s_FastLocalTimeMutex);
00070 
00071 // Store global time/timespan formats in TLS
00072 static CStaticTls<CTimeFormat> s_TlsFormatTime;
00073 static CStaticTls<CTimeFormat> s_TlsFormatSpan;
00074 static CStaticTls<CTimeFormat> s_TlsFormatStopWatch;
00075 
00076 static void s_TlsFormatCleanup(CTimeFormat* fmt, void* /* data */)
00077 {
00078     delete fmt;
00079 }
00080 
00081 // Global quick and dirty getter of local time
00082 static CSafeStaticPtr<CFastLocalTime> s_FastLocalTime;
00083 
00084 
00085 //============================================================================
00086 
00087 // Number of days per month
00088     static int s_DaysInMonth[12] = {
00089     31, 0, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
00090 };
00091 
00092 // Month names
00093 static const char* kMonthAbbr[12] = {
00094     "Jan", "Feb", "Mar", "Apr", "May", "Jun",
00095     "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
00096 };
00097 static const char* kMonthFull[12] = {
00098     "January", "February", "March", "April", "May", "June",
00099     "July", "August", "September", "October", "November", "December"
00100 };
00101 
00102 // Day of week names
00103 static const char* kWeekdayAbbr[7] = {
00104     "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat"
00105 };
00106 static const char* kWeekdayFull [7] = {
00107     "Sunday", "Monday", "Tuesday", "Wednesday",
00108     "Thursday", "Friday", "Saturday"
00109 };
00110 
00111 // Default value for time/timespan format
00112 static const char* kDefaultFormatTime      = "M/D/Y h:m:s";
00113 static const char* kDefaultFormatSpan      = "-S.n";
00114 static const char* kDefaultFormatStopWatch = "-S.n";
00115 
00116 // Set of the checked format symbols.
00117 // For CStopWatch class the format symbols are equal
00118 // to kFormatSymbolsSpan also.
00119 static const char* kFormatSymbolsTime = "yYMbBDdhHmsSzZwWlrpP";
00120 static const char* kFormatSymbolsSpan = "-dhHmMsSnN";
00121 
00122 // Character used to escape formatted symbols.
00123 const char kFormatEscapeSymbol = '$';
00124 
00125 
00126 // Macro to check range for time components.
00127 // See also:
00128 //      CTime::m_Data
00129 //      CTime::IsValid
00130 //      CTime::Set*() methods
00131 
00132 #define CHECK_RANGE(value, what, min, max) \
00133     if ( value < min  ||  value > max ) {  \
00134         NCBI_THROW(CTimeException, eArgument, \
00135                    "CTime: " what " value '" + \
00136                    NStr::Int8ToString(value) + "' is out of range"); \
00137     }
00138 
00139 #define CHECK_RANGE_YEAR(value)  CHECK_RANGE(value, "year", 1583, kMax_Int)
00140 #define CHECK_RANGE_MONTH(value) CHECK_RANGE(value, "month", 1, 12)
00141 #define CHECK_RANGE_DAY(value)   CHECK_RANGE(value, "day", 1, 31)
00142 #define CHECK_RANGE_HOUR(value)  CHECK_RANGE(value, "hour", 0, 23)
00143 #define CHECK_RANGE_MIN(value)   CHECK_RANGE(value, "minute", 0, 59)
00144 #define CHECK_RANGE_SEC(value)   CHECK_RANGE(value, "second", 0, 61)
00145 #define CHECK_RANGE_NSEC(value)  CHECK_RANGE(value, "nanosecond", 0, \
00146                                              kNanoSecondsPerSecond - 1)
00147 
00148 
00149 //============================================================================
00150 
00151 // Get number of days in "date"
00152 static unsigned s_Date2Number(const CTime& date)
00153 {
00154     if ( date.IsEmptyDate() ) {
00155         NCBI_THROW(CTimeException, eConvert, 
00156                    "s_Date2Number(): the date is empty");
00157     }
00158     unsigned d = date.Day();
00159     unsigned m = date.Month();
00160     unsigned y = date.Year();
00161     unsigned c, ya;
00162 
00163     if (m > 2) {
00164         m -= 3;
00165     } else {
00166         m += 9;
00167         y--;
00168     }
00169     c  = y / 100;
00170     ya = y - 100 * c;
00171 
00172     return ((146097 * c) >> 2) + ((1461 * ya) >> 2) +
00173             (153 * m + 2) / 5 +  d + 1721119;
00174 }
00175 
00176 
00177 // Conversion number of days in date format
00178 // timezone value compute on base <t>
00179 static CTime s_Number2Date(unsigned num, const CTime& t)
00180 {
00181     unsigned d;
00182     unsigned j = num - 1721119;
00183     unsigned year;
00184     unsigned day;
00185     unsigned month;
00186 
00187     year = (((j<<2) - 1) / 146097);
00188     j = (j<<2) - 1 - 146097 * year;
00189     d = (j>>2);
00190     j = ((d<<2) + 3) / 1461;
00191     d = (d<<2) + 3 - 1461 * j;
00192     d = (d + 4) >> 2;
00193     month = (5*d - 3) / 153;
00194     d = 5*d - 3 - 153 * month;
00195     day = (d + 5) / 5;
00196     year = 100 * year + j;
00197 
00198     if (month < 10) {
00199         month += 3;
00200     } else {
00201         month -= 9;
00202         year++;
00203     }
00204     // Construct new CTime object
00205     return
00206         CTime(year, month, day, t.Hour(), t.Minute(), t.Second(),
00207               t.NanoSecond(), t.GetTimeZone(), t.GetTimeZonePrecision());
00208 }
00209 
00210 
00211 // Calc <value> + <offset> on module <bound>.
00212 // Returns normalized value in <value>. 
00213 // The <major> will have a remainder after dividing.
00214 static inline
00215 void s_Offset(long *value, Int8 offset, long bound, int *major)
00216 {
00217     Int8 v = *value + offset;
00218     *major += (int)(v / bound);
00219     *value = (long)(v % bound);
00220     if (*value < 0) {
00221         *major -= 1;
00222         *value += bound;
00223     }
00224 }
00225 
00226 
00227 // Convert 'value' to string, append result to string 'str'.
00228 static inline 
00229 void s_AddInt(string& str, long value)
00230 {
00231     const size_t size = CHAR_BIT * sizeof(value);
00232     char   buf[size];
00233     size_t pos = size;
00234     do {
00235         buf[--pos] = char(value % 10) + '0';
00236         value /= 10;
00237     } while (value);
00238     str.append(buf + pos, size - pos);
00239 }
00240 
00241 
00242 // Convert 'value' to string, add leading '0' to size 'len'.
00243 // Append result to string 'str'.
00244 static inline 
00245 void s_AddZeroPadInt(string& str, long value, size_t len)
00246 {
00247     _ASSERT(value >= 0);
00248     _ASSERT((len > 0)  &&  (len < 10));
00249     const size_t size = 9;
00250     char buf[size];
00251     size_t pos = size;
00252     do {
00253         buf[--pos] = char(value % 10) + '0';
00254         value /= 10;
00255     } while (value);
00256     if (len > (size - pos)) {
00257         str.append(len - (size - pos), '0');
00258     }
00259     str.append(buf + pos, size - pos);
00260 }
00261 
00262 
00263 // Optimized variant of s_AddZeroPadInt() for len = 2.
00264 static inline 
00265 void s_AddZeroPadInt2(string& str, long value)
00266 {
00267     _ASSERT((value >= 0)  &&  (value <= 99));
00268     char buf[2];
00269     buf[1] = char(value % 10) + '0';
00270     buf[0] = char(value / 10) + '0'; 
00271     str.append(buf, 2);
00272 }
00273 
00274 
00275 
00276 //============================================================================
00277 //
00278 // CTimeFormat
00279 //
00280 //============================================================================
00281 
00282 
00283 CTimeFormat::CTimeFormat(void)
00284     : m_Flags(fDefault)
00285 {
00286     return;
00287 }
00288 
00289 
00290 CTimeFormat::CTimeFormat(const CTimeFormat& format)
00291 {
00292     *this = format;
00293 }
00294 
00295 
00296 CTimeFormat::CTimeFormat(const char* fmt, TFlags flags)
00297 {
00298     SetFormat(fmt, flags);
00299 }
00300 
00301 
00302 CTimeFormat::CTimeFormat(const string& fmt, TFlags flags)
00303 {
00304     SetFormat(fmt, flags);
00305 }
00306 
00307 
00308 CTimeFormat& CTimeFormat::operator= (const CTimeFormat& format)
00309 {
00310     if ( &format == this ) {
00311         return *this;
00312     }
00313     m_Str   = format.m_Str;
00314     m_Flags = format.m_Flags;
00315     return *this;
00316 }
00317 
00318 
00319 CTimeFormat CTimeFormat::GetPredefined(EPredefined fmt, TFlags flags)
00320 {
00321     // Predefined time formats
00322     static const char* s_Predefined[][2] =
00323     {
00324         {"Y",              "$Y"},
00325         {"Y-M",            "$Y-$M"},
00326         {"Y-M-D",          "$Y-$M-$D"},
00327         {"Y-M-DTh:m",      "$Y-$M-$DT$h:$m"},
00328         {"Y-M-DTh:m:s",    "$Y-$M-$DT$h:$m:$s"},
00329         {"Y-M-DTh:m:s.l",  "$Y-$M-$DT$h:$m:$s.$l"},
00330     };
00331     int fmt_type = (flags & fFormat_Ncbi) ? 1 : 0;
00332     return CTimeFormat(s_Predefined[(int)fmt][(int)fmt_type], flags);
00333 }
00334 
00335 
00336 //============================================================================
00337 //
00338 // CTime
00339 //
00340 //============================================================================
00341 
00342 CTime::CTime(const CTime& t)
00343 {
00344     *this = t;
00345 }
00346 
00347 
00348 CTime::CTime(int year, int yearDayNumber,
00349              ETimeZone tz, ETimeZonePrecision tzp)
00350 {
00351     Clear();
00352     m_Data.tz = tz;
00353     m_Data.tzprec = tzp;
00354 
00355     CTime t = CTime(year, 1, 1);
00356     t.AddDay(yearDayNumber - 1);
00357     m_Data.year  = t.Year();
00358     m_Data.month = t.Month();
00359     m_Data.day   = t.Day();
00360 }
00361 
00362 
00363 void CTime::x_Init(const string& str, const CTimeFormat& format)
00364 {
00365     Clear();
00366     if ( str.empty() ) {
00367         return;
00368     }
00369     // For partialy defined times use default values
00370     bool is_year_present  = false;
00371     bool is_month_present = false;
00372     bool is_day_present   = false;
00373     bool is_time_present  = false;
00374 
00375     const string& fmt = format.GetString();
00376     bool is_escaped = ((format.GetFlags() & CTimeFormat::fFormat_Simple) == 0);
00377     bool is_format_symbol = !is_escaped;
00378 
00379     const char* fff;
00380     const char* sss = str.c_str();
00381     bool  adjust_needed = false;
00382     long  adjust_tz     = 0;
00383 
00384     enum EHourFormat{
00385         e24, eAM, ePM
00386     };
00387     EHourFormat hourformat = e24;
00388     bool is_12hour = false;
00389 
00390     int weekday = -1;
00391     for (fff = fmt.c_str();  *fff != '\0';  fff++) {
00392         // Skip space symbols in format string
00393         if ( isspace((unsigned char)(*fff)) ) {
00394             continue;
00395         }
00396         // Skip preceding symbols for some formats
00397         if ( !is_format_symbol ) {
00398             if ( *fff == kFormatEscapeSymbol )  {
00399                 is_format_symbol = true;
00400                 continue;
00401             }
00402         }
00403         if ( is_escaped ) {
00404             is_format_symbol = false;
00405         }
00406         // Skip space symbols in time string
00407         while ( isspace((unsigned char)(*sss)) )
00408             sss++;
00409 
00410         // Non-format symbols
00411         if (strchr(kFormatSymbolsTime, *fff) == 0) {
00412             if (*fff == *sss) {
00413                 sss++;
00414                 continue;  // skip matching non-format symbols
00415             }
00416             break;  // error: non-matching non-format symbols
00417         }
00418 
00419         // Month
00420         if (*fff == 'b'  ||  *fff == 'B') {
00421             const char** name;
00422             if (*fff == 'b') {
00423                 name = kMonthAbbr;
00424             } else {
00425                 name = kMonthFull;
00426             }
00427             for (unsigned char i = 0;  i < 12;  i++) {
00428                 size_t namelen = strlen(*name);
00429                 if (NStr::strncasecmp(sss, *name, namelen) == 0) {
00430                     sss += namelen;
00431                     m_Data.month = i + 1;
00432                     break;
00433                 }
00434                 name++;
00435             }
00436             is_month_present = true;
00437             continue;
00438         }
00439 
00440         // Day of week
00441         if (*fff == 'w'  ||  *fff == 'W') {
00442             const char** day = (*fff == 'w') ? kWeekdayAbbr : kWeekdayFull;
00443             for (unsigned char i = 0;  i < 7;  i++) {
00444                 size_t len = strlen(*day);
00445                 if (NStr::strncasecmp(sss, *day, len) == 0) {
00446                     sss += len;
00447                     weekday = i;
00448                     break;
00449                 }
00450                 day++;
00451             }
00452             continue;
00453         }
00454 
00455         // Timezone (GMT time)
00456         if (*fff == 'Z') {
00457             if (NStr::strncasecmp(sss, "GMT", 3) == 0) {
00458                 m_Data.tz = eGmt;
00459                 sss += 3;
00460             } else {
00461                 m_Data.tz = eLocal;
00462             }
00463             continue;
00464         }
00465 
00466         // Timezone (local time in format GMT+HHMM)
00467         if (*fff == 'z') {
00468             m_Data.tz = eGmt;
00469             if (NStr::strncasecmp(sss, "GMT", 3) == 0) {
00470                 sss += 3;
00471             }
00472             while ( isspace((unsigned char)(*sss)) ) {
00473                 sss++;
00474             }
00475             int sign = (*sss == '+') ? 1 : ((*sss == '-') ? -1 : 0);
00476             if ( sign ) {
00477                 sss++;
00478             } else {
00479                 sign = 1;
00480             }
00481             long x_hour = 0;
00482             long x_min  = 0;
00483 
00484             char value_str[3];
00485             char* s = value_str;
00486             for (size_t len = 2;
00487                  len  &&  *sss  &&  isdigit((unsigned char)(*sss));
00488                  len--) {
00489                 *s++ = *sss++;
00490             }
00491             *s = '\0';
00492             try {
00493                 x_hour = NStr::StringToLong(value_str);
00494             }
00495             catch (CStringException) {
00496                 x_hour = 0;
00497             }
00498             try {
00499                 if ( *sss != '\0' ) {
00500                     s = value_str;
00501                     for (size_t len = 2;
00502                          len  &&  *sss  &&  isdigit((unsigned char)(*sss));
00503                          len--) {
00504                         *s++ = *sss++;
00505                     }
00506                     *s = '\0';
00507                     x_min = NStr::StringToLong(value_str,
00508                                                NStr::fAllowTrailingSymbols);
00509                 }
00510             }
00511             catch (CStringException) {
00512                 x_min = 0;
00513             }
00514             adjust_needed = true;
00515             adjust_tz = sign * (x_hour * 60 + x_min) * 60;
00516             continue;
00517         }
00518 
00519         // Timezone (local time in format GMT+HHMM)
00520         if (*fff == 'p'  ||  *fff == 'P') {
00521             if (NStr::strncasecmp(sss, "AM", 2) == 0) {
00522                 hourformat = eAM;
00523                 sss += 2;
00524             } else if (NStr::strncasecmp(sss, "PM", 2) == 0) {
00525                 hourformat = ePM;
00526                 sss += 2;
00527             }
00528             continue;
00529         }
00530 
00531         // Other format symbols -- read the next data ingredient
00532         char value_str[10];
00533         char* s = value_str;
00534         size_t len = 2;
00535         switch (*fff) {
00536             case 'Y': len = 4; break;
00537             case 'S': len = 9; break;
00538             case 'l': len = 3; break;
00539             case 'r': len = 6; break;
00540         }
00541         for ( ; len  &&  *sss  &&  isdigit((unsigned char)(*sss));  len--) {
00542             *s++ = *sss++;
00543         }
00544         *s = '\0';
00545         long value = NStr::StringToLong(value_str);
00546 
00547         // Set time part
00548         switch ( *fff ) {
00549         case 'Y':
00550             CHECK_RANGE_YEAR(value);
00551             m_Data.year = (unsigned int)value;
00552             is_year_present = true;
00553             break;
00554         case 'y':
00555             if (value >= 0  &&  value < 50) {
00556                 value += 2000;
00557             } else if (value >= 50  &&  value < 100) {
00558                 value += 1900;
00559             }
00560             CHECK_RANGE_YEAR(value);
00561             m_Data.year = (unsigned int)value;
00562             is_year_present = true;
00563             break;
00564         case 'M':
00565             CHECK_RANGE_MONTH(value);
00566             m_Data.month = (unsigned char)value;
00567             is_month_present = true;
00568             break;
00569         case 'D':
00570         case 'd':
00571             CHECK_RANGE_DAY(value);
00572             m_Data.day = (unsigned char)value;
00573             is_day_present = true;
00574             break;
00575         case 'h':
00576             CHECK_RANGE_HOUR(value);
00577             m_Data.hour = (unsigned char)value;
00578             is_time_present = true;
00579             break;
00580         case 'H':
00581             CHECK_RANGE_HOUR(value);
00582             m_Data.hour = (unsigned char)value % 12;
00583             is_12hour = true;
00584             is_time_present = true;
00585             break;
00586         case 'm':
00587             CHECK_RANGE_MIN(value);
00588             m_Data.min = (unsigned char)value;
00589             is_time_present = true;
00590             break;
00591         case 's':
00592             CHECK_RANGE_SEC(value);
00593             m_Data.sec = (unsigned char)value;
00594             is_time_present = true;
00595             break;
00596         case 'l':
00597             CHECK_RANGE_NSEC((Int8)value * 1000000);
00598             m_Data.nanosec = (Int4)value * 1000000;
00599             is_time_present = true;
00600             break;
00601         case 'r':
00602             CHECK_RANGE_NSEC((Int8)value * 1000);
00603             m_Data.nanosec = (Int4)value * 1000;
00604             is_time_present = true;
00605             break;
00606         case 'S':
00607             CHECK_RANGE_NSEC(value);
00608             m_Data.nanosec = (Int4)value;
00609             is_time_present = true;
00610             break;
00611         default:
00612             NCBI_THROW(CTimeException, eFormat,
00613                        "CTime::x_Init(): format '" + fmt + "' is incorrect");
00614         }
00615     }
00616 
00617     // Correct 12-hour time if needed
00618     if (is_12hour  &&  hourformat == ePM) {
00619         m_Data.hour += 12;
00620     }
00621 
00622     while ( isspace((unsigned char)(*sss)) )
00623         sss++;
00624 
00625     if (*fff != '\0'  &&  
00626         !(format.GetFlags() & CTimeFormat::fMatch_ShortTime)) {
00627         NCBI_THROW(CTimeException, eFormat, 
00628                    "CTime::x_Init(): time string '" + str +
00629                    "' is too short for time format '" + fmt + "'");
00630     }
00631     if (*sss != '\0'  &&  
00632         !(format.GetFlags() & CTimeFormat::fMatch_ShortFormat)) {
00633         NCBI_THROW(CTimeException, eFormat,
00634                    "CTime::x_Init(): time string '" + str +
00635                    "' is too long for time format '" + fmt + "'");
00636     }
00637 
00638     // For partialy defined times use default values
00639     int ptcache = 0;
00640     ptcache += (is_year_present  ? 2000 : 1000);
00641     ptcache += (is_month_present ? 200 : 100);
00642     ptcache += (is_day_present   ? 20 : 10);
00643     ptcache += (is_time_present  ? 2 : 1);
00644 
00645     // Use empty or current time to set missed time components
00646     CTime current;
00647     if ( !adjust_needed ) {
00648         switch (ptcache) {
00649             case 1222:
00650             case 1221:
00651             case 1211:
00652             case 1121:
00653             case 1122:
00654             case 1112:
00655                 current.SetCurrent();
00656         }
00657     }
00658     switch (ptcache) {
00659         case 2211:                          // Y,M      -> D = 1
00660             m_Data.day   = 1;
00661             break;
00662         case 2111:                          // Y        -> M,D = 1
00663             m_Data.month = 1;
00664             m_Data.day   = 1;
00665             break;
00666         case 1222:                          // M,D,time -> Y = current
00667         case 1221:                          // M,D      -> Y = current
00668             m_Data.year  = current.Year();
00669             break;
00670         case 1211:                          // M        -> Y = current, D = 1
00671             m_Data.year  = current.Year();
00672             m_Data.day   = 1;
00673             break;
00674         case 1122:                          // D, time  -> Y,M = current
00675         case 1121:                          // D        -> Y,M = current
00676             m_Data.year  = current.Year();
00677             m_Data.month = current.Month();
00678             break;
00679         case 1112:                          // time     -> Y,M,D = current
00680             m_Data.year  = current.Year();
00681             m_Data.month = current.Month();
00682             m_Data.day   = current.Day();
00683             break;
00684     }
00685 
00686     // Check on errors for weekday
00687     if (weekday != -1  &&  weekday != DayOfWeek()) {
00688         NCBI_THROW(CTimeException, eConvert,
00689                    "CTime::x_Init(): invalid day of week " + 
00690                    NStr::IntToString(weekday));
00691     }
00692     // Validate time value
00693     if ( !IsValid() ) {
00694         NCBI_THROW(CTimeException, eConvert,
00695                    "CTime::x_Init(): unable to convert string '" + str +
00696                    "' to CTime");
00697     }
00698     // Adjust time to GMT time (see 'z' format symbol above)
00699     if ( adjust_needed ) {
00700         AddSecond(-adjust_tz, CTime::eIgnoreDaylight);
00701     }
00702 }
00703 
00704 
00705 CTime::CTime(int year, int month, int day, int hour,
00706              int minute, int second, long nanosecond,
00707              ETimeZone tz, ETimeZonePrecision tzp)
00708 {
00709     CHECK_RANGE_YEAR(year);
00710     CHECK_RANGE_MONTH(month);
00711     CHECK_RANGE_DAY(day);
00712     CHECK_RANGE_HOUR(hour);
00713     CHECK_RANGE_MIN(minute);
00714     CHECK_RANGE_SEC(second);
00715     CHECK_RANGE_NSEC(nanosecond);
00716 
00717     m_Data.year        = year;
00718     m_Data.month       = month;
00719     m_Data.day         = day;
00720     m_Data.hour        = hour;
00721     m_Data.min         = minute;
00722     m_Data.sec         = second;
00723     m_Data.nanosec     = (Int4)nanosecond;
00724     m_Data.tz          = tz;
00725     m_Data.tzprec      = tzp;
00726     m_Data.adjTimeDiff = 0;
00727 
00728     if ( !IsValid() ) {
00729         NCBI_THROW(CTimeException, eInvalid,
00730                    "CTime::CTime(): invalid time");
00731     }
00732 }
00733 
00734 
00735 CTime::CTime(EInitMode mode, ETimeZone tz, ETimeZonePrecision tzp)
00736 {
00737     m_Data.tz = tz;
00738     m_Data.tzprec = tzp;
00739 
00740     if (mode == eCurrent) {
00741         SetCurrent();
00742     } else {
00743         Clear();
00744     }
00745 }
00746 
00747 
00748 CTime::CTime(time_t t, ETimeZonePrecision tzp)
00749 {
00750     m_Data.tz = eGmt;
00751     m_Data.tzprec = tzp;
00752     SetTimeT(t);
00753 }
00754 
00755 
00756 CTime::CTime(const struct tm& t, ETimeZonePrecision tzp)
00757 {
00758     m_Data.tz = eLocal;
00759     m_Data.tzprec = tzp;
00760     SetTimeTM(t);
00761 }
00762 
00763 
00764 CTime::CTime(const string& str, const CTimeFormat& format,
00765              ETimeZone tz, ETimeZonePrecision tzp)
00766 {
00767     m_Data.tz = tz;
00768     m_Data.tzprec = tzp;
00769 
00770     if (format.IsEmpty()) {
00771         x_Init(str, GetFormat());
00772     } else {
00773         x_Init(str, format);
00774     }
00775 }
00776 
00777 
00778 void CTime::SetYear(int year)
00779 {
00780     CHECK_RANGE_YEAR(year);
00781     m_Data.year = year;
00782     int n_days = DaysInMonth();
00783     if ( m_Data.day > n_days ) {
00784         m_Data.day = n_days;
00785     }
00786     // Additional checks
00787     if ( !IsValid() ) {
00788         NCBI_THROW(CTimeException, eInvalid,
00789                    "CTime::SetYear(): unable to set year number '" +
00790                    NStr::IntToString(year) + "'");
00791     }
00792 }
00793 
00794 
00795 void CTime::SetMonth(int month)
00796 {
00797     CHECK_RANGE_MONTH(month);
00798     m_Data.month = month;
00799     int n_days = DaysInMonth();
00800     if ( m_Data.day > n_days ) {
00801         m_Data.day = n_days;
00802     }
00803     // Additional checks
00804     if ( !IsValid() ) {
00805         NCBI_THROW(CTimeException, eInvalid,
00806                    "CTime::SetMonth(): unable to set month number '" +
00807                    NStr::IntToString(month) + "'");
00808     }
00809 }
00810 
00811 
00812 void CTime::SetDay(int day)
00813 {
00814     CHECK_RANGE_DAY(day);
00815     int n_days = DaysInMonth();
00816     if ( day > n_days ) {
00817         m_Data.day = n_days;
00818     } else {
00819         m_Data.day = day;
00820     }
00821     // Additional checks
00822     if ( !IsValid() ) {
00823         NCBI_THROW(CTimeException, eInvalid,
00824                    "CTime::SetDay(): unable to set day number '" +
00825                    NStr::IntToString(day) + "'");
00826     }
00827 }
00828 
00829 
00830 void CTime::SetHour(int hour)
00831 {
00832     CHECK_RANGE_HOUR(hour);
00833     m_Data.hour = hour;
00834 }
00835 
00836 
00837 void CTime::SetMinute(int minute)
00838 {
00839     CHECK_RANGE_MIN(minute);
00840     m_Data.min = minute;
00841 }
00842 
00843 
00844 void CTime::SetSecond(int second)
00845 {
00846     CHECK_RANGE_SEC(second);
00847     m_Data.sec = second;
00848 }
00849 
00850 
00851 void CTime::SetMilliSecond(long millisecond)
00852 {
00853     CHECK_RANGE_NSEC(millisecond * 1000000);
00854     m_Data.nanosec = (Int4)millisecond * 1000000;
00855 }
00856 
00857 
00858 void CTime::SetMicroSecond(long microsecond)
00859 {
00860     CHECK_RANGE_NSEC(microsecond * 1000);
00861     m_Data.nanosec = (Int4)microsecond * 1000;
00862 }
00863 
00864 
00865 void CTime::SetNanoSecond(long nanosecond)
00866 {
00867     CHECK_RANGE_NSEC(nanosecond);
00868     m_Data.nanosec = (Int4)nanosecond;
00869 }
00870 
00871 
00872 int CTime::YearDayNumber(void) const
00873 {
00874     unsigned first = s_Date2Number(CTime(Year(), 1, 1));
00875     unsigned self  = s_Date2Number(*this);
00876     _ASSERT(first <= self  &&  self < first + (IsLeap() ? 366 : 365));
00877     return int(self - first + 1);
00878 }
00879 
00880 
00881 int CTime::YearWeekNumber(EDayOfWeek first_day_of_week) const
00882 {
00883     if ( IsEmptyDate() ) {
00884         NCBI_THROW(CTimeException, eInvalid, 
00885                    "CTime::YearWeekNumber(): the date is empty");
00886     }
00887     if (first_day_of_week > eSaturday) {
00888         NCBI_THROW(CTimeException, eArgument,
00889                    "CTime::YearWeekNumber(): argument " + 
00890                    NStr::IntToString((int)first_day_of_week) +
00891                    " is incorrect");
00892     }
00893 
00894     int week_num = 0;
00895     int wday = DayOfWeek();
00896 
00897     // Adjust day of week (from default Sunday)
00898     wday -= first_day_of_week;
00899     if (wday < 0) {
00900         wday += 7;
00901     }
00902 
00903     // Calculate week number
00904     int yday = YearDayNumber() - 1;  // YearDayNumber() returns 1..366
00905     if (yday >= wday) {
00906         week_num = yday / 7;
00907         if ( (yday % 7) >= wday ) {
00908             week_num++;
00909         }
00910     }
00911     // Adjust range from [0..53] to [1..54]
00912     return week_num + 1;
00913 }
00914 
00915 
00916 int CTime::MonthWeekNumber(EDayOfWeek first_day_of_week) const
00917 {
00918     CTime first_of_month(Year(), Month(), 1);
00919     int week_num_first   = first_of_month.YearWeekNumber(first_day_of_week);
00920     int week_num_current = YearWeekNumber(first_day_of_week);
00921     return week_num_current - week_num_first + 1;
00922 }
00923 
00924 
00925 int CTime::DayOfWeek(void) const
00926 {
00927     if ( IsEmptyDate() ) {
00928         NCBI_THROW(CTimeException, eInvalid,
00929                    "CTime::DayOfWeek(): the date is empty");
00930     }
00931     int y = Year();
00932     int m = Month();
00933 
00934     y -= int(m < 3);
00935     return (y + y/4 - y/100 + y/400 + "-bed=pen+mad."[m] + Day()) % 7;
00936 }
00937 
00938 
00939 int CTime::DaysInMonth(void) const
00940 {
00941     if ( IsEmptyDate() ) {
00942         NCBI_THROW(CTimeException, eInvalid,
00943                    "CTime::DaysInMonth(): the date is empty");
00944     }
00945     int n_days = s_DaysInMonth[Month()-1];
00946     if (n_days == 0) {
00947         n_days = IsLeap() ? 29 : 28;
00948     }
00949     return n_days;
00950 }
00951 
00952 
00953 int CTime::MonthNameToNum(const string& month)
00954 {
00955     const char** name = month.length() == 3 ? kMonthAbbr : kMonthFull;
00956     for (int i = 0; i < 12; i++) {
00957         if (month == name[i]) {
00958             return i+1;
00959         }
00960     }
00961     // Always throw exceptions here.
00962     // Next if statements avoid compilation warnings.
00963     if ( name ) {
00964         NCBI_THROW(CTimeException, eArgument,
00965                    "CTime::MonthNameToNum(): invalid month name '" +
00966                    month + "'");
00967     }
00968     return -1;
00969 }
00970 
00971 
00972 string CTime::MonthNumToName(int month, ENameFormat format)
00973 {
00974     if (month < 1  ||  month > 12) {
00975         NCBI_THROW(CTimeException, eArgument,
00976                    "CTime::MonthNumToName(): invalid month number " +
00977                    NStr::IntToString(month));
00978     }
00979     month--;
00980     return format == eFull ? kMonthFull[month] : kMonthAbbr[month];
00981 }
00982 
00983 
00984 int CTime::DayOfWeekNameToNum(const string& day)
00985 {
00986     const char** name = day.length() == 3 ? kWeekdayAbbr : kWeekdayFull;
00987     for (int i = 0; i <= 6; i++) {
00988         if (day == name[i]) {
00989             return i;
00990         }
00991     }
00992     // Always throw exceptions here.
00993     // Next if statements avoid compilation warnings.
00994     if ( name ) {
00995         NCBI_THROW(CTimeException, eArgument,
00996                    "CTime::DayOfWeekNameToNum(): invalid day of week name '" +
00997                    day + "'");
00998     }
00999     return -1;
01000 }
01001 
01002 
01003 string CTime::DayOfWeekNumToName(int day, ENameFormat format)
01004 {
01005     if (day < 0  ||  day > 6) {
01006         return kEmptyStr;
01007     }
01008     return format == eFull ? kWeekdayFull[day] : kWeekdayAbbr[day];
01009 }
01010 
01011 
01012 void CTime::SetFormat(const CTimeFormat& format)
01013 {
01014     // Here we do not need to delete a previous value stored in the TLS.
01015     // The TLS will destroy it using s_TlsFormatCleanup().
01016     CTimeFormat* ptr = new CTimeFormat(format);
01017     s_TlsFormatTime.SetValue(ptr, s_TlsFormatCleanup);
01018 }
01019 
01020 
01021 CTimeFormat CTime::GetFormat(void)
01022 {
01023     CTimeFormat format;
01024     CTimeFormat* ptr = s_TlsFormatTime.GetValue();
01025     if ( !ptr ) {
01026         format.SetFormat(kDefaultFormatTime);
01027     } else {
01028         format = *ptr;
01029     }
01030     return format;
01031 }
01032 
01033 
01034 string CTime::AsString(const CTimeFormat& format, TSeconds out_tz) const
01035 {
01036     if ( !IsValid() ) {
01037         NCBI_THROW(CTimeException, eInvalid,
01038                    "CTime::AsString(): invalid time");
01039     }
01040     if ( IsEmpty() ) {
01041         return kEmptyStr;
01042     }
01043 #if !defined(TIMEZONE_IS_UNDEFINED)
01044     // MT-Safe protect
01045     CFastMutexGuard LOCK(s_TimeMutex);
01046 #endif
01047 
01048     const CTime* t = this;
01049     CTime* t_out = 0;
01050     // Adjust time for output timezone
01051     if (out_tz != eCurrentTimeZone) {
01052 #if defined(TIMEZONE_IS_UNDEFINED)
01053         ERR_POST_X(4, "Output timezone is unsupported on this platform");
01054 #else
01055         if (out_tz != TimeZone()) {
01056             t_out = new CTime(*this);
01057             t_out->AddSecond(TimeZone() - out_tz);
01058             t = t_out;
01059         }
01060 #endif
01061     }
01062     string str;
01063     str.reserve(64); // try to save on memory allocations
01064     string fmt;
01065     CTimeFormat::TFlags fmt_flags;
01066     if ( format.IsEmpty() ) {
01067         CTimeFormat f = GetFormat();
01068         fmt       = f.GetString();
01069         fmt_flags = f.GetFlags();
01070     } else {
01071         fmt       = format.GetString();
01072         fmt_flags = format.GetFlags();
01073     }
01074     bool is_escaped = ((fmt_flags & CTimeFormat::fFormat_Simple) == 0);
01075     bool is_format_symbol = !is_escaped;
01076 
01077     ITERATE(string, it, fmt) {
01078 
01079         if ( !is_format_symbol ) {
01080             if ( *it == kFormatEscapeSymbol )  {
01081                 is_format_symbol = true;
01082             } else {
01083                 str += *it;
01084             }
01085             continue;
01086         }
01087         if ( is_escaped ) {
01088             is_format_symbol = false;
01089         }
01090         switch ( *it ) {
01091         case 'y': s_AddZeroPadInt2(str, t->Year() % 100);   break;
01092         case 'Y': s_AddZeroPadInt(str, t->Year(), 4);       break;
01093         case 'M': s_AddZeroPadInt2(str, t->Month());        break;
01094         case 'b': str += kMonthAbbr[t->Month()-1];          break;
01095         case 'B': str += kMonthFull[t->Month()-1];          break;
01096         case 'D': s_AddZeroPadInt2(str, t->Day());          break;
01097         case 'd': s_AddZeroPadInt(str, t->Day(),1);         break;
01098         case 'h': s_AddZeroPadInt2(str, t->Hour());         break;
01099         case 'H': s_AddZeroPadInt2(str, (t->Hour()+11) % 12+1);
01100                   break;
01101         case 'm': s_AddZeroPadInt2(str, t->Minute());       break;
01102         case 's': s_AddZeroPadInt2(str, t->Second());       break;
01103         case 'l': s_AddZeroPadInt(str, t->NanoSecond() / 1000000, 3);
01104                   break;
01105         case 'r': s_AddZeroPadInt(str, t->NanoSecond() / 1000, 6);
01106                   break;
01107         case 'S': s_AddZeroPadInt(str, t->NanoSecond(), 9); break;
01108         case 'p': str += ( t->Hour() < 12) ? "am" : "pm" ;  break;
01109         case 'P': str += ( t->Hour() < 12) ? "AM" : "PM" ;  break;
01110         case 'z': {
01111 #if defined(TIMEZONE_IS_UNDEFINED)
01112                   ERR_POST_X(5, "Format symbol 'z' is unsupported "
01113                                 "on this platform");
01114 #else
01115                   str += "GMT";
01116                   if (IsGmtTime()) {
01117                       break;
01118                   }
01119                   TSeconds tz = (out_tz == eCurrentTimeZone) ?
01120                                 TimeZone() : out_tz;
01121                   str += (tz > 0) ? '-' : '+';
01122                   if (tz < 0) tz = -tz;
01123                   int tzh = int(tz / 3600);
01124                   s_AddZeroPadInt2(str, tzh);
01125                   s_AddZeroPadInt2(str, (int)(tz - tzh * 3600) / 60);
01126 #endif
01127                   break;
01128                   }
01129         case 'Z': if (IsGmtTime()) str += "GMT";            break;
01130         case 'w': str += kWeekdayAbbr[t->DayOfWeek()];      break;
01131         case 'W': str += kWeekdayFull[t->DayOfWeek()];      break;
01132         default : str += *it;                               break;
01133         }
01134     }
01135     // Free used memory
01136     if ( t_out ) {
01137         delete t_out;
01138     }
01139     return str;
01140 }
01141 
01142 
01143 time_t CTime::GetTimeT(void) const
01144 {
01145     if ( IsEmptyDate() ) {
01146         NCBI_THROW(CTimeException, eInvalid,
01147                    "CTime::GetTimeT(): the date is empty");
01148     }
01149     // MT-Safe protect
01150     CFastMutexGuard LOCK(s_TimeMutex);
01151 
01152     struct tm t;
01153 
01154     // Convert time to time_t value at base local time
01155 #if defined(HAVE_TIMEGM)  ||  defined(NCBI_OS_DARWIN)
01156     t.tm_sec   = Second();
01157 #else
01158     t.tm_sec   = Second() + (int)(IsGmtTime() ? -TimeZone() : 0);
01159 #endif
01160     t.tm_min   = Minute();
01161     t.tm_hour  = Hour();
01162     t.tm_mday  = Day();
01163     t.tm_mon   = Month()-1;
01164     t.tm_year  = Year()-1900;
01165     t.tm_isdst = -1;
01166 #if defined(NCBI_OS_DARWIN)
01167     time_t tt = mktime(&t);
01168     if ( tt == -1 ) {
01169         return -1;
01170     }
01171     return IsGmtTime() ? tt+t.tm_gmtoff : tt;
01172 #elif defined(HAVE_TIMEGM)
01173     return IsGmtTime() ? timegm(&t) : mktime(&t);
01174 #else
01175     struct tm *ttemp;
01176     time_t timer;
01177     timer = mktime(&t);
01178     if ( timer == -1 ) {
01179         return -1;
01180     }
01181 
01182     // Correct timezone for GMT time
01183     if ( IsGmtTime() ) {
01184 
01185        // Call mktime() second time for GMT time !!!
01186        // 1st - to get correct value of TimeZone().
01187        // 2nd - to get value "timer".
01188 
01189         t.tm_sec   = Second() - (int)TimeZone();
01190         t.tm_min   = Minute();
01191         t.tm_hour  = Hour();
01192         t.tm_mday  = Day();
01193         t.tm_mon   = Month()-1;
01194         t.tm_year  = Year()-1900;
01195         t.tm_isdst = -1;
01196         timer = mktime(&t);
01197         if ( timer == -1 ) {
01198             return -1;
01199         }
01200 
01201 #  if defined(HAVE_LOCALTIME_R)
01202         struct tm temp;
01203         localtime_r(&timer, &temp);
01204         ttemp = &temp;
01205 #  else
01206         ttemp = localtime(&timer);
01207 #  endif
01208         if (ttemp == NULL)
01209             return -1;
01210         if (ttemp->tm_isdst > 0  &&  Daylight())
01211             timer += 3600;
01212     }
01213     return timer;
01214 #endif
01215 }
01216 
01217 
01218 struct tm CTime::GetTimeTM(void) const
01219 {
01220     CTime lt = GetLocalTime();
01221     struct tm t;
01222     t.tm_sec   = lt.Second();
01223     t.tm_min   = lt.Minute();
01224     t.tm_hour  = lt.Hour();
01225     t.tm_mday  = lt.Day();
01226     t.tm_mon   = lt.Month()-1;
01227     t.tm_year  = lt.Year()-1900;
01228     t.tm_wday  = lt.DayOfWeek();
01229     t.tm_yday  = -1;
01230     t.tm_isdst = -1;
01231     return t;
01232 }
01233 
01234 
01235 CTime& CTime::SetTimeTM(const struct tm& t)
01236 {
01237     CHECK_RANGE_YEAR   (t.tm_year + 1900);
01238     CHECK_RANGE_MONTH  (t.tm_mon + 1);
01239     CHECK_RANGE_DAY    (t.tm_mday);
01240     CHECK_RANGE_HOUR   (t.tm_hour);
01241     CHECK_RANGE_MIN    (t.tm_min);
01242     CHECK_RANGE_SEC    (t.tm_sec);
01243 
01244     m_Data.year        = t.tm_year + 1900;
01245     m_Data.month       = t.tm_mon + 1;
01246     m_Data.day         = t.tm_mday;
01247     m_Data.hour        = t.tm_hour;
01248     m_Data.min         = t.tm_min;
01249     m_Data.sec         = t.tm_sec;
01250     m_Data.nanosec     = 0;
01251     m_Data.tz          = eLocal;
01252     //m_Data.tzprec    -- not changed;
01253     m_Data.adjTimeDiff = 0;
01254 
01255     if ( !IsValid() ) {
01256         NCBI_THROW(CTimeException, eConvert,
01257                    "CTime::SetTimeTM(): invalid time");
01258     }
01259     return *this;
01260 }
01261 
01262 
01263 
01264 TDBTimeU CTime::GetTimeDBU(void) const
01265 {
01266     TDBTimeU dbt;
01267     CTime t  = GetLocalTime();
01268     unsigned first = s_Date2Number(CTime(1900, 1, 1));
01269     unsigned curr  = s_Date2Number(t);
01270 
01271     dbt.days = (Uint2)(curr - first);
01272     dbt.time = (Uint2)(t.Hour() * 60 + t.Minute());
01273     return dbt;
01274 }
01275 
01276 
01277 TDBTimeI CTime::GetTimeDBI(void) const
01278 {
01279     TDBTimeI dbt;
01280     CTime t  = GetLocalTime();
01281     unsigned first = s_Date2Number(CTime(1900, 1, 1));
01282     unsigned curr  = s_Date2Number(t);
01283 
01284     dbt.days = (Int4)(curr - first);
01285     dbt.time = (Int4)((t.Hour() * 3600 + t.Minute() * 60 + t.Second()) * 300) +
01286                (Int4)((double)t.NanoSecond() * 300 / kNanoSecondsPerSecond);
01287     return dbt;
01288 }
01289 
01290 
01291 CTime& CTime::SetTimeDBU(const TDBTimeU& t)
01292 {
01293     // Local time - 1/1/1900 00:00:00.0
01294     CTime time(1900, 1, 1, 0, 0, 0, 0, eLocal);
01295 
01296     time.SetTimeZonePrecision(GetTimeZonePrecision());
01297     time.AddDay(t.days);
01298     time.AddMinute(t.time);
01299     time.ToTime(GetTimeZone());
01300 
01301     *this = time;
01302     return *this;
01303 }
01304 
01305 
01306 CTime& CTime::SetTimeDBI(const TDBTimeI& t)
01307 {
01308     // Local time - 1/1/1900 00:00:00.0
01309     CTime time(1900, 1, 1, 0, 0, 0, 0, eLocal);
01310 
01311     time.SetTimeZonePrecision(GetTimeZonePrecision());
01312     time.AddDay(t.days);
01313     time.AddSecond(t.time / 300);
01314     time.AddNanoSecond((long)((t.time % 300) *
01315                               (double)kNanoSecondsPerSecond / 300));
01316     time.ToTime(GetTimeZone());
01317 
01318     *this = time;
01319     return *this;
01320 }
01321 
01322 
01323 CTime& CTime::x_SetTimeMTSafe(const time_t* value)
01324 {
01325     // MT-Safe protect
01326     CFastMutexGuard LOCK(s_TimeMutex);
01327     x_SetTime(value);
01328     return *this;
01329 }
01330 
01331 // Get current GMT time with nanoseconds
01332 static void s_GetTimeT(time_t& timer, long& ns)
01333 {
01334 #if defined(NCBI_OS_MSWIN)
01335     struct _timeb timebuffer;
01336     _ftime(&timebuffer);
01337     timer = timebuffer.time;
01338     ns = (long) timebuffer.millitm *
01339          (long) (kNanoSecondsPerSecond / kMilliSecondsPerSecond);
01340 
01341 #elif defined(NCBI_OS_UNIX)
01342     struct timeval tp;
01343     if (gettimeofday(&tp,0) == -1) {
01344         timer = -1;
01345     } else {
01346         timer = tp.tv_sec;
01347         ns = long((double)tp.tv_usec *
01348                   (double)kNanoSecondsPerSecond /
01349                   (double)kMicroSecondsPerSecond);
01350     }
01351 #else
01352     timer = time(0);
01353     ns = 0;
01354 #endif
01355     if (timer == (time_t)(-1)) {
01356         NCBI_THROW(CTimeException, eConvert,
01357                    "s_GetTimeT(): unable to get time value");
01358     }
01359 }
01360 
01361 
01362 CTime& CTime::x_SetTime(const time_t* value)
01363 {
01364     time_t timer;
01365     long ns = 0;
01366 
01367     // Get time with nanoseconds
01368     if ( value ) {
01369         timer = *value;
01370     } else {
01371         s_GetTimeT(timer, ns);
01372     }
01373 
01374     // Bind values to internal variables
01375     struct tm *t;
01376 
01377 #ifdef HAVE_LOCALTIME_R
01378     struct tm temp;
01379     if (GetTimeZone() == eLocal) {
01380         localtime_r(&timer, &temp);
01381     } else {
01382         gmtime_r(&timer, &temp);
01383     }
01384     t = &temp;
01385 #else
01386     t = ( GetTimeZone() == eLocal ) ? localtime(&timer) : gmtime(&timer);
01387     if ( !t ) {
01388         // Error was detected: incorrect timer value or system error
01389         NCBI_THROW(CTimeException, eConvert, 
01390                    "CTime::x_SetTime(): localtime/gmtime error, "
01391                    "possible incorrect time_t value");
01392     }
01393 #endif
01394     m_Data.adjTimeDiff = 0;
01395     m_Data.year        = t->tm_year + 1900;
01396     m_Data.month       = t->tm_mon + 1;
01397     m_Data.day         = t->tm_mday;
01398     m_Data.hour        = t->tm_hour;
01399     m_Data.min         = t->tm_min;
01400     m_Data.sec         = t->tm_sec;
01401     CHECK_RANGE_NSEC(ns);
01402     m_Data.nanosec     = (Int4)ns;
01403     return *this;
01404 }
01405 
01406 
01407 CTime& CTime::AddMonth(int months, EDaylight adl)
01408 {
01409     if ( IsEmptyDate() ) {
01410         NCBI_THROW(CTimeException, eInvalid,
01411                    "CTime::AddMonth(): the date is empty");
01412     }
01413     if ( !months ) {
01414         return *this;
01415     }
01416     CTime *pt = 0;
01417     bool aflag = false;
01418     if ((adl == eAdjustDaylight)  &&  x_NeedAdjustTime()) {
01419         pt = new CTime(*this);
01420         if ( !pt ) {
01421             NCBI_THROW(CCoreException, eNullPtr, kEmptyStr);
01422         }
01423         aflag = true;
01424     }
01425     long newMonth = Month() - 1;
01426     int newYear = Year();
01427     s_Offset(&newMonth, months, 12, &newYear);
01428     m_Data.year = newYear;
01429     m_Data.month = (int)newMonth + 1;
01430     x_AdjustDay();
01431     if ( aflag ) {
01432         x_AdjustTime(*pt);
01433         delete pt;
01434     }
01435     return *this;
01436 }
01437 
01438 
01439 CTime& CTime::AddDay(int days, EDaylight adl)
01440 {
01441     if ( IsEmptyDate() ) {
01442         NCBI_THROW(CTimeException, eInvalid,
01443                    "CTime::AddDay(): the date is empty");
01444     }
01445     if ( !days ) {
01446         return *this;
01447     }
01448     CTime *pt = 0;
01449     bool aflag = false;
01450     if ((adl == eAdjustDaylight)  &&  x_NeedAdjustTime()) {
01451         pt = new CTime(*this);
01452         if ( !pt ) {
01453             NCBI_THROW(CCoreException, eNullPtr, kEmptyStr);
01454         }
01455         aflag = true;
01456     }
01457 
01458     // Make necessary object
01459     *this = s_Number2Date(s_Date2Number(*this) + days, *this);
01460 
01461     // If need, make adjustment time specially
01462     if ( aflag ) {
01463         x_AdjustTime(*pt);
01464         delete pt;
01465     }
01466     return *this;
01467 }
01468 
01469 
01470 // Parameter <shift_time> access or denied use time shift in process
01471 // adjust hours.
01472 CTime& CTime::x_AddHour(int hours, EDaylight adl, bool shift_time)
01473 {
01474     if ( IsEmptyDate() ) {
01475         NCBI_THROW(CTimeException, eInvalid,
01476                    "CTime::x_AddHour(): the date is empty");
01477     }
01478     if ( !hours ) {
01479         return *this;
01480     }
01481     CTime *pt = 0;
01482     bool aflag = false;
01483     if ((adl == eAdjustDaylight)  &&  x_NeedAdjustTime()) {
01484         pt = new CTime(*this);
01485         if ( !pt ) {
01486             NCBI_THROW(CCoreException, eNullPtr, kEmptyStr);
01487         }
01488         aflag = true;
01489     }
01490     int dayOffset = 0;
01491     long newHour = Hour();
01492     s_Offset(&newHour, hours, 24, &dayOffset);
01493     m_Data.hour = (int)newHour;
01494     AddDay(dayOffset, eIgnoreDaylight);
01495     if ( aflag ) {
01496         x_AdjustTime(*pt, shift_time);
01497         delete pt;
01498     }
01499     return *this;
01500 }
01501 
01502 
01503 CTime& CTime::AddMinute(int minutes, EDaylight adl)
01504 {
01505     if ( IsEmptyDate() ) {
01506         NCBI_THROW(CTimeException, eInvalid,
01507                    "CTime::AddMinute(): the date is empty");
01508     }
01509     if ( !minutes ) {
01510         return *this;
01511     }
01512     CTime *pt = 0;
01513     bool aflag = false;
01514     if ((adl == eAdjustDaylight) && x_NeedAdjustTime()) {
01515         pt = new CTime(*this);
01516         if ( !pt ) {
01517             NCBI_THROW(CCoreException, eNullPtr, kEmptyStr);
01518         }
01519         aflag = true;
01520     }
01521     int hourOffset = 0;
01522     long newMinute = Minute();
01523     s_Offset(&newMinute, minutes, 60, &hourOffset);
01524     m_Data.min = (int)newMinute;
01525     AddHour(hourOffset, eIgnoreDaylight);
01526     if ( aflag ) {
01527         x_AdjustTime(*pt);
01528         delete pt;
01529     }
01530     return *this;
01531 }
01532 
01533 
01534 CTime& CTime::AddSecond(TSeconds seconds, EDaylight adl)
01535 {
01536     if ( IsEmptyDate() ) {
01537         NCBI_THROW(CTimeException, eInvalid,
01538                    "CTime::AddSecond(): the date is empty");
01539     }
01540     if ( !seconds ) {
01541         return *this;
01542     }
01543     int minuteOffset = 0;
01544     long newSecond = Second();
01545     s_Offset(&newSecond, seconds, 60, &minuteOffset);
01546     m_Data.sec = (int)newSecond;
01547     return AddMinute(minuteOffset, adl);
01548 }
01549 
01550 
01551 CTime& CTime::AddNanoSecond(long ns)
01552 {
01553     if ( IsEmptyDate() ) {
01554         NCBI_THROW(CTimeException, eInvalid,
01555                    "CTime::AddNanoSecond(): the date is empty");
01556     }
01557     if ( !ns ) {
01558         return *this;
01559     }
01560     int secondOffset = 0;
01561     long newNanoSecond = NanoSecond();
01562     s_Offset(&newNanoSecond, ns, kNanoSecondsPerSecond, &secondOffset);
01563     m_Data.nanosec = (Int4)newNanoSecond;
01564     return AddSecond(secondOffset);
01565 }
01566 
01567 
01568 CTime& CTime::AddTimeSpan(const CTimeSpan& ts)
01569 {
01570     if ( ts.GetSign() == eZero ) {
01571         return *this;
01572     }
01573     AddSecond(ts.GetCompleteSeconds());
01574     AddNanoSecond(ts.GetNanoSecondsAfterSecond());
01575     return *this;
01576 }
01577 
01578 
01579 CTime& CTime::Round(ERoundPrecision precision, EDaylight adl)
01580 {
01581     if ( IsEmptyDate() ) {
01582         return *this;
01583     }
01584     switch (precision) {
01585         case eRound_Day:
01586             if ( m_Data.hour >= 12 )
01587                 AddDay(1, adl);
01588             break;
01589         case eRound_Hour:
01590             if ( m_Data.min >= 30 )
01591                 AddHour(1, adl);
01592             break;
01593         case eRound_Minute:
01594             if ( m_Data.sec >= 30 )
01595                 AddMinute(1, adl);
01596             break;
01597         case eRound_Second:
01598             if ( m_Data.nanosec >= kNanoSecondsPerSecond/2 )
01599                 AddSecond(1, adl);
01600             m_Data.nanosec = 0;
01601             break;
01602         case eRound_Millisecond:
01603             m_Data.nanosec = 
01604                 (Int4)(m_Data.nanosec + kNanoSecondsPerSecond/2000) 
01605                 / 1000000 * 1000000;
01606             break;
01607         case eRound_Microsecond:
01608             m_Data.nanosec = 
01609                 (Int4)(m_Data.nanosec + kNanoSecondsPerSecond/2000000)
01610                 / 1000 * 1000;
01611             break;
01612         default:
01613             NCBI_THROW(CTimeException, eArgument,
01614                        "CTime::Round(): rounding precision is out of range");
01615     }
01616     if ( m_Data.nanosec == kNanoSecondsPerSecond ) {
01617         AddSecond(1, adl);
01618         m_Data.nanosec = 0;
01619     }
01620     // Clean time components with lesser precision
01621     Truncate(precision);
01622     return *this;
01623 }
01624 
01625 
01626 CTime& CTime::Truncate(ERoundPrecision precision)
01627 {
01628     // Clean time components with lesser precision
01629     switch (precision) {
01630         case eRound_Day:
01631             m_Data.hour = 0;
01632             // fall through
01633         case eRound_Hour:
01634             m_Data.min = 0;
01635             // fall through
01636         case eRound_Minute:
01637             m_Data.sec = 0;
01638             // fall through
01639         case eRound_Second:
01640             m_Data.nanosec = 0;
01641             break;
01642         case eRound_Millisecond:
01643             m_Data.nanosec = m_Data.nanosec / 1000000 * 1000000;
01644             break;
01645         case eRound_Microsecond:
01646             m_Data.nanosec = m_Data.nanosec / 1000 * 1000;
01647             break;
01648         default:
01649             break;
01650     }
01651     return *this;
01652 }
01653 
01654 
01655 CTime& CTime::Clear()
01656 {
01657     m_Data.year        = 0;
01658     m_Data.month       = 0;
01659     m_Data.day         = 0;
01660     m_Data.hour        = 0;
01661     m_Data.min         = 0;
01662     m_Data.sec         = 0;
01663     m_Data.nanosec     = 0;
01664     m_Data.adjTimeDiff = 0;
01665     return *this;
01666 }
01667 
01668 
01669 bool CTime::IsValid(void) const
01670 {
01671     if ( IsEmpty() )
01672         return true;
01673 
01674     if (Year() < 1583) // first Gregorian date February 24, 1582
01675         return false;
01676     if (Month()  < 1  ||  Month()  > 12)
01677         return false;
01678     if (Month() == 2) {
01679         if (Day() < 1 ||  Day() > (IsLeap() ? 29 : 28))
01680             return false;
01681     } else {
01682         if (Day() < 1 ||  Day() > s_DaysInMonth[Month() - 1])
01683             return false;
01684     }
01685     if (Hour()   < 0  ||  Hour()   > 23)
01686         return false;
01687     if (Minute() < 0  ||  Minute() > 59)
01688         return false;
01689     // leap seconds are supported
01690     if (Second() < 0  ||  Second() > 61)
01691         return false;
01692     if (NanoSecond() < 0  ||  NanoSecond() >= kNanoSecondsPerSecond)
01693         return false;
01694 
01695     return true;
01696 }
01697 
01698 
01699 CTime CTime::GetLocalTime(void) const
01700 {
01701     if ( IsEmptyDate() ) {
01702         NCBI_THROW(CTimeException, eInvalid,
01703                    "CTime::GetLocalTime(): the date is empty");
01704     }
01705     if ( IsLocalTime() ) {
01706         return *this;
01707     }
01708     CTime t(*this);
01709     return t.ToLocalTime();
01710 }
01711 
01712 
01713 CTime CTime::GetGmtTime(void) const
01714 {
01715     if ( IsEmptyDate() ) {
01716         NCBI_THROW(CTimeException, eInvalid,
01717                    "CTime::GetGmtTime(): the date is empty");
01718     }
01719     if ( IsGmtTime() ) {
01720         return *this;
01721     }
01722     CTime t(*this);
01723     return t.ToGmtTime();
01724 }
01725 
01726 
01727 CTime& CTime::ToTime(ETimeZone tz)
01728 {
01729     if ( IsEmptyDate() ) {
01730         NCBI_THROW(CTimeException, eInvalid,
01731                    "CTime::ToTime(): the date is empty");
01732     }
01733     if (GetTimeZone() != tz) {
01734         struct tm* t;
01735         time_t timer;
01736         timer = GetTimeT();
01737         if (timer == -1)
01738             return *this;
01739 
01740         // MT-Safe protect
01741         CFastMutexGuard LOCK(s_TimeMutex);
01742 
01743 #if defined(HAVE_LOCALTIME_R)
01744         struct tm temp;
01745         if (tz == eLocal) {
01746             localtime_r(&timer, &temp);
01747         } else {
01748             gmtime_r(&timer, &temp);
01749         }
01750         t = &temp;
01751 #else
01752         t = ( tz == eLocal ) ? localtime(&timer) : gmtime(&timer);
01753         if ( !t ) {
01754             // Error was detected: incorrect timer value or system error
01755             NCBI_THROW(CTimeException, eConvert, 
01756                        "CTime::ToTime(): localtime/gmtime error, "
01757                        "possible incorrect time_t value");
01758         }
01759 #endif
01760         m_Data.year  = t->tm_year + 1900;
01761         m_Data.month = t->tm_mon + 1;
01762         m_Data.day   = t->tm_mday;
01763         m_Data.hour  = t->tm_hour;
01764         m_Data.min   = t->tm_min;
01765         m_Data.sec   = t->tm_sec;
01766         m_Data.tz    = tz;
01767     }
01768     return *this;
01769 }
01770 
01771 
01772 bool CTime::operator== (const CTime& t) const
01773 {
01774     CTime tmp(t);
01775     if ( !tmp.IsEmptyDate() ) {
01776         tmp.ToTime(GetTimeZone());
01777     }
01778     return
01779         Year()       == tmp.Year()    &&
01780         Month()      == tmp.Month()   &&
01781         Day()        == tmp.Day()     &&
01782         Hour()       == tmp.Hour()    &&
01783         Minute()     == tmp.Minute()  &&
01784         Second()     == tmp.Second()  &&
01785         NanoSecond() == tmp.NanoSecond();
01786 }
01787 
01788 
01789 bool CTime::operator> (const CTime& t) const
01790 {
01791     CTime tmp(t);
01792     if ( !tmp.IsEmptyDate() ) {
01793         tmp.ToTime(GetTimeZone());
01794     }
01795     if (Year()   > tmp.Year())
01796         return true;
01797     if (Year()   < tmp.Year())
01798         return false;
01799     if (Month()  > tmp.Month())
01800         return true;
01801     if (Month()  < tmp.Month())
01802         return false;
01803     if (Day()    > tmp.Day())
01804         return true;
01805     if (Day()    < tmp.Day())
01806         return false;
01807     if (Hour()   > tmp.Hour())
01808         return true;
01809     if (Hour()   < tmp.Hour())
01810         return false;
01811     if (Minute() > tmp.Minute())
01812         return true;
01813     if (Minute() < tmp.Minute())
01814         return false;
01815     if (Second() > tmp.Second())
01816         return true;
01817     if (Second() < tmp.Second())
01818         return false;
01819     if (NanoSecond() > tmp.NanoSecond())
01820         return true;
01821 
01822     return false;
01823 }
01824 
01825 
01826 bool CTime::operator< (const CTime& t) const
01827 {
01828     CTime tmp(t);
01829     if ( !tmp.IsEmptyDate() ) {
01830         tmp.ToTime(GetTimeZone());
01831     }
01832     if (Year()   < tmp.Year())
01833         return true;
01834     if (Year()   > tmp.Year())
01835         return false;
01836     if (Month()  < tmp.Month())
01837         return true;
01838     if (Month()  > tmp.Month())
01839         return false;
01840     if (Day()    < tmp.Day())
01841         return true;
01842     if (Day()    > tmp.Day())
01843         return false;
01844     if (Hour()   < tmp.Hour())
01845         return true;
01846     if (Hour()   > tmp.Hour())
01847         return false;
01848     if (Minute() < tmp.Minute())
01849         return true;
01850     if (Minute() > tmp.Minute())
01851         return false;
01852     if (Second() < tmp.Second())
01853         return true;
01854     if (Second() > tmp.Second())
01855         return false;
01856     if (NanoSecond() < tmp.NanoSecond())
01857         return true;
01858 
01859     return false;
01860 }
01861 
01862 
01863 bool CTime::IsLeap(void) const
01864 {
01865     int year = Year();
01866     return (year % 4 == 0  &&  year % 100 != 0)  ||  year % 400 == 0;
01867 }
01868 
01869 
01870 TSeconds CTime::TimeZoneDiff(void) const
01871 {
01872     const CTime tl(GetLocalTime());
01873     const CTime tg(GetGmtTime());
01874 
01875     TSeconds dSecs  = tl.Second() - tg.Second();
01876     int      dMins  = tl.Minute() - tg.Minute();
01877     int      dHours = tl.Hour()   - tg.Hour();
01878     int      dDays  = tl.DiffWholeDays(tg);
01879     return ((dDays * 24 + dHours) * 60 + dMins) * 60 + dSecs;
01880 }
01881 
01882 
01883 int CTime::DiffWholeDays(const CTime& t) const
01884 {
01885     return int(s_Date2Number(*this) - s_Date2Number(t));
01886 }
01887 
01888 TSeconds CTime::DiffSecond(const CTime& from) const
01889 { 
01890     const CTime* p1, *p2;
01891     CTime        t1,  t2;
01892     if (GetTimeZone() != from.GetTimeZone()) {
01893         t1 = *this;
01894         t2 =  from;
01895         t1.ToGmtTime();
01896         t2.ToGmtTime();
01897         p1 = &t1;
01898         p2 = &t2;
01899     } else {
01900         p1 =  this;
01901         p2 = &from;
01902     }
01903     TSeconds dSecs  = p1->Second() - p2->Second();
01904     int      dMins  = p1->Minute() - p2->Minute();
01905     int      dHours = p1->Hour()   - p2->Hour();
01906     int      dDays  = p1->DiffWholeDays(*p2);
01907     return ((dDays * 24 + dHours) * 60 + dMins) * 60 + dSecs;
01908 }
01909 
01910 
01911 CTimeSpan CTime::DiffTimeSpan(const CTime& t) const
01912 {
01913     TSeconds sec = DiffSecond(t);
01914     if (sec < kMin_Long  || sec > kMax_Long) {
01915         NCBI_THROW(CTimeException, eConvert, 
01916                    "CTime::DiffTimeSpan(): difference in time " +
01917                    NStr::Int8ToString(sec) + 
01918                    " is too big to convert to CTimeSpan");
01919     }
01920     return CTimeSpan((long)sec , NanoSecond() - t.NanoSecond());
01921 }
01922 
01923 
01924 void CTimeSpan::Set(double seconds)
01925 {
01926     if (seconds < kMin_Long  || seconds > kMax_Long) {
01927         NCBI_THROW(CTimeException, eConvert, 
01928                   "CTimeSpan::Set(): value " + NStr::DoubleToString(seconds) +
01929                   " is too big to convert to CTimeSpan");
01930     }
01931     m_Sec = long(seconds);
01932     m_NanoSec = long((seconds - m_Sec) * kNanoSecondsPerSecond);
01933     x_Normalize();
01934 }
01935 
01936 
01937 void CTime::x_AdjustDay()
01938 {
01939     int n_days = DaysInMonth();
01940     if (Day() > n_days) {
01941         m_Data.day = n_days;
01942     }
01943 }
01944 
01945 
01946 CTime& CTime::x_AdjustTime(const CTime& from, bool shift_time)
01947 {
01948     if ( !x_NeedAdjustTime() )
01949         return *this;
01950 
01951     switch ( GetTimeZonePrecision() ) {
01952     case eMinute:
01953         if (Minute() != from.Minute())
01954             return x_AdjustTimeImmediately(from, shift_time);
01955     case eHour:
01956         if (Hour() != from.Hour())
01957             return x_AdjustTimeImmediately(from, shift_time);
01958     case eDay:
01959         if (Day() != from.Day())
01960             return x_AdjustTimeImmediately(from, shift_time);
01961     case eMonth:
01962         if (Month() != from.Month())
01963             return x_AdjustTimeImmediately(from, shift_time);
01964     default:
01965         break;
01966     }
01967     return *this;
01968 }
01969 
01970 
01971 CTime& CTime::x_AdjustTimeImmediately(const CTime& from, bool shift_time)
01972 {
01973     // Time in hours for temporary time shift.
01974     // Shift used for obtainment correct result at changeover daytime saving.
01975     // Must be > 3 (Linux distinction). On other platforms may be == 3.
01976     const int kShift = 4;
01977 
01978     // MT-Safe protect
01979     CFastMutexGuard LOCK(s_TimeAdjustMutex);
01980 
01981     // Special conversion from <const CTime> to <CTime>
01982     CTime tmp(from);
01983     int sign = 0;
01984     TSeconds diff = 0;
01985     // Primary procedure call
01986     if ( shift_time ) {
01987         sign = ( *this > from ) ? 1 : -1;
01988         // !!! Run TimeZoneDiff() first for old time value
01989         diff = -tmp.TimeZoneDiff() + TimeZoneDiff();
01990         // Correction need's if time already in identical timezone
01991         if (!diff  ||  diff == m_Data.adjTimeDiff) {
01992             return *this;
01993         }
01994     }
01995     // Recursive procedure call. Inside below
01996     // x_AddHour(*, eAdjustDaylight, false)
01997     else  {
01998         // Correction need't if difference not found
01999         if (diff == m_Data.adjTimeDiff) {
02000             return *this;
02001         }
02002     }
02003     // Make correction with temporary time shift
02004     time_t t = GetTimeT();
02005     CTime tn(t + (time_t)diff + 3600 * kShift * sign);
02006     if (from.GetTimeZone() == eLocal) {
02007         tn.ToLocalTime();
02008     }
02009     tn.SetTimeZonePrecision(GetTimeZonePrecision());
02010 
02011     // Release adjust time mutex
02012     LOCK.Release();
02013 
02014     // Primary procedure call
02015     if ( shift_time ) {
02016         // Cancel temporary time shift
02017         tn.x_AddHour(-kShift * sign, eAdjustDaylight, false);
02018         tn.m_Data.adjTimeDiff = (Int4)diff;
02019     }
02020     *this = tn;
02021     return *this;
02022 }
02023 
02024 
02025 
02026 //=============================================================================
02027 //
02028 //  CTimeSpan
02029 //
02030 //=============================================================================
02031 
02032 
02033 CTimeSpan::CTimeSpan(long days, long hours, long minutes, long seconds,
02034                      long nanoseconds)
02035 {
02036     TSeconds sec = (((TSeconds)days*24 + hours)*60 + minutes)*60 +
02037                    seconds + nanoseconds/kNanoSecondsPerSecond;        
02038     if (sec < kMin_Long  || seconds > kMax_Long) {
02039         NCBI_THROW(CTimeException, eConvert, 
02040                    "CTimeSpan::CTimeSpan(): value (" +
02041                    NStr::Int8ToString(days)    + ", " +
02042                    NStr::Int8ToString(hours)   + ", " +
02043                    NStr::Int8ToString(minutes) + ", " +
02044                    NStr::Int8ToString(seconds) +
02045                    ", nanosec) is too big to convert to CTimeSpan");
02046     }
02047     m_Sec     = (long)sec;
02048     m_NanoSec = nanoseconds % kNanoSecondsPerSecond;
02049     x_Normalize();
02050 }
02051 
02052 
02053 CTimeSpan::CTimeSpan(const string& str, const CTimeFormat& format)
02054 {
02055     if (format.IsEmpty()) {
02056         x_Init(str, GetFormat());
02057     } else {
02058         x_Init(str, format);
02059     }
02060 }
02061 
02062 void CTimeSpan::x_Init(const string& str, const CTimeFormat& format)
02063 {
02064     Clear();
02065     if ( str.empty() ) {
02066         return;
02067     }
02068     const string& fmt = format.GetString();
02069     bool is_escaped = ((format.GetFlags() & CTimeFormat::fFormat_Simple) == 0);
02070     bool is_format_symbol = !is_escaped;
02071 
02072     const char* fff;
02073     const char* sss = str.c_str();
02074     int   sign = 1;
02075 
02076     for (fff = fmt.c_str();  *fff != '\0';  fff++) {
02077 
02078         // Skip preceding symbols for some formats
02079         if ( !is_format_symbol ) {
02080             if ( *fff == kFormatEscapeSymbol )  {
02081                 is_format_symbol = true;
02082                 continue;
02083             }
02084         }
02085         if ( is_escaped ) {
02086             is_format_symbol = false;
02087         }
02088         // Non-format symbols
02089         if (strchr(kFormatSymbolsSpan, *fff) == 0) {
02090             if (*fff == *sss) {
02091                 sss++;
02092                 continue;  // skip matching non-format symbols
02093             }
02094             break;  // error: non-matching non-format symbols
02095         }
02096 
02097         // Sign: if specified that the time span is negative
02098         if (*fff == '-') {
02099             if (*sss == '-') {
02100                 sign = -1;
02101                 sss++;
02102             }
02103             continue;
02104         }
02105         // Other format symbols -- read the next data ingredient
02106         char value_str[21];
02107         char* s = value_str;
02108         for (size_t len = 20;
02109              len  &&  *sss  &&  isdigit((unsigned char)(*sss));  len--) {
02110             *s++ = *sss++;
02111         }
02112         *s = '\0';
02113         long value = NStr::StringToLong(value_str);
02114 
02115         switch ( *fff ) {
02116         case 'd':
02117             m_Sec += value * 86400L;
02118             break;
02119         case 'h':
02120             m_Sec += value * 3600L;
02121             break;
02122         case 'H':
02123             m_Sec = value * 3600L;
02124             break;
02125         case 'm':
02126             m_Sec += value * 60L;
02127             break;
02128         case 'M':
02129             m_Sec = value * 60L;
02130             break;
02131         case 's':
02132             m_Sec += value;
02133             break;
02134         case 'S':
02135             m_Sec = value;
02136             break;
02137         case 'n':
02138             m_NanoSec = value;
02139             break;
02140         default:
02141             NCBI_THROW(CTimeException, eFormat,
02142                        "CTimeSpan::x_Init(): format '" + fmt +
02143                        "' is incorrect");
02144         }
02145     }
02146     // Normalize time span
02147     if (sign < 0) {
02148         Invert();
02149     }
02150     x_Normalize();
02151 
02152     // Check on errors
02153     if (*fff != '\0'  &&  
02154         !(format.GetFlags() & CTimeFormat::fMatch_ShortTime)) {
02155         NCBI_THROW(CTimeException, eFormat, 
02156                    "CTimeSpan::x_Init(): time string '" + str +
02157                    "' is too short for time format '" + fmt + "'");
02158     }
02159     if (*sss != '\0'  && 
02160         !(format.GetFlags() & CTimeFormat::fMatch_ShortFormat)) {
02161         NCBI_THROW(CTimeException, eFormat,
02162                    "CTimeSpan::x_Init(): time string '" + str +
02163                    "' is too long for time format '" + fmt + "'");
02164     }
02165 }
02166 
02167 
02168 void CTimeSpan::x_Normalize(void)
02169 {
02170     m_Sec += m_NanoSec / kNanoSecondsPerSecond;
02171     m_NanoSec %= kNanoSecondsPerSecond;
02172     // If signs are different then make timespan correction
02173     if (m_Sec > 0  &&  m_NanoSec < 0) {
02174         m_Sec--;
02175         m_NanoSec += kNanoSecondsPerSecond;
02176     } else if (m_Sec < 0  &&  m_NanoSec > 0) {
02177         m_Sec++;
02178         m_NanoSec -= kNanoSecondsPerSecond;
02179     }
02180 }
02181 
02182 
02183 void CTimeSpan::SetFormat(const CTimeFormat& format)
02184 {
02185     // Here we do not need to delete a previous value stored in the TLS.
02186     // The TLS will destroy it using s_TlsFormatCleanup().
02187     CTimeFormat* ptr = new CTimeFormat(format);
02188     s_TlsFormatSpan.SetValue(ptr, s_TlsFormatCleanup);
02189 }
02190 
02191 
02192 CTimeFormat CTimeSpan::GetFormat(void)
02193 {
02194     CTimeFormat format;
02195     CTimeFormat* ptr = s_TlsFormatSpan.GetValue();
02196     if ( !ptr ) {
02197         format.SetFormat(kDefaultFormatSpan);
02198     } else {
02199         format = *ptr;
02200     }
02201     return format;
02202 }
02203 
02204 
02205 string CTimeSpan::AsString(const CTimeFormat& format) const
02206 {
02207     string str;
02208     str.reserve(64); // try to save on memory allocations
02209     string fmt;
02210     CTimeFormat::TFlags fmt_flags;
02211     if ( format.IsEmpty() ) {
02212         CTimeFormat f = GetFormat();
02213         fmt       = f.GetString();
02214         fmt_flags = f.GetFlags();
02215     } else {
02216         fmt       = format.GetString();
02217         fmt_flags = format.GetFlags();
02218     }
02219     bool is_escaped = ((fmt_flags & CTimeFormat::fFormat_Simple) == 0);
02220     bool is_format_symbol = !is_escaped;
02221 
02222     ITERATE(string, it, fmt) {
02223 
02224         if ( !is_format_symbol ) {
02225             if ( *it == kFormatEscapeSymbol )  {
02226                 is_format_symbol = true;
02227             } else {
02228                 str += *it;
02229             }
02230             continue;
02231         }
02232         if ( is_escaped ) {
02233             is_format_symbol = false;
02234         }
02235         switch ( *it ) {
02236         case '-': if (GetSign() == eNegative) {
02237                       str += "-";
02238                   }
02239                   break;
02240         case 'd': s_AddInt(str, abs(GetCompleteDays()));
02241                   break;
02242         case 'h': s_AddZeroPadInt2(str, abs(x_Hour()));
02243                   break;
02244         case 'H': s_AddInt(str, abs(GetCompleteHours()));
02245                   break;
02246         case 'm': s_AddZeroPadInt2(str, abs(x_Minute()));
02247                   break;
02248         case 'M': s_AddInt(str, abs(GetCompleteMinutes()));
02249                   break;
02250         case 's': s_AddZeroPadInt2(str, abs(x_Second()));
02251                   break;
02252         case 'S': s_AddInt(str, abs(GetCompleteSeconds()));
02253                   break;
02254         case 'n': s_AddZeroPadInt(str, abs(GetNanoSecondsAfterSecond()), 9);
02255                   break;
02256         default : str += *it;
02257                   break;
02258         }
02259     }
02260     return str;
02261 }
02262 
02263 
02264 struct SSmartStringItem {
02265     SSmartStringItem(void) : value(0), str(kEmptyStr), str0(kEmptyStr) {};
02266     SSmartStringItem(long v, const string& s, const string& s0)
02267         : value(v), str(s), str0(s0) {};
02268     long    value;
02269     string  str;
02270     string  str0;
02271 };
02272 
02273 string CTimeSpan::AsSmartString(ESmartStringPrecision precision,
02274                                 ERound                rounding,
02275                                 ESmartStringZeroMode  zero_mode) const
02276 {
02277     // Make positive copy
02278     CTimeSpan diff(*this);
02279     if ( diff.GetSign() == eNegative ) {
02280         diff.Invert();
02281     }
02282 
02283     // Get nanoseconds before rounding
02284     long nanoseconds = diff.GetNanoSecondsAfterSecond();
02285 
02286     // Named or float precision level
02287     bool is_named_precision = (precision <= eSSP_Nanosecond);
02288 
02289 
02290     // Round time span
02291     if ( rounding == eRound  ) {
02292 
02293         int adjust_level;
02294 
02295         // Named precision level
02296         if ( is_named_precision ) {
02297             adjust_level = precision;
02298         } else {
02299             adjust_level = eSSP_Nanosecond;
02300             // Float precision level
02301             long  days         = diff.GetCompleteDays();
02302             int   hours        = diff.x_Hour();
02303             int   minutes      = diff.x_Minute();
02304             int   seconds      = diff.x_Second();
02305             int   adjust_shift = precision - eSSP_Nanosecond - 1;
02306 
02307             if ( days >=365 ) {
02308                 adjust_level = eSSP_Year + adjust_shift;
02309             } else if (days >= 30) {
02310                 adjust_level = eSSP_Month + adjust_shift;
02311             } else if (days > 0) {
02312                 adjust_level = eSSP_Day + adjust_shift;
02313             } else if (hours > 0) {
02314                 adjust_level = eSSP_Hour + adjust_shift;
02315             } else if (minutes > 0) {
02316                 adjust_level = eSSP_Minute + adjust_shift;
02317             } else if (seconds > 0) {
02318                 adjust_level = eSSP_Second + adjust_shift;
02319             }
02320             if (adjust_level > eSSP_Second) {
02321                 if ( nanoseconds % 1000 == 0 ) {
02322                     adjust_level = eSSP_Millisecond;
02323                 } else if ( nanoseconds % 1000000 == 0 ) {
02324                     adjust_level = eSSP_Microsecond;
02325                 }
02326             }
02327        }
02328         // Add adjustment time span
02329         switch (ESmartStringPrecision(adjust_level)) {
02330             case eSSP_Year:
02331                 diff += CTimeSpan(365/2, 0, 0, 0);
02332                 break;
02333             case eSSP_Month:
02334                 diff += CTimeSpan(15, 0, 0, 0);
02335                 break;
02336             case eSSP_Day:
02337                 diff += CTimeSpan(0, 12, 0, 0);
02338                 break;
02339             case eSSP_Hour:
02340                 diff += CTimeSpan(0, 0, 30, 0);
02341                 break;
02342             case eSSP_Minute:
02343                 diff += CTimeSpan(0, 0, 0, 30);
02344                 break;
02345             case eSSP_Second:
02346                 diff += CTimeSpan(0, 0, 0, 0, kNanoSecondsPerSecond/2);
02347                 break;
02348             case eSSP_Millisecond:
02349                 diff += CTimeSpan(0, 0, 0, 0, kNanoSecondsPerSecond/2000);
02350                 break;
02351             case eSSP_Microsecond:
02352                 diff += CTimeSpan(0, 0, 0, 0, kMicroSecondsPerSecond/2000000);
02353                 break;
02354             default:
02355                 ; // nanoseconds -- nothing to do
02356         }
02357     }
02358 
02359 
02360     // Prepare data
02361     typedef SSmartStringItem SItem;
02362     const int max_count = 7;
02363     SItem span[max_count];
02364     long days = diff.GetCompleteDays();
02365 
02366     span[0] = SItem(days/365       , "year",   "this year");    days %= 365;
02367     span[1] = SItem(days/30        , "month",  "this month");   days %= 30;
02368     span[2] = SItem(days           , "day",    "today");
02369     span[3] = SItem(diff.x_Hour()  , "hour",   "0 hours");
02370     span[4] = SItem(diff.x_Minute(), "minute", "0 minutes");
02371     span[5] = SItem(diff.x_Second(), "second", "0 seconds");
02372     switch (precision) {
02373         case eSSP_Millisecond:
02374             span[6] = SItem(nanoseconds / 1000000, 
02375                             "millisecond", "0 milliseconds");
02376             break;
02377         case eSSP_Microsecond:
02378             span[6] = SItem(nanoseconds / 1000,
02379                             "microsecond", "0 microseconds");
02380             break;
02381         case eSSP_Nanosecond:
02382             span[6] = SItem(nanoseconds, 
02383                             "nanosecond", "0 nanoseconds");
02384             break;
02385         default:
02386             ; // other not nanoseconds based precisions
02387     }
02388 
02389     // Result string
02390     string result;
02391     int current_precision = is_named_precision ? eSSP_Year  : eSSP_Precision1;
02392 
02393     // Compose result string
02394 
02395     for (int i = 0;  i < max_count  &&  current_precision <= precision;  i++) {
02396         long val = span[i].value;
02397         if ( !val ) {
02398             if ( result.empty() ) {
02399                 if (current_precision == precision  &&
02400                     current_precision != eSSP_Precision1) {
02401                     break;
02402                 }
02403                 if ( is_named_precision ) {
02404                     current_precision++;
02405                 }
02406                 continue;
02407             }
02408             if (zero_mode == eSSZ_SkipZero) {
02409                 current_precision++;
02410                 continue;
02411             } else {
02412                 long sum = 0;
02413                 int  cp = current_precision + 1;
02414                 for (int j = i + 1;
02415                      j < max_count  &&  (cp <= precision);  j++, cp++) {
02416                     sum += span[j].value;
02417                 }
02418                 if ( !sum ) {
02419                     // all trailing parts are zeros -- skip all
02420                     current_precision = precision;
02421                     break;
02422                 }
02423             }
02424         }
02425         current_precision++;
02426         if ( !result.empty() ) {
02427             result += " ";
02428         }
02429         result += NStr::LongToString(val) + " " + span[i].str;
02430         if (val > 1  ||  val == 0) {
02431             result += "s";
02432         }
02433     }
02434     if ( result.empty() ) {
02435         if ( precision > eSSP_Second ) {
02436             return span[eSSP_Second].str0;
02437         } else {
02438             return span[precision].str0;
02439         }
02440     }
02441     return result;
02442 }
02443 
02444 
02445 
02446 //=============================================================================
02447 //
02448 //  CTimeout
02449 //
02450 //=============================================================================
02451 
02452 
02453 static string s_SpecialValueName(CTimeout::EType type)
02454 { 
02455     switch(type) {
02456     case CTimeout::eDefault:
02457         return "eDefault";
02458     case CTimeout::eInfinite:
02459         return "eInfinity";
02460     default:
02461         return kEmptyStr;
02462     }
02463 }
02464 
02465 
02466 bool CTimeout::IsZero() const
02467 {
02468     if ( !IsFinite() ) {
02469         if (m_Type == eDefault) {
02470             NCBI_THROW(CTimeException, eInvalid, 
02471                        "CTimeout::IsZero():  cannot be used for "
02472                        "default timeout");
02473         }
02474         return false;
02475     }
02476     return !m_Sec  &&  !m_NanoSec;
02477 }
02478 
02479 
02480 unsigned long CTimeout::GetAsMilliSeconds(void) const
02481 { 
02482     if ( !IsFinite() ) {
02483         NCBI_THROW(CTimeException, eConvert, 
02484                    "CTimeout::GetAsMilliSeconds(): cannot convert from " +
02485                    s_SpecialValueName(m_Type) + " timeout value");
02486     }
02487 #if (SIZEOF_INT == SIZEOF_LONG)
02488     // Roughly calculate maximum number of seconds that can be safely converted
02489     // to milliseconds without overflow.
02490     if (m_Sec > (kMax_ULong/1000 - 1)) {
02491         NCBI_THROW(CTimeException, eConvert, 
02492                    "CTimeout::GetAsMilliSeconds(): timeout value " +
02493                    NStr::UIntToString(m_Sec) + 
02494                    " sec is too big to convert to 'unsigned long'");
02495     }
02496 #endif
02497     return m_Sec * kMilliSecondsPerSecond +
02498         m_NanoSec / (kNanoSecondsPerSecond/kMilliSecondsPerSecond);
02499 }
02500 
02501 
02502 double CTimeout::GetAsDouble(void) const
02503 {
02504     if ( !IsFinite() ) {
02505         NCBI_THROW(CTimeException, eConvert, 
02506                    "CTimeout::GetAsDouble(): cannot convert from " +
02507                    s_SpecialValueName(m_Type) + " timeout value");
02508     }
02509     return m_Sec + double(m_NanoSec) / kNanoSecondsPerSecond;
02510 }
02511 
02512 
02513 CTimeSpan CTimeout::GetAsTimeSpan(void) const
02514 {
02515     if ( !IsFinite() ) {
02516         NCBI_THROW(CTimeException, eConvert, 
02517                    "CTimeout::GetAsTimeSpan(): cannot convert from " +
02518                    s_SpecialValueName(m_Type) + " timeout value");
02519     }
02520 #if (SIZEOF_INT == SIZEOF_LONG)
02521     if ( m_Sec > (long)kMax_Long ) {
02522         NCBI_THROW(CTimeException, eConvert, 
02523                    "CTimeout::GetAsTimeSpan(): timeout value " +
02524                    NStr::UIntToString(m_Sec) + 
02525                    "is too big to convert to CTimeSpan");
02526         // We don't need to check microseconds here, because it always have
02527         // normalized value and can be safely converted to nanoseconds.
02528     }
02529 #endif
02530     CTimeSpan ts(m_Sec, m_NanoSec);
02531     return ts;
02532 }
02533 
02534 
02535 void CTimeout::Get(unsigned int *sec, unsigned int *microsec) const
02536 {
02537     if ( !IsFinite() ) {
02538         NCBI_THROW(CTimeException, eConvert, 
02539                    "CTimeout::Get(): cannot convert from " +
02540                    s_SpecialValueName(m_Type) + " timeout value");
02541     }
02542     if ( sec )
02543         *sec  = m_Sec;
02544     if ( microsec )
02545         *microsec = m_NanoSec / (kNanoSecondsPerSecond/kMicroSecondsPerSecond);
02546 }
02547 
02548 void CTimeout::GetNano(unsigned int *sec, unsigned int *nanosec) const
02549 {
02550     if ( !IsFinite() ) {
02551         NCBI_THROW(CTimeException, eConvert, 
02552                    "CTimeout::Get(): cannot convert from " +
02553                    s_SpecialValueName(m_Type) + " timeout value");
02554     }
02555     if ( sec )
02556         *sec  = m_Sec;
02557     if ( nanosec )
02558         *nanosec = m_NanoSec;
02559 }
02560 
02561 
02562 void CTimeout::Set(EType type)
02563 {
02564     switch(type) {
02565     case eDefault:
02566     case eInfinite:
02567         m_Type = type;
02568         break;
02569     case eZero:
02570         m_Type = eFinite;
02571         Set(0,0);
02572         break;
02573     default:
02574         NCBI_THROW(CTimeException, eArgument, 
02575             "CTimeout::Set(type): incorrect type value " +
02576             NStr::IntToString(type));
02577     }
02578 }
02579 
02580 void CTimeout::Set(unsigned int sec, unsigned int microsec)
02581 {
02582     m_Type     = eFinite;
02583     m_Sec      = sec + microsec / kMicroSecondsPerSecond;
02584     m_NanoSec  = (microsec % kMicroSecondsPerSecond) *
02585         (kNanoSecondsPerSecond/kMicroSecondsPerSecond);
02586 }
02587 
02588 void CTimeout::SetNano(unsigned int sec, unsigned int nanosec)
02589 {
02590     m_Type     = eFinite;
02591     m_Sec      = sec + nanosec / kNanoSecondsPerSecond;
02592     m_NanoSec  = nanosec % kNanoSecondsPerSecond;
02593 }
02594 
02595 void CTimeout::Set(double sec)
02596 {
02597     if (sec < 0) {
02598         NCBI_THROW(CTimeException, eConvert, 
02599                    "CTimeout::Set(double): cannot set negative value " +
02600                    NStr::DoubleToString(sec));
02601     }
02602     if (sec > kMax_UInt) {
02603         NCBI_THROW(CTimeException, eConvert, 
02604                    "CTimeout::Set(double): timeout value " +
02605                    NStr::DoubleToString(sec) + " is too big");
02606     }
02607     m_Type     = eFinite;
02608     m_Sec      = (unsigned int)sec;
02609     m_NanoSec  = (unsigned int)((sec - m_Sec) * kNanoSecondsPerSecond);
02610 }
02611 
02612 
02613 void CTimeout::Set(const CTimeSpan& ts)
02614 {
02615     if (ts.GetSign() == eNegative) {
02616         NCBI_THROW(CTimeException, eConvert, 
02617                    "CTimeout::Set(): cannot convert from negative "
02618                    "CTimeStamp '" + ts.AsString() + "'");
02619     }
02620     if ((Uint8)ts.GetCompleteSeconds() > kMax_UInt) {
02621         NCBI_THROW(CTimeException, eConvert, 
02622                    "CTimeout: CTimeStamp value '" + ts.AsString() +
02623                    "' is too big");
02624         // We don't need to check nanoseconds, because CTimeSpan always have
02625         // normalized value and its value can be safely converted
02626         // to microseconds.
02627     }
02628     m_Type     = eFinite;
02629     m_Sec      = (unsigned int)ts.GetCompleteSeconds();
02630     m_NanoSec  = (unsigned int)ts.GetNanoSecondsAfterSecond();
02631 }
02632 
02633 
02634 #define COMPARE_TIMEOUT_TYPES(t1, t2) (int(t1) << 2 | int(t2))
02635 
02636 
02637 bool CTimeout::operator== (const CTimeout& t) const
02638 {
02639     switch (COMPARE_TIMEOUT_TYPES(m_Type, t.m_Type)) {
02640     case COMPARE_TIMEOUT_TYPES(eFinite, eFinite):
02641         return m_Sec == t.m_Sec  &&  m_NanoSec == t.m_NanoSec;
02642     case COMPARE_TIMEOUT_TYPES(eInfinite, eInfinite):
02643         return true;  // infinite == infinite
02644     case COMPARE_TIMEOUT_TYPES(eFinite, eInfinite):
02645     case COMPARE_TIMEOUT_TYPES(eInfinite, eFinite):
02646         return false; // infinite != value
02647     default:
02648         NCBI_THROW(CTimeException, eArgument,
02649             "CTimeout::operator==(): unable to compare with eDefault timeout");
02650     }
02651 }
02652 
02653 
02654 bool CTimeout::operator< (const CTimeout& t) const
02655 {
02656     switch (COMPARE_TIMEOUT_TYPES(m_Type, t.m_Type)) {
02657     case COMPARE_TIMEOUT_TYPES(eFinite, eFinite):
02658         return m_Sec == t.m_Sec ? m_NanoSec < t.m_NanoSec : m_Sec < t.m_Sec;
02659     case COMPARE_TIMEOUT_TYPES(eFinite, eInfinite):
02660         return true;  // value < infinite
02661     case COMPARE_TIMEOUT_TYPES(eInfinite, eFinite):
02662     case COMPARE_TIMEOUT_TYPES(eInfinite, eInfinite):
02663         return false;
02664     default:
02665         NCBI_THROW(CTimeException, eArgument,
02666             "CTimeout::operator<(): unable to compare with eDefault timeout");
02667     }
02668 }
02669 
02670 
02671 bool CTimeout::operator> (const CTimeout& t) const
02672 {
02673     switch (COMPARE_TIMEOUT_TYPES(m_Type, t.m_Type)) {
02674     case COMPARE_TIMEOUT_TYPES(eFinite, eFinite):
02675         return m_Sec == t.m_Sec ? m_NanoSec > t.m_NanoSec : m_Sec > t.m_Sec;
02676     case COMPARE_TIMEOUT_TYPES(eInfinite, eFinite):
02677         return true;  // infinite > value
02678     case COMPARE_TIMEOUT_TYPES(eFinite, eInfinite):
02679     case COMPARE_TIMEOUT_TYPES(eInfinite, eInfinite):
02680         return false;
02681     default:
02682         NCBI_THROW(CTimeException, eArgument,
02683             "CTimeout::operator>(): unable to compare with eDefault timeout");
02684     }
02685 }
02686 
02687 
02688 bool CTimeout::operator>= (const CTimeout& t) const
02689 {
02690     switch (COMPARE_TIMEOUT_TYPES(m_Type, t.m_Type)) {
02691     case COMPARE_TIMEOUT_TYPES(eFinite, eFinite):
02692         return m_Sec == t.m_Sec ? m_NanoSec >= t.m_NanoSec : m_Sec >= t.m_Sec;
02693     case COMPARE_TIMEOUT_TYPES(eFinite, eInfinite):
02694         return false;     // value < infinity
02695     case COMPARE_TIMEOUT_TYPES(eInfinite, eFinite):
02696     case COMPARE_TIMEOUT_TYPES(eInfinite, eInfinite):
02697     case COMPARE_TIMEOUT_TYPES(eInfinite, eDefault):
02698         return true;      // infinity >= everything
02699     case COMPARE_TIMEOUT_TYPES(eDefault, eFinite):
02700         if ( t.IsZero() ) 
02701             return true;  // default >= zero
02702         // fall through
02703     default:
02704         NCBI_THROW(CTimeException, eArgument,
02705             "CTimeout::operator>=(): unable to compare with eDefault timeout");
02706     }
02707 }
02708 
02709 
02710 bool CTimeout::operator<= (const CTimeout& t) const
02711 {
02712     switch (COMPARE_TIMEOUT_TYPES(m_Type, t.m_Type)) {
02713     case COMPARE_TIMEOUT_TYPES(eFinite, eFinite):
02714         return m_Sec == t.m_Sec ? m_NanoSec <= t.m_NanoSec : m_Sec <= t.m_Sec;
02715     case COMPARE_TIMEOUT_TYPES(eInfinite, eFinite):
02716         return false;    // infinity > value
02717     case COMPARE_TIMEOUT_TYPES(eFinite, eInfinite):
02718     case COMPARE_TIMEOUT_TYPES(eInfinite, eInfinite):
02719     case COMPARE_TIMEOUT_TYPES(eDefault, eInfinite):
02720         return true;     // everything <= infinity
02721     case COMPARE_TIMEOUT_TYPES(eFinite, eDefault):
02722         if ( IsZero() ) 
02723             return true; // zero <= default
02724         // fall through
02725     default:
02726         NCBI_THROW(CTimeException, eArgument,
02727             "CTimeout::operator<=(): unable to compare with eDefault timeout");
02728     }
02729 }
02730 
02731 //=============================================================================
02732 //
02733 //  CAbsTimeout
02734 //
02735 //=============================================================================
02736 
02737 CAbsTimeout::CAbsTimeout(unsigned int sec, unsigned int nanosec)
02738     : m_Seconds(0), m_Nanoseconds(0), m_Infinite(false)
02739 {
02740     x_Now();
02741     x_Add(sec,nanosec);
02742 
02743 }
02744 
02745 CAbsTimeout::CAbsTimeout(const CTimeout& rel_timeout)
02746     : m_Seconds(0), m_Nanoseconds(0), m_Infinite(false)
02747 {
02748     if (rel_timeout.IsInfinite()) {
02749         m_Infinite = true;
02750     }
02751     else if (rel_timeout.IsFinite()) {
02752         x_Now();
02753         unsigned int sec, mksec;
02754         rel_timeout.Get(&sec, &mksec);
02755         x_Add(sec,mksec*1000);
02756     }
02757 }
02758 
02759 void CAbsTimeout::x_Now(void)
02760 {
02761 #if defined(NCBI_OS_MSWIN)
02762     struct _timeb timebuffer;
02763     _ftime(&timebuffer);
02764     m_Seconds = timebuffer.time;
02765     m_Nanoseconds = (unsigned int)timebuffer.millitm *
02766         (kNanoSecondsPerSecond / kMilliSecondsPerSecond);
02767 #else
02768 #if 0
02769     struct timespec timebuffer;
02770     clock_gettime(CLOCK_REALTIME, &timebuffer);
02771     m_Seconds = timebuffer.tv_sec;
02772     m_Nanoseconds = timebuffer.tv_nsec;
02773 #else
02774     struct timeval tp;
02775     if (gettimeofday(&tp,0) != -1) {
02776         m_Seconds = tp.tv_sec;
02777         m_Nanoseconds = tp.tv_usec *
02778             (kNanoSecondsPerSecond / kMicroSecondsPerSecond);
02779     }
02780 #endif
02781 #endif
02782 }
02783 
02784 void CAbsTimeout::x_Add(unsigned int seconds, unsigned int nanoseconds)
02785 {
02786     if (m_Infinite || (seconds == 0 && nanoseconds == 0)) {
02787         return;
02788     }
02789     unsigned int nn = m_Nanoseconds + nanoseconds;
02790     m_Seconds    += nn/kNanoSecondsPerSecond;
02791     m_Nanoseconds = nn%kNanoSecondsPerSecond;
02792     m_Seconds += seconds;
02793 }
02794 
02795 void CAbsTimeout::GetExpirationTime(time_t* sec, unsigned int* nanosec) const
02796 {
02797     if ( IsInfinite() ) {
02798         NCBI_THROW(CTimeException, eConvert, 
02799                    "CAbsTimeout::GetExpirationTime(): cannot convert from " +
02800                    s_SpecialValueName(CTimeout::eInfinite) + " timeout value");
02801     }
02802     if (sec) {
02803         *sec = m_Seconds;
02804     }
02805     if (nanosec) {
02806         *nanosec = m_Nanoseconds;
02807     }
02808 }
02809 
02810 CNanoTimeout CAbsTimeout::GetRemainingTime(void) const
02811 {
02812     if ( IsInfinite() ) {
02813         NCBI_THROW(CTimeException, eConvert, 
02814                    "CAbsTimeout::GetRemainingTime(): cannot convert from " +
02815                    s_SpecialValueName(CTimeout::eInfinite) + " timeout value");
02816     }
02817 
02818     CAbsTimeout now(0,0);
02819 
02820     time_t       thenS  = m_Seconds;
02821     unsigned int thenNS = m_Nanoseconds;
02822     time_t       nowS   = now.m_Seconds;
02823     unsigned int nowNS  = now.m_Nanoseconds;
02824 
02825     if (thenNS >= nowNS) {
02826         thenNS -= nowNS;
02827     } else {
02828         --thenS;
02829         thenNS = kNanoSecondsPerSecond - (nowNS - thenNS);
02830     }
02831     thenS -= nowS;
02832 
02833     if (thenS < 0) {
02834         thenS = 0;
02835         thenNS = 0;
02836     }
02837     return CNanoTimeout((unsigned int)thenS,thenNS);
02838 }
02839 
02840 //=============================================================================
02841 //
02842 //  CFastLocalTime
02843 //
02844 //=============================================================================
02845 
02846 CFastLocalTime::CFastLocalTime(unsigned int sec_after_hour)
02847     : m_SecAfterHour(sec_after_hour),
02848       m_LastTuneupTime(0), m_LastSysTime(0),
02849       m_Timezone(0), m_Daylight(-1), m_IsTuneup(NULL)
02850 {
02851 #if !defined(TIMEZONE_IS_UNDEFINED)
02852     // MT-Safe protect: use CTime locking mutex
02853     CFastMutexGuard LOCK(s_TimeMutex);
02854     m_Timezone = (int)TimeZone();
02855     m_Daylight = Daylight();
02856 #endif
02857     m_LocalTime.SetTimeZonePrecision(CTime::eHour);
02858     m_TunedTime.SetTimeZonePrecision(CTime::eHour);
02859 }
02860 
02861 
02862 void CFastLocalTime::Tuneup(void)
02863 {
02864     if ( m_IsTuneup ) {
02865         return;
02866     }
02867     // Get system time
02868     time_t timer;
02869     long ns;
02870     s_GetTimeT(timer, ns);
02871     x_Tuneup(timer, ns);
02872 }
02873 
02874 
02875 bool CFastLocalTime::x_Tuneup(time_t timer, long nanosec)
02876 {
02877     // Tuneup in progress
02878     if (SwapPointers(&m_IsTuneup, (void*)1))
02879         return false;
02880 
02881     // MT-Safe protect: use CTime locking mutex
02882     CFastMutexGuard LOCK(s_TimeMutex);
02883     m_TunedTime.x_SetTime(&timer);
02884     m_TunedTime.SetNanoSecond(nanosec);
02885 
02886 #if !defined(TIMEZONE_IS_UNDEFINED)
02887     m_Timezone = (int)TimeZone();
02888     m_Daylight = Daylight();
02889 #endif
02890 
02891     LOCK.Release();
02892 
02893     // Copy tuned time to cached local time
02894     CFastMutexGuard FLT_LOCK(s_FastLocalTimeMutex);
02895     m_LastTuneupTime = timer;
02896     m_LocalTime   = m_TunedTime;
02897     m_LastSysTime = m_LastTuneupTime;
02898 
02899     // Clear flag
02900     m_IsTuneup = NULL;
02901 
02902     return true;
02903 }
02904 
02905 
02906 CTime CFastLocalTime::GetLocalTime(void)
02907 {
02908     CFastMutexGuard LOCK(eEmptyGuard);
02909 
02910 retry:
02911     // Get system time
02912     time_t timer;
02913     long ns;
02914     s_GetTimeT(timer, ns);
02915 
02916     // Avoid to make time tune up in first m_SecAfterHour for each hour
02917     // Otherwise do this at each hours/timezone change.
02918     if ( !m_IsTuneup ) {
02919 #if !defined(TIMEZONE_IS_UNDEFINED)
02920         // Get current timezone
02921         TSeconds x_timezone = TimeZone();
02922         int x_daylight = Daylight();
02923         {{
02924             // MT-Safe protect: use CTime locking mutex
02925             CFastMutexGuard LOCK(s_TimeMutex);
02926             x_timezone = TimeZone();
02927             x_daylight = Daylight();
02928         }}
02929 #endif
02930         if ( !m_LastTuneupTime  ||
02931             ((timer / 3600 != m_LastTuneupTime / 3600)  &&
02932              (timer % 3600 >  (time_t)m_SecAfterHour))
02933 #if !defined(TIMEZONE_IS_UNDEFINED)
02934             ||  (x_timezone != m_Timezone  ||  x_daylight != m_Daylight)
02935 #endif
02936         ) {
02937             if (x_Tuneup(timer, ns)) {
02938                 return m_LocalTime;
02939             }
02940         }
02941     }
02942     // MT-Safe protect
02943     LOCK.Guard(s_FastLocalTimeMutex);
02944 
02945     if ( !m_LastTuneupTime ) {
02946         LOCK.Release();
02947         NCBI_SCHED_YIELD();
02948         goto retry;
02949     } else {
02950         // Adjust local time on base of system time without any system calls
02951         m_LocalTime.AddSecond(timer - m_LastSysTime, CTime::eIgnoreDaylight);
02952         m_LocalTime.SetNanoSecond(ns);
02953     }
02954     m_LastSysTime = timer;
02955 
02956     // Return computed local time
02957     return m_LocalTime;
02958 }
02959 
02960 
02961 int CFastLocalTime::GetLocalTimezone(void)
02962 {
02963 #if !defined(TIMEZONE_IS_UNDEFINED)
02964     // Get system timer
02965     time_t timer;
02966     long ns;
02967     s_GetTimeT(timer, ns);
02968 
02969     // Avoid to make time tune up in first m_SecAfterHour for each hour
02970     // Otherwise do this at each hours/timezone change.
02971     if ( !m_IsTuneup ) {
02972         // Get current timezone
02973         TSeconds x_timezone = TimeZone();
02974         int x_daylight = Daylight();
02975         {{
02976             // MT-Safe protect: use CTime locking mutex
02977             CFastMutexGuard LOCK(s_TimeMutex);
02978             x_timezone = TimeZone();
02979             x_daylight = Daylight();
02980         }}
02981         if ( !m_LastTuneupTime  ||
02982             ((timer / 3600 != m_LastTuneupTime / 3600)  &&
02983              (timer % 3600 >  (time_t)m_SecAfterHour))
02984             ||  (x_timezone != m_Timezone  ||  x_daylight != m_Daylight)
02985         ) {
02986             x_Tuneup(timer, ns);
02987         }
02988     }
02989 #endif
02990     // Return local timezone
02991     return m_Timezone;
02992 }
02993 
02994 
02995 //=============================================================================
02996 //
02997 //  CStopWatch
02998 //
02999 //=============================================================================
03000 
03001 // deprecated
03002 CStopWatch::CStopWatch(bool start)
03003 {
03004     m_Total = 0;
03005     m_Start = 0;
03006     m_State = eStop;
03007     if ( start ) {
03008         Start();
03009     }
03010 } // NCBI_FAKE_WARNING
03011 
03012 double CStopWatch::GetTimeMark()
03013 {
03014 #if defined(NCBI_OS_MSWIN)
03015     // For Win32, we use QueryPerformanceCounter()
03016 
03017     LARGE_INTEGER bigint;
03018     static double freq;
03019     static bool first = true;
03020 
03021     if ( first ) {
03022         LARGE_INTEGER nfreq;
03023         QueryPerformanceFrequency(&nfreq);
03024         freq  = double(nfreq.QuadPart);
03025         first = false;
03026     }
03027 
03028     if ( !QueryPerformanceCounter(&bigint) ) {
03029         return 0.0;
03030     }
03031     return double(bigint.QuadPart) / freq;
03032 
03033 #else
03034     // For Unixes, we use gettimeofday()
03035 
03036     struct timeval time;
03037     if ( gettimeofday (&time, 0) ) {
03038         return 0.0;
03039     }
03040     return double(time.tv_sec) + double(time.tv_usec) / 1e6;
03041 #endif
03042 }
03043 
03044 
03045 void CStopWatch::SetFormat(const CTimeFormat& format)
03046 {
03047     // Here we do not need to delete a previous value stored in the TLS.
03048     // The TLS will destroy it using s_TlsFormatCleanup().
03049     CTimeFormat* ptr = new CTimeFormat(format);
03050     s_TlsFormatStopWatch.SetValue(ptr, s_TlsFormatCleanup);
03051 }
03052 
03053 
03054 CTimeFormat CStopWatch::GetFormat(void)
03055 {
03056     CTimeFormat format;
03057     CTimeFormat* ptr = s_TlsFormatStopWatch.GetValue();
03058     if ( !ptr ) {
03059         format.SetFormat(kDefaultFormatStopWatch);
03060     } else {
03061         format = *ptr;
03062     }
03063     return format;
03064 }
03065 
03066 
03067 string CStopWatch::AsString(const CTimeFormat& format) const
03068 {
03069     CTimeSpan ts(Elapsed());
03070     if ( format.IsEmpty() ) {
03071         CTimeFormat fmt = GetFormat();
03072         return ts.AsString(fmt);
03073     }
03074     return ts.AsString(format);
03075 }
03076 
03077 
03078 //============================================================================
03079 //
03080 //  Extern
03081 //
03082 //============================================================================
03083 
03084 
03085 CTime GetFastLocalTime(void)
03086 {
03087     return s_FastLocalTime->GetLocalTime();
03088 }
03089 
03090 
03091 void TuneupFastLocalTime(void)
03092 {
03093     s_FastLocalTime->Tuneup();
03094 }
03095 
03096 const char* CTimeException::GetErrCodeString(void) const
03097 {
03098     switch (GetErrCode()) {
03099     case eConvert:   return "eConvert";
03100     case eInvalid:   return "eInvalid";
03101     case eFormat:    return "eFormat";
03102     default:         return CException::GetErrCodeString();
03103     }
03104 }
03105 
03106 END_NCBI_SCOPE
Modified on Wed May 23 13:24:37 2012 by modify_doxy.py rev. 337098