|
NCBI C++ ToolKit
|
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
1.7.5.1
Modified on Wed May 23 13:24:37 2012 by modify_doxy.py rev. 337098