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