Vault
4.1
|
00001 /* 00002 Copyright c1997-2014 Trygve Isaacson. All rights reserved. 00003 This file is part of the Code Vault version 4.1 00004 http://www.bombaydigital.com/ 00005 License: MIT. See LICENSE.md in the Vault top level directory. 00006 */ 00007 00010 #include "vinstant.h" 00011 #include "vtypes_internal.h" 00012 00013 #include "vcodepoint.h" 00014 #include "vstring.h" 00015 #include "vexception.h" 00016 00017 #undef sscanf 00018 00019 // VDuration ----------------------------------------------------------------- 00020 00021 // The static constants are returned with accessor methods because of the 00022 // unpredictable order of static initialization. So we use local statics 00023 // that are initialized on first use, and return them from these accessors. 00024 00025 // static 00026 const VDuration& VDuration::UNSPECIFIED() { 00027 static const VDuration kUNSPECIFIED(V_MAX_S64); 00028 return kUNSPECIFIED; 00029 } 00030 00031 // static 00032 const VDuration& VDuration::NEGATIVE_INFINITY() { 00033 static const VDuration kNEGATIVE_INFINITY(V_MIN_S64); 00034 return kNEGATIVE_INFINITY; 00035 } 00036 00037 // static 00038 const VDuration& VDuration::ZERO() { 00039 static const VDuration kZERO(CONST_S64(0)); 00040 return kZERO; 00041 } 00042 00043 // static 00044 const VDuration& VDuration::MILLISECOND() { 00045 static const VDuration kMILLISECOND(CONST_S64(1)); 00046 return kMILLISECOND; 00047 } 00048 00049 // static 00050 const VDuration& VDuration::SECOND() { 00051 static const VDuration kSECOND(kMillisecondsPerSecond); 00052 return kSECOND; 00053 } 00054 00055 // static 00056 const VDuration& VDuration::MINUTE() { 00057 static const VDuration kMINUTE(kMillisecondsPerMinute); 00058 return kMINUTE; 00059 } 00060 00061 // static 00062 const VDuration& VDuration::HOUR() { 00063 static const VDuration kHOUR(kMillisecondsPerHour); 00064 return kHOUR; 00065 } 00066 00067 // static 00068 const VDuration& VDuration::DAY() { 00069 static const VDuration kDAY(kMillisecondsPerDay); 00070 return kDAY; 00071 } 00072 00073 // static 00074 const VDuration& VDuration::POSITIVE_INFINITY() { 00075 static const VDuration kPOSITIVE_INFINITY(V_MAX_S64 - CONST_S64(1)); 00076 return kPOSITIVE_INFINITY; 00077 } 00078 00079 VDuration::VDuration(const VInstant& sinceWhen) : 00080 mDurationMilliseconds(VDuration(VInstant() - sinceWhen).getDurationMilliseconds()) { 00081 } 00082 00083 // static 00084 VDuration VDuration::createFromDurationString(const VString& s) { 00085 VDuration duration; 00086 duration.setDurationString(s); 00087 return duration; 00088 } 00089 00090 VDuration& VDuration::operator+=(const VDuration& forwardOffset) { 00091 // For normal values: 00092 // Adding +/- infinity results in +/- infinity. 00093 // All other operations result in no change. 00094 // For infinite values: 00095 // Adding the opposite infinity results in zero. 00096 // All other operations result in no change. 00097 if (this->isSpecific()) { 00098 if (forwardOffset.isSpecific()) { 00099 mDurationMilliseconds += forwardOffset.getDurationMilliseconds(); 00100 } else if (forwardOffset == VDuration::POSITIVE_INFINITY()) { 00101 *this = VDuration::POSITIVE_INFINITY(); 00102 } else if (forwardOffset == VDuration::NEGATIVE_INFINITY()) { 00103 *this = VDuration::NEGATIVE_INFINITY(); 00104 } 00105 } else if ((*this == VDuration::POSITIVE_INFINITY()) && (forwardOffset == VDuration::NEGATIVE_INFINITY())) { 00106 *this = VDuration::ZERO(); 00107 } else if ((*this == VDuration::NEGATIVE_INFINITY()) && (forwardOffset == VDuration::POSITIVE_INFINITY())) { 00108 *this = VDuration::ZERO(); 00109 } 00110 00111 return *this; 00112 } 00113 00114 VDuration& VDuration::operator-=(const VDuration& backwardOffset) { 00115 // For normal values: 00116 // Subtracting +/- infinity results in the opposite +/- infinity. 00117 // All other operations result in no change. 00118 // For infinite values: 00119 // Subtracting the the same infinity results in zero. 00120 // All other operations result in no change. 00121 if (this->isSpecific()) { 00122 if (backwardOffset.isSpecific()) { 00123 mDurationMilliseconds -= backwardOffset.getDurationMilliseconds(); 00124 } else if (backwardOffset == VDuration::POSITIVE_INFINITY()) { 00125 *this = VDuration::NEGATIVE_INFINITY(); 00126 } else if (backwardOffset == VDuration::NEGATIVE_INFINITY()) { 00127 *this = VDuration::POSITIVE_INFINITY(); 00128 } 00129 } else if ((*this == VDuration::POSITIVE_INFINITY()) && (backwardOffset == VDuration::POSITIVE_INFINITY())) { 00130 *this = VDuration::ZERO(); 00131 } else if ((*this == VDuration::NEGATIVE_INFINITY()) && (backwardOffset == VDuration::NEGATIVE_INFINITY())) { 00132 *this = VDuration::ZERO(); 00133 } 00134 00135 return *this; 00136 } 00137 00138 VDuration& VDuration::operator*=(Vs64 multiplier) { 00139 // Normal values use simple multiplication. 00140 // Anything multiplied by zero is zero. 00141 // +/- infinity can be flipped to the opposite +/- infinity with a 00142 // negative multiplier, and set to zero with a zero multiplier. 00143 // All other operations result in no change. 00144 if (this->isSpecific()) { 00145 mDurationMilliseconds *= multiplier; 00146 } else if (multiplier == 0) { 00147 *this = VDuration::ZERO(); 00148 } else if (multiplier < 0) { 00149 if (*this == VDuration::POSITIVE_INFINITY()) { 00150 *this = VDuration::NEGATIVE_INFINITY(); 00151 } else if (*this == VDuration::NEGATIVE_INFINITY()) { 00152 *this = VDuration::POSITIVE_INFINITY(); 00153 } 00154 } 00155 00156 return *this; 00157 } 00158 00159 VDuration& VDuration::operator/=(int divisor) { 00160 // Normal values use simple division, with divide-by-zero yielding 00161 // +/- infinity. 00162 // All other operations result in no change. 00163 if (this->isSpecific()) { 00164 if (divisor != 0) { 00165 mDurationMilliseconds /= divisor; 00166 } else { 00167 // Dividing a normal duration by zero. 00168 if (mDurationMilliseconds >= 0) { 00169 *this = VDuration::POSITIVE_INFINITY(); 00170 } else { 00171 *this = VDuration::NEGATIVE_INFINITY(); 00172 } 00173 } 00174 } 00175 00176 return *this; 00177 } 00178 00179 VDuration& VDuration::operator%=(const VDuration& divisor) { 00180 // Normal values use simple remainder division, with mod-by-zero yielding 00181 // no change. 00182 // All other operations result in no change. 00183 if (this->isSpecific()) { 00184 if (divisor.isSpecific() && (divisor != VDuration::ZERO())) { 00185 mDurationMilliseconds %= divisor.mDurationMilliseconds; 00186 } 00187 } 00188 00189 return *this; 00190 } 00191 00192 VDuration VDuration::operator-() const { 00193 // Negating a normal value is obvious. 00194 // Negating +/- infinity flips to the opposite +/- infinity. 00195 // All other operations result in the same value returned. 00196 if (this->isSpecific()) 00197 return VDuration(-mDurationMilliseconds); 00198 else if (*this == VDuration::POSITIVE_INFINITY()) 00199 return VDuration::NEGATIVE_INFINITY(); 00200 else if (*this == VDuration::NEGATIVE_INFINITY()) 00201 return VDuration::POSITIVE_INFINITY(); 00202 else 00203 return *this; 00204 } 00205 00206 VDuration operator/(const VDuration& d, int divisor) { 00207 // Re-use in-place division code. 00208 VDuration result = d; 00209 result /= divisor; 00210 return result; 00211 } 00212 00213 VDuration operator%(const VDuration& d, const VDuration& divisor) { 00214 // Re-use in-place mod code. 00215 VDuration result = d; 00216 result %= divisor; 00217 return result; 00218 } 00219 00220 VString VDuration::getDurationString() const { 00221 // most common case first! 00222 if ((mDurationMilliseconds >= 0) && (mDurationMilliseconds < kMillisecondsPerSecond)) 00223 return VSTRING_FORMAT(VSTRING_FORMATTER_S64 "ms", mDurationMilliseconds); 00224 else if (*this == VDuration::UNSPECIFIED()) 00225 return "UNSPECIFIED"; 00226 else if (*this == VDuration::POSITIVE_INFINITY()) 00227 return "INFINITY"; 00228 else if (*this == VDuration::NEGATIVE_INFINITY()) 00229 return "-INFINITY"; 00230 else if (mDurationMilliseconds % kMillisecondsPerDay == 0) 00231 return VSTRING_FORMAT(VSTRING_FORMATTER_INT "d", this->getDurationDays()); 00232 else if (mDurationMilliseconds % kMillisecondsPerHour == 0) 00233 return VSTRING_FORMAT(VSTRING_FORMATTER_INT "h", this->getDurationHours()); 00234 else if (mDurationMilliseconds % kMillisecondsPerMinute == 0) 00235 return VSTRING_FORMAT(VSTRING_FORMATTER_INT "m", this->getDurationMinutes()); 00236 else if (mDurationMilliseconds % kMillisecondsPerSecond == 0) 00237 return VSTRING_FORMAT(VSTRING_FORMATTER_INT "s", this->getDurationSeconds()); 00238 else 00239 return VSTRING_FORMAT(VSTRING_FORMATTER_S64 "ms", mDurationMilliseconds); 00240 } 00241 00242 VString VDuration::getDurationStringFractionalSeconds() const { 00243 int wholeSeconds = this->getDurationSeconds(); 00244 int thousandths = static_cast<int>(this->getDurationMilliseconds() % kMillisecondsPerSecond); 00245 00246 return VSTRING_FORMAT("%d.%03d", wholeSeconds, thousandths); 00247 } 00248 00249 void VDuration::setDurationString(const VString& s) { 00250 // Our API doc declares that we throw VRangeException on a malformed 00251 // input string. The VString parse functions are doing this for us. 00252 // Get the test order right: note that "ends with d" (days) is true 00253 // for "unspecified". 00254 VString value(s); 00255 if (s.endsWith("ms")) { 00256 value.truncateLength(value.length() - 2); 00257 mDurationMilliseconds = value.parseS64(); 00258 } else if (s.endsWith('s')) { 00259 value.truncateLength(value.length() - 1); 00260 mDurationMilliseconds = kMillisecondsPerSecond * value.parseS64(); 00261 } else if (s.endsWith('m')) { 00262 value.truncateLength(value.length() - 1); 00263 mDurationMilliseconds = kMillisecondsPerMinute * value.parseS64(); 00264 } else if (s.endsWith('h')) { 00265 value.truncateLength(value.length() - 1); 00266 mDurationMilliseconds = kMillisecondsPerHour * value.parseS64(); 00267 } else if (s.equalsIgnoreCase("UNSPECIFIED")) { 00268 *this = VDuration::UNSPECIFIED(); 00269 } else if (s.equalsIgnoreCase("INFINITY")) { 00270 *this = VDuration::POSITIVE_INFINITY(); 00271 } else if (s.equalsIgnoreCase("-INFINITY")) { 00272 *this = VDuration::NEGATIVE_INFINITY(); 00273 } else if (s.endsWith('d')) { 00274 value.truncateLength(value.length() - 1); 00275 mDurationMilliseconds = kMillisecondsPerDay * value.parseS64(); 00276 } else { 00277 VDouble seconds = value.parseDouble(); 00278 VDouble milliseconds = seconds * 1000.0; 00279 mDurationMilliseconds = (Vs64) milliseconds; 00280 } 00281 } 00282 00283 // static 00284 VDuration VDuration::_complexAdd(const VDuration& d1, const VDuration& d2) { 00285 // Re-use in-place addition code. 00286 VDuration result = d1; 00287 result += d2; 00288 return result; 00289 } 00290 00291 // static 00292 VDuration VDuration::_complexSubtract(const VDuration& d1, const VDuration& d2) { 00293 // Re-use in-place subtraction code. 00294 VDuration result = d1; 00295 result -= d2; 00296 return result; 00297 } 00298 00299 // static 00300 VDuration VDuration::_complexMultiply(const VDuration& d, Vs64 multiplier) { 00301 // Re-use in-place multiplication code. 00302 VDuration result = d; 00303 result *= multiplier; 00304 return result; 00305 } 00306 00307 // static 00308 VDuration VDuration::_complexMin(const VDuration& d1, const VDuration& d2) { 00309 if (VDuration::areValuesSpecific(d1, d2)) 00310 return (d1 < d2) ? d1 : d2; 00311 00312 // If either value is UNSPECIFIED, min is UNSPECIFIED 00313 if ((d1 == VDuration::UNSPECIFIED()) || (d2 == VDuration::UNSPECIFIED())) 00314 return VDuration::UNSPECIFIED(); 00315 00316 // If either value is -i, min is -i. 00317 if ((d1 == VDuration::NEGATIVE_INFINITY()) || (d2 == VDuration::NEGATIVE_INFINITY())) 00318 return VDuration::NEGATIVE_INFINITY(); 00319 00320 // One of them must be +i. min is the other. 00321 if (d1 == VDuration::POSITIVE_INFINITY()) 00322 return d2; 00323 else 00324 return d1; 00325 } 00326 00327 // static 00328 VDuration VDuration::_complexMax(const VDuration& d1, const VDuration& d2) { 00329 if (VDuration::areValuesSpecific(d1, d2)) 00330 return (d1 > d2) ? d1 : d2; 00331 00332 // If either value is UNSPECIFIED, min is UNSPECIFIED 00333 if ((d1 == VDuration::UNSPECIFIED()) || (d2 == VDuration::UNSPECIFIED())) 00334 return VDuration::UNSPECIFIED(); 00335 00336 // If either value is +i, max is +i. 00337 if ((d1 == VDuration::POSITIVE_INFINITY()) || (d2 == VDuration::POSITIVE_INFINITY())) 00338 return VDuration::POSITIVE_INFINITY(); 00339 00340 // One of them must be -i. max is the other. 00341 if (d1 == VDuration::NEGATIVE_INFINITY()) 00342 return d2; 00343 else 00344 return d1; 00345 } 00346 00347 // static 00348 VDuration VDuration::_complexAbs(const VDuration& d) { 00349 if (d.isSpecific()) 00350 return (d.mDurationMilliseconds < CONST_S64(0)) ? -d : d; 00351 00352 if ((d == VDuration::NEGATIVE_INFINITY()) || (d == VDuration::POSITIVE_INFINITY())) 00353 return VDuration::POSITIVE_INFINITY(); 00354 00355 return d; // presumably UNSPECIFIED 00356 } 00357 00358 // VInstantStruct ------------------------------------------------------------ 00359 00360 #ifdef VPLATFORM_WIN 00361 #define V_USE_TIMEGM_REPLACEMENT 00362 #endif 00363 #ifdef VPLATFORM_UNIX_HPUX 00364 #define V_USE_TIMEGM_REPLACEMENT 00365 #endif 00366 00367 #ifdef V_USE_TIMEGM_REPLACEMENT 00368 00369 static const time_t SECONDS_PER_MINUTE = 60; 00370 static const time_t SECONDS_PER_HOUR = 60 * SECONDS_PER_MINUTE; 00371 static const time_t SECONDS_PER_DAY = 24 * SECONDS_PER_HOUR; 00372 static const time_t SECONDS_PER_YEAR_FOR_NON_LEAP_YEAR = 365 * SECONDS_PER_DAY; 00373 static const time_t SECONDS_PER_YEAR_FOR_LEAP_YEAR = 366 * SECONDS_PER_DAY; 00374 00375 static const time_t DAYS_PER_MONTH_FOR_NON_LEAP_YEAR[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 00376 static const time_t DAYS_PER_MONTH_FOR_LEAP_YEAR[12] = {31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; 00377 00378 static const int EPOCH_YEAR = 1970; 00379 00380 static bool _isLeapYear(int year) { 00381 return ((year % 4) == 0) && 00382 (((year % 100) != 0) || ((year % 400) == 0)); 00383 } 00384 00385 // More efficient to work directly with VInstantStruct than convert back-and-forth with a 'struct tm'. 00386 static Vs64 _timegm_instantStructToMilliseconds(const VInstantStruct& when) { 00387 time_t resultSecondsFromEpoch = 0; 00388 00389 if (when.mYear >= EPOCH_YEAR) { 00390 for (int year = EPOCH_YEAR; year < when.mYear; ++year) { 00391 resultSecondsFromEpoch += (_isLeapYear(year) ? SECONDS_PER_YEAR_FOR_LEAP_YEAR : SECONDS_PER_YEAR_FOR_NON_LEAP_YEAR); 00392 } 00393 } else if (when.mYear < EPOCH_YEAR) { 00394 // Note that on Windows, most time APIs will return an error for values before 1970. However, we can handle them here easily. 00395 for (int year = EPOCH_YEAR; year >= when.mYear; --year) { 00396 resultSecondsFromEpoch -= (_isLeapYear(year) ? SECONDS_PER_YEAR_FOR_LEAP_YEAR : SECONDS_PER_YEAR_FOR_NON_LEAP_YEAR); 00397 } 00398 } 00399 00400 for (int monthIndex = 0; monthIndex < when.mMonth - 1; ++monthIndex) { // Note: mMonth range is 1..12, tm.m_mon range is 0..11. 00401 resultSecondsFromEpoch += (_isLeapYear(when.mYear) ? (DAYS_PER_MONTH_FOR_LEAP_YEAR[monthIndex] * SECONDS_PER_DAY) : (DAYS_PER_MONTH_FOR_NON_LEAP_YEAR[monthIndex] * SECONDS_PER_DAY)); 00402 } 00403 00404 resultSecondsFromEpoch += ((when.mDay - 1) * SECONDS_PER_DAY); 00405 resultSecondsFromEpoch += (when.mHour * SECONDS_PER_HOUR); 00406 resultSecondsFromEpoch += (when.mMinute * SECONDS_PER_MINUTE); 00407 resultSecondsFromEpoch += when.mSecond; 00408 00409 Vs64 result = (CONST_S64(1000) * static_cast<Vs64>(resultSecondsFromEpoch)) + static_cast<Vs64>(when.mMillisecond); 00410 return result; 00411 } 00412 00413 #endif /* V_USE_TIMEGM_REPLACEMENT */ 00414 00415 VInstantStruct::VInstantStruct(const VDate& date, const VTimeOfDay& timeOfDay) : 00416 mYear(date.getYear()), 00417 mMonth(date.getMonth()), 00418 mDay(date.getDay()), 00419 mHour(timeOfDay.getHour()), 00420 mMinute(timeOfDay.getMinute()), 00421 mSecond(timeOfDay.getSecond()), 00422 mMillisecond(timeOfDay.getMillisecond()), 00423 mDayOfWeek(0) { 00424 } 00425 00426 #define INSTANT_STRUCT_FORMAT "%d-%02d-%02d %02d:%02d:%02d.%03d" 00427 00428 Vs64 VInstantStruct::getOffsetFromUTCStruct() const { 00429 00430 #ifdef V_USE_TIMEGM_REPLACEMENT 00431 return _timegm_instantStructToMilliseconds(*this); 00432 #else 00433 struct tm fields; 00434 00435 this->getTmStruct(fields); 00436 00437 Vs64 timegmSeconds = static_cast<Vs64>(::timegm(&fields)); 00438 00439 if (timegmSeconds == CONST_S64(-1)) { 00440 throw VStackTraceException(VSTRING_FORMAT("VInstantStruct::getOffsetFromUTCStruct: time value '" INSTANT_STRUCT_FORMAT "' is out of range.", 00441 mYear, mMonth, mDay, mHour, mMinute, mSecond, mMillisecond)); 00442 } 00443 00444 Vs64 resultOffsetMilliseconds = CONST_S64(1000) * timegmSeconds; 00445 00446 resultOffsetMilliseconds += (Vs64) mMillisecond; 00447 00448 return resultOffsetMilliseconds; 00449 #endif /* V_USE_TIMEGM_REPLACEMENT */ 00450 } 00451 00452 Vs64 VInstantStruct::getOffsetFromLocalStruct() const { 00453 return VInstantStruct::_platform_offsetFromLocalStruct(*this); 00454 } 00455 00456 void VInstantStruct::setUTCStructFromOffset(Vs64 offset) { 00457 VInstantStruct::_platform_offsetToUTCStruct(offset, *this); 00458 } 00459 00460 void VInstantStruct::setLocalStructFromOffset(Vs64 offset) { 00461 VInstantStruct::_platform_offsetToLocalStruct(offset, *this); 00462 } 00463 00464 void VInstantStruct::getTmStruct(struct tm& fields) const { 00465 ::memset(&fields, 0, sizeof(fields)); 00466 00467 fields.tm_year = mYear - 1900; // tm_year field is years since 1900 00468 fields.tm_mon = mMonth - 1; // tm_mon field is 0..11 00469 fields.tm_mday = mDay; 00470 fields.tm_hour = mHour; 00471 fields.tm_min = mMinute; 00472 fields.tm_sec = mSecond; 00473 fields.tm_isdst = -1; 00474 } 00475 00476 void VInstantStruct::setFromTmStruct(const struct tm& fields, int millisecond) { 00477 mYear = fields.tm_year + 1900; // tm_year field is years since 1900 00478 mMonth = fields.tm_mon + 1; // tm_mon field is 0..11 00479 mDay = fields.tm_mday; 00480 mHour = fields.tm_hour; 00481 mMinute = fields.tm_min; 00482 mSecond = fields.tm_sec; 00483 mMillisecond = millisecond; 00484 mDayOfWeek = fields.tm_wday; 00485 } 00486 00487 // static 00488 void VInstantStruct::_threadsafe_localtime(const time_t epochOffset, struct tm* resultStorage) { 00489 time_t offset = epochOffset; 00490 struct tm* result; 00491 00492 #ifdef V_HAVE_REENTRANT_TIME 00493 result = ::localtime_r(&offset, resultStorage); 00494 #else 00495 result = ::localtime(&offset); 00496 #endif 00497 00498 if (result == NULL) 00499 throw VStackTraceException(VSTRING_FORMAT("VInstant::threadsafe_localtime: input time value " VSTRING_FORMATTER_INT " is out of range.", (int) offset)); 00500 00501 // Only copy result if we're NOT using reentrant version that already wrote result. 00502 #ifndef V_HAVE_REENTRANT_TIME 00503 *resultStorage = *result; 00504 #endif 00505 } 00506 00507 // static 00508 void VInstantStruct::_threadsafe_gmtime(const time_t epochOffset, struct tm* resultStorage) { 00509 struct tm* result; 00510 00511 #ifdef V_HAVE_REENTRANT_TIME 00512 result = ::gmtime_r(&epochOffset, resultStorage); 00513 #else 00514 result = ::gmtime(&epochOffset); 00515 #endif 00516 00517 if (result == NULL) 00518 throw VStackTraceException(VSTRING_FORMAT("VInstant::threadsafe_gmtime: input time value " VSTRING_FORMATTER_INT " is out of range.", (int) epochOffset)); 00519 00520 // Only copy result if we're NOT using reentrant version that already wrote result. 00521 #ifndef V_HAVE_REENTRANT_TIME 00522 *resultStorage = *result; 00523 #endif 00524 } 00525 00526 // VInstant ------------------------------------------------------------------ 00527 00528 static const Vs64 kInfinitePastInternalValue = V_MIN_S64; 00529 static const Vs64 kInfiniteFutureInternalValue = V_MAX_S64 - CONST_S64(1); 00530 static const Vs64 kNeverOccurredInternalValue = V_MAX_S64; 00531 00532 // The static constants are returned with accessor methods because of the 00533 // unpredictable order of static initialization. So we use local statics 00534 // that are initialized on first use, and return them from these accessors. 00535 00536 // static 00537 const VInstant& VInstant::INFINITE_PAST() { 00538 static const VInstant kINFINITE_PAST(kInfinitePastInternalValue); 00539 return kINFINITE_PAST; 00540 } 00541 00542 const VInstant& VInstant::INFINITE_FUTURE() { 00543 static const VInstant kINFINITE_FUTURE(kInfiniteFutureInternalValue); 00544 return kINFINITE_FUTURE; 00545 } 00546 00547 const VInstant& VInstant::NEVER_OCCURRED() { 00548 static const VInstant kNEVER_OCCURRED(kNeverOccurredInternalValue); 00549 return kNEVER_OCCURRED; 00550 } 00551 00552 const VString& VInstant::UTC_TIME_ZONE_ID() { 00553 static const VString kUTC_TIME_ZONE_ID("UTC"); 00554 return kUTC_TIME_ZONE_ID; 00555 } 00556 00557 const VString& VInstant::LOCAL_TIME_ZONE_ID() { 00558 static const VString kLOCAL_TIME_ZONE_ID; // empty string means local time zone 00559 return kLOCAL_TIME_ZONE_ID; 00560 } 00561 00562 Vs64 VInstant::gSimulatedClockOffset(0); 00563 Vs64 VInstant::gFrozenClockValue(0); 00564 00565 IVRemoteTimeZoneConverter* VInstant::gRemoteTimeZoneConverter = NULL; 00566 00567 // static 00568 void VInstant::setRemoteTimeZoneConverter(IVRemoteTimeZoneConverter* converter) { 00569 gRemoteTimeZoneConverter = converter; 00570 } 00571 00572 // static 00573 IVRemoteTimeZoneConverter* VInstant::getRemoteTimeZoneConverter() { 00574 return gRemoteTimeZoneConverter; 00575 } 00576 00577 VInstant::VInstant() 00578 : mValue(0) 00579 { 00580 VInstant::setNow(); 00581 } 00582 00583 VInstant& VInstant::operator=(const VInstant& i) { 00584 if (this != &i) { 00585 mValue = i.getValue(); 00586 } 00587 00588 return *this; 00589 } 00590 00591 VInstant& VInstant::operator+=(const VDuration& forwardOffsetDuration) { 00592 if (this->isSpecific() && forwardOffsetDuration.isSpecific()) 00593 mValue += forwardOffsetDuration.getDurationMilliseconds(); 00594 else if (this->isSpecific() && (forwardOffsetDuration == VDuration::NEGATIVE_INFINITY())) 00595 *this = VInstant::INFINITE_PAST(); 00596 else if (this->isSpecific() && (forwardOffsetDuration == VDuration::POSITIVE_INFINITY())) 00597 *this = VInstant::INFINITE_FUTURE(); 00598 00599 return *this; 00600 } 00601 00602 VInstant& VInstant::operator-=(const VDuration& backwardOffsetDuration) { 00603 if (this->isSpecific() && backwardOffsetDuration.isSpecific()) 00604 mValue -= backwardOffsetDuration.getDurationMilliseconds(); 00605 else if (this->isSpecific() && (backwardOffsetDuration == VDuration::NEGATIVE_INFINITY())) 00606 *this = VInstant::INFINITE_FUTURE(); 00607 else if (this->isSpecific() && (backwardOffsetDuration == VDuration::POSITIVE_INFINITY())) 00608 *this = VInstant::INFINITE_PAST(); 00609 00610 return *this; 00611 } 00612 00613 void VInstant::setNow() { 00614 if (gFrozenClockValue == 0) { 00615 mValue = VInstant::_platform_now(); 00616 mValue += gSimulatedClockOffset; 00617 } else { 00618 mValue = gFrozenClockValue; 00619 } 00620 } 00621 00622 void VInstant::setTrueNow() { 00623 mValue = VInstant::_platform_now(); 00624 } 00625 00626 VInstantStruct VInstant::getUTCInstantFields() const { 00627 VInstantStruct when; 00628 when.setUTCStructFromOffset(mValue); 00629 return when; 00630 } 00631 00632 VInstantStruct VInstant::getLocalInstantFields() const { 00633 VInstantStruct when; 00634 when.setLocalStructFromOffset(mValue); 00635 return when; 00636 } 00637 00638 // These are the special string representations we use to signify the three non-specific time values, plus one for setting to the current time. 00639 static VString VINSTANT_STRING_NOW("NOW"); 00640 static VString VINSTANT_STRING_PAST("PAST"); 00641 static VString VINSTANT_STRING_FUTURE("FUTURE"); 00642 static VString VINSTANT_STRING_NEVER("NEVER"); 00643 00644 static VInstantFormatter VINSTANT_FORMATTER_FILENAMESAFE("yMMddHHmmssSSS"); 00645 static VInstantFormatter VINSTANT_FORMATTER_FILENAMESAFE_WITHOUT_MILLISECONDS("yMMddHHmmss"); 00646 static VInstantFormatter VINSTANT_FORMATTER_UTC_DEFAULT("y-MM-dd HH:mm:ss.SSS 'UTC'"); 00647 static VInstantFormatter VINSTANT_FORMATTER_UTC_DEFAULT_WITHOUT_MILLISECONDS("y-MM-dd HH:mm:ss 'UTC'"); 00648 static VInstantFormatter VINSTANT_FORMATTER_LOCAL_DEFAULT("y-MM-dd HH:mm:ss.SSS"); 00649 static VInstantFormatter VINSTANT_FORMATTER_LOCAL_DEFAULT_WITHOUT_MILLISECONDS("y-MM-dd HH:mm:ss"); 00650 00651 // Readability values for passing to _getLegacyFormatter(isUTC, . . . ) 00652 #define LEGACY_FORMATTER_IS_UTC true 00653 #define LEGACY_FORMATTER_IS_LOCAL false 00654 00655 static const VInstantFormatter& _getLegacyFormatter(bool isUTC, bool fileNameSafe, bool wantMilliseconds) { 00656 if (fileNameSafe) { 00657 return (wantMilliseconds ? VINSTANT_FORMATTER_FILENAMESAFE : VINSTANT_FORMATTER_FILENAMESAFE_WITHOUT_MILLISECONDS); 00658 } else if (isUTC) { 00659 return (wantMilliseconds ? VINSTANT_FORMATTER_UTC_DEFAULT : VINSTANT_FORMATTER_UTC_DEFAULT_WITHOUT_MILLISECONDS); 00660 } else { 00661 return (wantMilliseconds ? VINSTANT_FORMATTER_LOCAL_DEFAULT : VINSTANT_FORMATTER_LOCAL_DEFAULT_WITHOUT_MILLISECONDS); 00662 } 00663 } 00664 00665 VString VInstant::getUTCString() const { 00666 return this->getUTCString(VINSTANT_FORMATTER_UTC_DEFAULT); 00667 } 00668 00669 VString VInstant::getUTCString(const VInstantFormatter& formatter) const { 00670 if (this->isSpecific()) { 00671 return formatter.formatUTCString(*this); 00672 } else if (*this == VInstant::INFINITE_PAST()) { 00673 return VINSTANT_STRING_PAST; 00674 } else if (*this == VInstant::INFINITE_FUTURE()) { 00675 return VINSTANT_STRING_FUTURE; 00676 } else { // == NEVER_OCCURRED 00677 return VINSTANT_STRING_NEVER; 00678 } 00679 } 00680 00681 void VInstant::getUTCString(VString& s, bool fileNameSafe, bool wantMilliseconds) const { 00682 s = this->getUTCString(_getLegacyFormatter(LEGACY_FORMATTER_IS_UTC, fileNameSafe, wantMilliseconds)); 00683 } 00684 00685 VString VInstant::getLocalString() const { 00686 return this->getLocalString(VINSTANT_FORMATTER_LOCAL_DEFAULT); 00687 } 00688 00689 VString VInstant::getLocalString(const VInstantFormatter& formatter) const { 00690 if (this->isSpecific()) { 00691 return formatter.formatLocalString(*this); 00692 } else if (*this == VInstant::INFINITE_PAST()) { 00693 return VINSTANT_STRING_PAST; 00694 } else if (*this == VInstant::INFINITE_FUTURE()) { 00695 return VINSTANT_STRING_FUTURE; 00696 } else { // == NEVER_OCCURRED 00697 return VINSTANT_STRING_NEVER; 00698 } 00699 } 00700 00701 void VInstant::getLocalString(VString& s, bool fileNameSafe, bool wantMilliseconds) const { 00702 s = this->getLocalString(_getLegacyFormatter(LEGACY_FORMATTER_IS_LOCAL, fileNameSafe, wantMilliseconds)); 00703 } 00704 00705 #define SSCANF_FORMAT_UTC_WITH_MILLISECONDS "%d-%02d-%02d %02d:%02d:%02d.%03d UTC" 00706 00707 void VInstant::setUTCString(const VString& s) { 00708 if (s.isEmpty() || (s == VINSTANT_STRING_NOW)) { 00709 this->setNow(); 00710 } else if (s == VINSTANT_STRING_PAST) { 00711 mValue = kInfinitePastInternalValue; 00712 } else if (s == VINSTANT_STRING_FUTURE) { 00713 mValue = kInfiniteFutureInternalValue; 00714 } else if (s == VINSTANT_STRING_NEVER) { 00715 mValue = kNeverOccurredInternalValue; 00716 } else { 00717 VInstantStruct when; 00718 when.mDayOfWeek = 0; 00719 00720 (void) ::sscanf(s, SSCANF_FORMAT_UTC_WITH_MILLISECONDS, &when.mYear, &when.mMonth, &when.mDay, &when.mHour, &when.mMinute, &when.mSecond, &when.mMillisecond); 00721 00722 mValue = when.getOffsetFromUTCStruct(); 00723 } 00724 } 00725 00726 #define SSCANF_FORMAT_LOCAL_WITH_MILLISECONDS "%d-%02d-%02d %02d:%02d:%02d.%03d" 00727 00728 void VInstant::setLocalString(const VString& s) { 00729 if (s.isEmpty() || (s == VINSTANT_STRING_NOW)) { 00730 this->setNow(); 00731 } else if (s == VINSTANT_STRING_PAST) { 00732 mValue = kInfinitePastInternalValue; 00733 } else if (s == VINSTANT_STRING_FUTURE) { 00734 mValue = kInfiniteFutureInternalValue; 00735 } else if (s == VINSTANT_STRING_NEVER) { 00736 mValue = kNeverOccurredInternalValue; 00737 } else { 00738 VInstantStruct when; 00739 when.mDayOfWeek = 0; 00740 00741 (void) ::sscanf(s, SSCANF_FORMAT_LOCAL_WITH_MILLISECONDS, &when.mYear, &when.mMonth, &when.mDay, &when.mHour, &when.mMinute, &when.mSecond, &when.mMillisecond); 00742 00743 mValue = when.getOffsetFromLocalStruct(); 00744 } 00745 } 00746 00747 void VInstant::getValues(VDate& date, VTimeOfDay& timeOfDay, const VString& timeZoneID) const { 00748 VInstantStruct when; 00749 00750 if (timeZoneID == VInstant::LOCAL_TIME_ZONE_ID()) 00751 when.setLocalStructFromOffset(mValue); 00752 else if (timeZoneID == VInstant::UTC_TIME_ZONE_ID()) 00753 when.setUTCStructFromOffset(mValue); 00754 else if (gRemoteTimeZoneConverter == NULL) 00755 throw VStackTraceException(VSTRING_FORMAT("Request for remote time zone conversion (%s) without a converter.", timeZoneID.chars())); 00756 else 00757 gRemoteTimeZoneConverter->offsetToRTZStruct(mValue, timeZoneID, when); 00758 00759 date.set(when.mYear, when.mMonth, when.mDay); 00760 timeOfDay.set(when.mHour, when.mMinute, when.mSecond, when.mMillisecond); 00761 } 00762 00763 VDate VInstant::getLocalDate() const { 00764 return this->getDate(VInstant::LOCAL_TIME_ZONE_ID()); 00765 } 00766 00767 VDate VInstant::getDate(const VString& timeZoneID) const { 00768 VInstantStruct when; 00769 00770 if (timeZoneID == VInstant::LOCAL_TIME_ZONE_ID()) 00771 when.setLocalStructFromOffset(mValue); 00772 else if (timeZoneID == VInstant::UTC_TIME_ZONE_ID()) 00773 when.setUTCStructFromOffset(mValue); 00774 else if (gRemoteTimeZoneConverter == NULL) 00775 throw VStackTraceException(VSTRING_FORMAT("Request for remote time zone conversion (%s) without a converter.", timeZoneID.chars())); 00776 else 00777 gRemoteTimeZoneConverter->offsetToRTZStruct(mValue, timeZoneID, when); 00778 00779 return VDate(when.mYear, when.mMonth, when.mDay); 00780 } 00781 00782 VTimeOfDay VInstant::getLocalTimeOfDay() const { 00783 return this->getTimeOfDay(VInstant::LOCAL_TIME_ZONE_ID()); 00784 } 00785 00786 VTimeOfDay VInstant::getTimeOfDay(const VString& timeZoneID) const { 00787 VInstantStruct when; 00788 00789 if (timeZoneID == VInstant::LOCAL_TIME_ZONE_ID()) 00790 when.setLocalStructFromOffset(mValue); 00791 else if (timeZoneID == VInstant::UTC_TIME_ZONE_ID()) 00792 when.setUTCStructFromOffset(mValue); 00793 else if (gRemoteTimeZoneConverter == NULL) 00794 throw VStackTraceException(VSTRING_FORMAT("Request for remote time zone conversion (%s) without a converter.", timeZoneID.chars())); 00795 else 00796 gRemoteTimeZoneConverter->offsetToRTZStruct(mValue, timeZoneID, when); 00797 00798 return VTimeOfDay(when.mHour, when.mMinute, when.mSecond, when.mMillisecond); 00799 } 00800 00801 VDateAndTime VInstant::getLocalDateAndTime() const { 00802 return this->getDateAndTime(VInstant::LOCAL_TIME_ZONE_ID()); 00803 } 00804 00805 VDateAndTime VInstant::getDateAndTime(const VString& timeZoneID) const { 00806 VInstantStruct when; 00807 00808 if (*this == VInstant::INFINITE_PAST() || 00809 *this == VInstant::INFINITE_FUTURE() || 00810 *this == VInstant::NEVER_OCCURRED()) 00811 throw VStackTraceException(VSTRING_FORMAT("Request for specific time values with non-specific time '%s'.", this->getLocalString().chars())); 00812 else if (timeZoneID == VInstant::LOCAL_TIME_ZONE_ID()) 00813 when.setLocalStructFromOffset(mValue); 00814 else if (timeZoneID == VInstant::UTC_TIME_ZONE_ID()) 00815 when.setUTCStructFromOffset(mValue); 00816 else if (gRemoteTimeZoneConverter == NULL) 00817 throw VStackTraceException(VSTRING_FORMAT("Request for remote time zone conversion (%s) without a converter.", timeZoneID.chars())); 00818 else 00819 gRemoteTimeZoneConverter->offsetToRTZStruct(mValue, timeZoneID, when); 00820 00821 return VDateAndTime(when.mYear, when.mMonth, when.mDay, when.mHour, when.mMinute, when.mSecond, when.mMillisecond); 00822 } 00823 00824 void VInstant::setLocalDateAndTime(const VDateAndTime& dt) { 00825 this->setDateAndTime(dt, VInstant::LOCAL_TIME_ZONE_ID()); 00826 } 00827 00828 void VInstant::setDateAndTime(const VDateAndTime& dt, const VString& timeZoneID) { 00829 VInstantStruct when(dt.getDate(), dt.getTimeOfDay()); 00830 00831 if (timeZoneID == VInstant::LOCAL_TIME_ZONE_ID()) 00832 mValue = when.getOffsetFromLocalStruct(); 00833 else if (timeZoneID == VInstant::UTC_TIME_ZONE_ID()) 00834 mValue = when.getOffsetFromUTCStruct(); 00835 else if (gRemoteTimeZoneConverter == NULL) 00836 throw VStackTraceException(VSTRING_FORMAT("Request for remote time zone conversion (%s) without a converter.", timeZoneID.chars())); 00837 else 00838 mValue = gRemoteTimeZoneConverter->offsetFromRTZStruct(timeZoneID, when); 00839 } 00840 00841 void VInstant::setValues(const VDate& date, const VTimeOfDay& timeOfDay, const VString& timeZoneID) { 00842 VInstantStruct when(date, timeOfDay); 00843 00844 if (timeZoneID == VInstant::LOCAL_TIME_ZONE_ID()) 00845 mValue = when.getOffsetFromLocalStruct(); 00846 else if (timeZoneID == VInstant::UTC_TIME_ZONE_ID()) 00847 mValue = when.getOffsetFromUTCStruct(); 00848 else if (gRemoteTimeZoneConverter == NULL) 00849 throw VStackTraceException(VSTRING_FORMAT("Request for remote time zone conversion (%s) without a converter.", timeZoneID.chars())); 00850 else 00851 mValue = gRemoteTimeZoneConverter->offsetFromRTZStruct(timeZoneID, when); 00852 } 00853 00854 static const int kSecondsPerDay = 86400; 00855 00856 Vs64 VInstant::getLocalOffsetMilliseconds() const { 00857 /* 00858 Return the offset, in milliseconds, of the local time zone, at this 00859 indicated instant. 00860 */ 00861 00862 VInstantStruct localStruct; 00863 localStruct.setLocalStructFromOffset(mValue); 00864 00865 VInstantStruct utcStruct; 00866 utcStruct.setUTCStructFromOffset(mValue); 00867 00868 // Now we have two structs for the instant. 00869 // A little math will tell us the difference in h/m/s. 00870 // We know the delta cannot be more than 24 hours. 00871 // No need to worry about milliseconds; time zone offset resolutions are only in minutes. 00872 // (We could presumably even ignore the seconds for that matter.) 00873 int deltaSeconds; 00874 int localSecondsOfDay = (3600 * localStruct.mHour) + (60 * localStruct.mMinute) + localStruct.mSecond; 00875 int utcSecondsOfDay = (3600 * utcStruct.mHour) + (60 * utcStruct.mMinute) + utcStruct.mSecond; 00876 00877 if (localStruct.mDay == utcStruct.mDay) { 00878 // Same date. Just a difference in hours/minutes. 00879 00880 deltaSeconds = localSecondsOfDay - utcSecondsOfDay; 00881 } else if ((localStruct.mDay == utcStruct.mDay + 1) || 00882 ((localStruct.mDay == 1) && (utcStruct.mDay > 27))) { // detect wrap-around day 1 and day 28-31 00883 // We are ahead of GMT and we are already on the "next" calendar date. 00884 // localSecondsOfDay is therefore smaller, so add a day's worth of seconds it to compensate. 00885 00886 deltaSeconds = (localSecondsOfDay + kSecondsPerDay) - utcSecondsOfDay; 00887 } else { 00888 // We are behind GMT and are still on the "previous" calendar date. 00889 // utcSecondsOfDay is therefore smaller, so add a day's worth of seconds it to compensate. 00890 00891 deltaSeconds = localSecondsOfDay - (utcSecondsOfDay + kSecondsPerDay); 00892 } 00893 00894 /* 00895 Debugging notes: 00896 For a value in Pacific Standard Time (winter time), we should return -8 hrs, which is -28800 * 1000 = -28,800,000ms 00897 For a value in Pacific Daylight Time (summer time), we should return -7 hrs, which is -25200 * 1000 = -25,200,000ms 00898 For a value in India Standard Time, we should return +5 hrs 30 minutes, which is +19800 * 1000 = +19,800,000ms 00899 */ 00900 00901 return (Vs64)(deltaSeconds * 1000); 00902 } 00903 00904 // static 00905 bool VInstant::_complexGT(const VInstant& i1, const VInstant& i2) { 00906 // We also handle the simple case correctly. 00907 if (i1.isSpecific() && i2.isSpecific()) 00908 return i1 > i2; 00909 00910 // If they're the same internal value, they're equal. That's NOT i1>i2. 00911 if (i1.mValue == i2.mValue) 00912 return false; 00913 00914 // Not the same internal value. If i2 is past, then i1>i2. 00915 if ((i2 == VInstant::INFINITE_PAST()) && i1.isComparable()) 00916 return true; 00917 00918 // Not the same internal value. If i1 is future, then i1>i2. 00919 if ((i1 == VInstant::INFINITE_FUTURE()) && i2.isComparable()) 00920 return true; 00921 00922 // None the above. Condition does not hold. 00923 return false; 00924 } 00925 00926 // static 00927 bool VInstant::_complexGTE(const VInstant& i1, const VInstant& i2) { 00928 // We also handle the simple case correctly. 00929 if (i1.isSpecific() && i2.isSpecific()) 00930 return i1 >= i2; 00931 00932 // If they're the same internal value, they're equal. So i1>=i2. 00933 if (i1.mValue == i2.mValue) 00934 return true; 00935 00936 // Not the same internal value. If i2 is past, then i1>i2. 00937 if ((i2 == VInstant::INFINITE_PAST()) && i1.isComparable()) 00938 return true; 00939 00940 // Not the same internal value. If i1 is future, then i1>i2. 00941 if ((i1 == VInstant::INFINITE_FUTURE()) && i2.isComparable()) 00942 return true; 00943 00944 // None the above. Condition does not hold. 00945 return false; 00946 } 00947 00948 // static 00949 bool VInstant::_complexLT(const VInstant& i1, const VInstant& i2) { 00950 // We also handle the simple case correctly. 00951 if (i1.isSpecific() && i2.isSpecific()) 00952 return i1 < i2; 00953 00954 // If they're the same internal value, they're equal. That's NOT i1<i2. 00955 if (i1.mValue == i2.mValue) 00956 return false; 00957 00958 // Not the same internal value. If i1 is past, then i1<i2. 00959 if ((i1 == VInstant::INFINITE_PAST()) && i2.isComparable()) 00960 return true; 00961 00962 // Not the same internal value. If i2 is future, then i1<i2. 00963 if ((i2 == VInstant::INFINITE_FUTURE()) && i1.isComparable()) 00964 return true; 00965 00966 // None the above. Condition does not hold. 00967 return false; 00968 } 00969 00970 // static 00971 bool VInstant::_complexLTE(const VInstant& i1, const VInstant& i2) { 00972 // We also handle the simple case correctly. 00973 if (i1.isSpecific() && i2.isSpecific()) 00974 return i1 <= i2; 00975 00976 // If they're the same internal value, they're equal. So i1<=i2. 00977 if (i1.mValue == i2.mValue) 00978 return true; 00979 00980 // Not the same internal value. If i1 is past, then i1<i2. 00981 if ((i1 == VInstant::INFINITE_PAST()) && i2.isComparable()) 00982 return true; 00983 00984 // Not the same internal value. If i2 is future, then i1<i2. 00985 if ((i2 == VInstant::INFINITE_FUTURE()) && i1.isComparable()) 00986 return true; 00987 00988 // None the above. Condition does not hold. 00989 return false; 00990 } 00991 00992 // static 00993 Vs64 VInstant::snapshot() { 00994 if (gFrozenClockValue == 0) 00995 return VInstant::_platform_snapshot() + gSimulatedClockOffset; 00996 else 00997 return gFrozenClockValue; 00998 } 00999 01000 // static 01001 VDuration VInstant::snapshotDelta(Vs64 snapshotValue) { 01002 return VDuration::MILLISECOND() * (VInstant::snapshot() - snapshotValue); 01003 } 01004 01005 // static 01006 void VInstant::incrementSimulatedClockOffset(const VDuration& delta) { 01007 gSimulatedClockOffset += delta.getDurationMilliseconds(); 01008 } 01009 01010 // static 01011 void VInstant::setSimulatedClockOffset(const VDuration& offset) { 01012 gSimulatedClockOffset = offset.getDurationMilliseconds(); 01013 } 01014 01015 // static 01016 void VInstant::setSimulatedClockValue(const VInstant& simulatedCurrentTime) { 01017 gSimulatedClockOffset = 0; // so that "now" will be true current time, not existing simulated time 01018 VInstant now; 01019 VInstant::setSimulatedClockOffset(simulatedCurrentTime - now); 01020 } 01021 01022 // static 01023 VDuration VInstant::getSimulatedClockOffset() { 01024 return VDuration::MILLISECOND() * gSimulatedClockOffset; 01025 } 01026 01027 // static 01028 void VInstant::freezeTime(const VInstant& frozenTimeValue) { 01029 gFrozenClockValue = frozenTimeValue.getValue(); 01030 } 01031 01032 // static 01033 void VInstant::shiftFrozenTime(const VDuration& delta) { 01034 gFrozenClockValue += delta.getDurationMilliseconds(); 01035 } 01036 01037 // static 01038 void VInstant::unfreezeTime() { 01039 gFrozenClockValue = 0; 01040 } 01041 01042 // static 01043 bool VInstant::isTimeFrozen() { 01044 return gFrozenClockValue != 0; 01045 } 01046 01047 // VDate --------------------------------------------------------------------- 01048 01049 // Is ASSERT_INVARIANT enabled/disabled specifically for VDate and VTimeOfDay? 01050 #ifdef V_ASSERT_INVARIANT_VDATE_AND_TIME_ENABLED 01051 #undef ASSERT_INVARIANT 01052 #if V_ASSERT_INVARIANT_VDATE_AND_TIME_ENABLED == 1 01053 #define ASSERT_INVARIANT() this->_assertInvariant() ///< Macro to call this->_assertInvariant(). 01054 #else 01055 #define ASSERT_INVARIANT() ((void) 0) ///< No-op. 01056 #endif 01057 #endif 01058 01059 const VCodePoint VDate::kLocalDateSeparator('/'); 01060 01061 // static 01062 VDate VDate::createFromDateString(const VString& dateString, const VCodePoint& delimiter) { 01063 VStringVector parts = dateString.split(delimiter); 01064 if (parts.size() < 3) { 01065 throw VRangeException(VSTRING_FORMAT("Unable to parse date from '%s'.", dateString.chars())); 01066 } 01067 01068 return VDate(parts[0].parseInt(), parts[1].parseInt(), parts[2].parseInt()); 01069 } 01070 01071 VDate::VDate() 01072 : mYear(0) 01073 , mMonth(1) 01074 , mDay(1) 01075 { 01076 ASSERT_INVARIANT(); 01077 } 01078 01079 VDate::VDate(const VString& timeZoneID) 01080 : mYear(0) 01081 , mMonth(1) 01082 , mDay(1) 01083 { 01084 VInstant now; 01085 VDate nowDate = now.getDate(timeZoneID); 01086 mYear = nowDate.getYear(); 01087 mMonth = nowDate.getMonth(); 01088 mDay = nowDate.getDay(); 01089 01090 ASSERT_INVARIANT(); 01091 } 01092 01093 VDate::VDate(int year, int month, int day) 01094 : mYear(year) 01095 , mMonth(month) 01096 , mDay(day) 01097 { 01098 if ((month < 1) || (month > 12) || 01099 (day < 1) || (day > 32)) // 32 allowed when incrementing the date 01100 throw VRangeException(VSTRING_FORMAT("VDate: %d-%02d-%02d is an invalid value.", year, month, day)); 01101 01102 ASSERT_INVARIANT(); 01103 } 01104 01105 int VDate::getYear() const { 01106 ASSERT_INVARIANT(); 01107 01108 return mYear; 01109 } 01110 01111 int VDate::getMonth() const { 01112 ASSERT_INVARIANT(); 01113 01114 return mMonth; 01115 } 01116 01117 int VDate::getDay() const { 01118 ASSERT_INVARIANT(); 01119 01120 return mDay; 01121 } 01122 01123 int VDate::getDayOfWeek() const { 01124 ASSERT_INVARIANT(); 01125 01126 VInstantStruct when; 01127 01128 when.mYear = mYear; 01129 when.mMonth = mMonth; 01130 when.mDay = mDay; 01131 when.mHour = 12; // noon, smack dab in middle of day 01132 when.mMinute = 0; 01133 when.mSecond = 0; 01134 when.mMillisecond = 0; 01135 01136 // First get the UTC offset of that UTC date. 01137 Vs64 offset = when.getOffsetFromUTCStruct(); 01138 01139 // Now reverse to get the mDayOfWeek filled out. 01140 when.setUTCStructFromOffset(offset); 01141 01142 return when.mDayOfWeek; 01143 } 01144 01145 void VDate::set(int year, int month, int day) { 01146 ASSERT_INVARIANT(); 01147 01148 if ((month < 1) || (month > 12) || 01149 (day < 1) || (day > 32)) // 32 allowed when incrementing the date 01150 throw VRangeException(VSTRING_FORMAT("VDate::set: %d-%02d-%02d is an invalid value.", year, month, day)); 01151 01152 mYear = year; 01153 mMonth = month; 01154 mDay = day; 01155 01156 ASSERT_INVARIANT(); 01157 } 01158 01159 void VDate::setYear(int year) { 01160 ASSERT_INVARIANT(); 01161 01162 mYear = year; 01163 01164 ASSERT_INVARIANT(); 01165 } 01166 01167 void VDate::setMonth(int month) { 01168 ASSERT_INVARIANT(); 01169 01170 mMonth = month; 01171 01172 ASSERT_INVARIANT(); 01173 } 01174 01175 void VDate::setDay(int day) { 01176 ASSERT_INVARIANT(); 01177 01178 mDay = day; 01179 01180 ASSERT_INVARIANT(); 01181 } 01182 01183 void VDate::_assertInvariant() const { 01184 VASSERT_IN_RANGE(mMonth, 0, 12); 01185 VASSERT_IN_RANGE(mDay, 0, 32); 01186 } 01187 01188 // VTimeOfDay ---------------------------------------------------------------- 01189 01190 const VCodePoint VTimeOfDay::kLocalTimeSeparator(':'); 01191 01192 VTimeOfDay::VTimeOfDay() 01193 : mHour(0) 01194 , mMinute(0) 01195 , mSecond(0) 01196 , mMillisecond(0) 01197 { 01198 ASSERT_INVARIANT(); 01199 } 01200 01201 VTimeOfDay::VTimeOfDay(const VString& timeZoneID) 01202 : mHour(0) 01203 , mMinute(0) 01204 , mSecond(0) 01205 , mMillisecond(0) 01206 { 01207 VInstant now; 01208 VTimeOfDay nowTimeOfDay = now.getTimeOfDay(timeZoneID); 01209 mHour = nowTimeOfDay.getHour(); 01210 mMinute = nowTimeOfDay.getMinute(); 01211 mSecond = nowTimeOfDay.getSecond(); 01212 mMillisecond = nowTimeOfDay.getMillisecond(); 01213 01214 ASSERT_INVARIANT(); 01215 } 01216 01217 VTimeOfDay::VTimeOfDay(int hour, int minute, int second, int millisecond) 01218 : mHour(hour) 01219 , mMinute(minute) 01220 , mSecond(second) 01221 , mMillisecond(millisecond) 01222 { 01223 if ((hour < 0) || (hour > 23) || 01224 (minute < 0) || (minute > 59) || 01225 (second < 0) || (second > 59) || 01226 (millisecond < 0) || (millisecond > 999)) 01227 throw VRangeException(VSTRING_FORMAT("VTimeOfDay: %02d:%02d:%02d.%03d is an invalid value.", hour, minute, second, millisecond)); 01228 01229 ASSERT_INVARIANT(); 01230 } 01231 01232 int VTimeOfDay::getHour() const { 01233 ASSERT_INVARIANT(); 01234 01235 return mHour; 01236 } 01237 01238 int VTimeOfDay::getMinute() const { 01239 ASSERT_INVARIANT(); 01240 01241 return mMinute; 01242 } 01243 01244 int VTimeOfDay::getSecond() const { 01245 ASSERT_INVARIANT(); 01246 01247 return mSecond; 01248 } 01249 01250 int VTimeOfDay::getMillisecond() const { 01251 ASSERT_INVARIANT(); 01252 01253 return mMillisecond; 01254 } 01255 01256 void VTimeOfDay::set(int hour, int minute, int second, int millisecond) { 01257 // No point in doing our own ASSERT_INVARIANT here, because each 01258 // setter we call will do it anyway. 01259 this->setHour(hour); 01260 this->setMinute(minute); 01261 this->setSecond(second); 01262 this->setMillisecond(millisecond); 01263 } 01264 01265 void VTimeOfDay::setHour(int hour) { 01266 ASSERT_INVARIANT(); 01267 01268 if ((hour < 0) || (hour > 23)) 01269 throw VRangeException(VSTRING_FORMAT("VTimeOfDay::set/setHour: " VSTRING_FORMATTER_INT " is an invalid value.", hour)); 01270 01271 mHour = hour; 01272 01273 ASSERT_INVARIANT(); 01274 } 01275 01276 void VTimeOfDay::setMinute(int minute) { 01277 ASSERT_INVARIANT(); 01278 01279 if ((minute < 0) || (minute > 59)) 01280 throw VRangeException(VSTRING_FORMAT("VTimeOfDay::set/setMinute: " VSTRING_FORMATTER_INT " is an invalid value.", minute)); 01281 01282 mMinute = minute; 01283 01284 ASSERT_INVARIANT(); 01285 } 01286 01287 void VTimeOfDay::setSecond(int second) { 01288 ASSERT_INVARIANT(); 01289 01290 if ((second < 0) || (second > 59)) 01291 throw VRangeException(VSTRING_FORMAT("VTimeOfDay::set/setSecond: " VSTRING_FORMATTER_INT " is an invalid value.", second)); 01292 01293 mSecond = second; 01294 01295 ASSERT_INVARIANT(); 01296 } 01297 01298 void VTimeOfDay::setMillisecond(int millisecond) { 01299 ASSERT_INVARIANT(); 01300 01301 if ((millisecond < 0) || (millisecond > 999)) 01302 throw VRangeException(VSTRING_FORMAT("VTimeOfDay::set/setMillisecond: " VSTRING_FORMATTER_INT " is an invalid value.", millisecond)); 01303 01304 mMillisecond = millisecond; 01305 01306 ASSERT_INVARIANT(); 01307 } 01308 01309 void VTimeOfDay::setToStartOfDay() { 01310 // No need for ASSERT_INVARIANT, since set() will call it. 01311 this->set(0, 0, 0, 0); 01312 } 01313 01314 void VTimeOfDay::_assertInvariant() const { 01315 VASSERT_IN_RANGE(mHour, 0, 23); 01316 VASSERT_IN_RANGE(mMinute, 0, 59); 01317 VASSERT_IN_RANGE(mSecond, 0, 59); 01318 VASSERT_IN_RANGE(mMillisecond, 0, 999); 01319 } 01320 01321 // VDateAndTime -------------------------------------------------------------- 01322 01323 VDateAndTime::VDateAndTime(const VString& timeZoneID) 01324 : mDate() 01325 , mTimeOfDay() 01326 { 01327 // This is more efficient than simply letting both the mDate and 01328 // mTimeOfDay construct themselves from the timeZoneID, because here we 01329 // can do a single time conversion, rather than letting each one do the 01330 // conversion separately. 01331 VInstant now; 01332 VDate nowDate; 01333 VTimeOfDay nowTimeOfDay; 01334 01335 now.getValues(nowDate, nowTimeOfDay, timeZoneID); 01336 01337 mDate.setYear(nowDate.getYear()); 01338 mDate.setMonth(nowDate.getMonth()); 01339 mDate.setDay(nowDate.getDay()); 01340 mTimeOfDay.setHour(nowTimeOfDay.getHour()); 01341 mTimeOfDay.setMinute(nowTimeOfDay.getMinute()); 01342 mTimeOfDay.setSecond(nowTimeOfDay.getSecond()); 01343 mTimeOfDay.setMillisecond(nowTimeOfDay.getMillisecond()); 01344 } 01345 01346 // VInstantFormatter --------------------------------------------------------- 01347 01348 // static 01349 const VInstantFormatterLocaleInfo& VInstantFormatterLocaleInfo::getLocaleInfo(const VString& /*localeName*/) { 01350 // TODO: Allow info for other locales to be registered and returned. 01351 static const VInstantFormatterLocaleInfo EN_US_INFO; 01352 return EN_US_INFO; 01353 } 01354 01355 VInstantFormatterLocaleInfo::VInstantFormatterLocaleInfo() 01356 : CE_MARKER("AD") 01357 , AM_MARKER("AM") 01358 , PM_MARKER("PM") 01359 { 01360 MONTH_NAMES_SHORT.push_back("Jan"); 01361 MONTH_NAMES_SHORT.push_back("Feb"); 01362 MONTH_NAMES_SHORT.push_back("Mar"); 01363 MONTH_NAMES_SHORT.push_back("Apr"); 01364 MONTH_NAMES_SHORT.push_back("May"); 01365 MONTH_NAMES_SHORT.push_back("Jun"); 01366 MONTH_NAMES_SHORT.push_back("Jul"); 01367 MONTH_NAMES_SHORT.push_back("Aug"); 01368 MONTH_NAMES_SHORT.push_back("Sep"); 01369 MONTH_NAMES_SHORT.push_back("Oct"); 01370 MONTH_NAMES_SHORT.push_back("Nov"); 01371 MONTH_NAMES_SHORT.push_back("Dec"); 01372 01373 MONTH_NAMES_LONG.push_back("January"); 01374 MONTH_NAMES_LONG.push_back("February"); 01375 MONTH_NAMES_LONG.push_back("March"); 01376 MONTH_NAMES_LONG.push_back("April"); 01377 MONTH_NAMES_LONG.push_back("May"); 01378 MONTH_NAMES_LONG.push_back("June"); 01379 MONTH_NAMES_LONG.push_back("July"); 01380 MONTH_NAMES_LONG.push_back("August"); 01381 MONTH_NAMES_LONG.push_back("September"); 01382 MONTH_NAMES_LONG.push_back("October"); 01383 MONTH_NAMES_LONG.push_back("November"); 01384 MONTH_NAMES_LONG.push_back("December"); 01385 01386 DAY_NAMES_SHORT.push_back("Sun"); 01387 DAY_NAMES_SHORT.push_back("Mon"); 01388 DAY_NAMES_SHORT.push_back("Tue"); 01389 DAY_NAMES_SHORT.push_back("Wed"); 01390 DAY_NAMES_SHORT.push_back("Thu"); 01391 DAY_NAMES_SHORT.push_back("Fri"); 01392 DAY_NAMES_SHORT.push_back("Sat"); 01393 01394 DAY_NAMES_LONG.push_back("Sunday"); 01395 DAY_NAMES_LONG.push_back("Monday"); 01396 DAY_NAMES_LONG.push_back("Tuesday"); 01397 DAY_NAMES_LONG.push_back("Wednesday"); 01398 DAY_NAMES_LONG.push_back("Thursday"); 01399 DAY_NAMES_LONG.push_back("Friday"); 01400 DAY_NAMES_LONG.push_back("Saturday"); 01401 } 01402 01403 #define DEFAULT_FORMAT_SPECIFIER "y-MM-dd HH:mm:ss.SSS" 01404 #define V_DEFAULT_LOCALE "en-us" 01405 01406 VInstantFormatter::VInstantFormatter() 01407 : mFormatSpecifier(DEFAULT_FORMAT_SPECIFIER) 01408 , mLocaleInfo(VInstantFormatterLocaleInfo::getLocaleInfo(V_DEFAULT_LOCALE)) 01409 { 01410 } 01411 01412 VInstantFormatter::VInstantFormatter(const VInstantFormatterLocaleInfo& localeInfo) 01413 : mFormatSpecifier(DEFAULT_FORMAT_SPECIFIER) 01414 , mLocaleInfo(localeInfo) 01415 { 01416 } 01417 01418 VInstantFormatter::VInstantFormatter(const VString& formatSpecifier) 01419 : mFormatSpecifier(formatSpecifier) 01420 , mLocaleInfo(VInstantFormatterLocaleInfo::getLocaleInfo(V_DEFAULT_LOCALE)) 01421 { 01422 } 01423 01424 VInstantFormatter::VInstantFormatter(const VString& formatSpecifier, const VInstantFormatterLocaleInfo& localeInfo) 01425 : mFormatSpecifier(formatSpecifier) 01426 , mLocaleInfo(localeInfo) 01427 { 01428 } 01429 01430 VString VInstantFormatter::formatLocalString(const VInstant& when) const { 01431 return this->_format(when.getLocalInstantFields(), static_cast<int>(when.getLocalOffsetMilliseconds())); 01432 } 01433 01434 VString VInstantFormatter::formatUTCString(const VInstant& when) const { 01435 return this->_format(when.getUTCInstantFields(), 0); 01436 } 01437 01438 VString VInstantFormatter::_format(const VInstantStruct& when, int utcOffsetMilliseconds) const { 01439 // For efficiency, extract the date and time once rather than while looping as needed. 01440 // We'll almost inevitably need them. 01441 VString result; 01442 VString pendingFieldSpecifier; 01443 bool isEscaped = false; // true if we have encountered a single quote (') but not its match to close 01444 bool isUnescapePending = false; // true if we have encountered a second single quote (') 01445 bool gotEscapedChars = false; 01446 01447 for (VString::const_iterator i = mFormatSpecifier.begin(); i != mFormatSpecifier.end(); ++i) { 01448 VCodePoint cp = *i; 01449 01450 // If we're in escaped mode, handle all cases separately right here. 01451 if (isEscaped) { 01452 01453 if (cp == '\'') { 01454 // This is likely the end of the escape block, but could be the first of a two adjacent quotes which would mean to emit a single quote 01455 if (isUnescapePending) { 01456 // Get back to normal escape mode and emit the single quote. 01457 result += '\''; 01458 isUnescapePending = false; 01459 } else { 01460 isUnescapePending = true; 01461 } 01462 01463 continue; 01464 01465 } else if (isUnescapePending) { 01466 // The unescape is complete. Exit escape mode and proceed on to the big switch statement as normal. 01467 // If there was nothing between the escape quotes, we should emit a single quote. 01468 if (!gotEscapedChars) { 01469 result += '\''; 01470 } 01471 01472 isEscaped = false; 01473 isUnescapePending = false; 01474 gotEscapedChars = false; 01475 // lack of continue here, so we will proceed to the switch below and process this character 01476 } else { 01477 // This is some character inside the escape sequence. Just emit it. 01478 gotEscapedChars = true; 01479 result += cp; 01480 continue; 01481 } 01482 01483 } 01484 01485 if (cp.isASCII()) { 01486 switch (cp.toASCIIChar()) { 01487 01488 case '\'': 01489 01490 // Enter escaped mode. 01491 this->_flushPendingFieldSpecifier(when, utcOffsetMilliseconds, pendingFieldSpecifier, result); 01492 isEscaped = true; 01493 isUnescapePending = false; 01494 break; 01495 01496 // The following characters match those in Java 1.7 SimpleDateFormat: 01497 // <http://docs.oracle.com/javase/7/docs/api/java/text/SimpleDateFormat.html> 01498 case 'G': 01499 case 'y': 01500 case 'Y': 01501 case 'M': 01502 //case 'w': // week in year: NOT YET IMPLEMENTED 01503 //case 'W': // week in month: NOT YET IMPLEMENTED 01504 //case 'D': 01505 case 'd': 01506 //case 'F': // day of week in month: NOT YET IMPLEMENTED 01507 case 'E': 01508 case 'u': 01509 case 'a': 01510 case 'H': 01511 case 'k': 01512 case 'K': 01513 case 'h': 01514 case 'm': 01515 case 's': 01516 case 'S': 01517 case 'z': 01518 case 'Z': 01519 case 'X': 01520 if (pendingFieldSpecifier.isNotEmpty() && (cp != *(pendingFieldSpecifier.begin()))) { 01521 this->_flushPendingFieldSpecifier(when, utcOffsetMilliseconds, pendingFieldSpecifier, result); 01522 } 01523 01524 pendingFieldSpecifier += cp; 01525 break; 01526 01527 // These are the ones we don't yet implement. Emit nothing, rather than unprocessed char like default does. 01528 case 'w': // week in year: NOT YET IMPLEMENTED 01529 case 'W': // week in month: NOT YET IMPLEMENTED 01530 case 'D': 01531 case 'F': // day of week in month: NOT YET IMPLEMENTED 01532 this->_flushPendingFieldSpecifier(when, utcOffsetMilliseconds, pendingFieldSpecifier, result); 01533 break; 01534 01535 default: 01536 this->_flushPendingFieldSpecifier(when, utcOffsetMilliseconds, pendingFieldSpecifier, result); 01537 result += cp; 01538 break; 01539 } 01540 01541 } 01542 01543 } 01544 01545 this->_flushPendingFieldSpecifier(when, utcOffsetMilliseconds, pendingFieldSpecifier, result); 01546 01547 return result; 01548 } 01549 01550 void VInstantFormatter::_flushPendingFieldSpecifier(const VInstantStruct& when, int utcOffsetMilliseconds, VString& fieldSpecifier, VString& resultToAppendTo) const { 01551 if (fieldSpecifier.isEmpty()) { 01552 return; 01553 } 01554 01555 int fieldSpecifierLength = fieldSpecifier.length(); 01556 if (fieldSpecifier.startsWith('G')) { 01557 this->_flushFixedLengthTextValue(mLocaleInfo.CE_MARKER, resultToAppendTo); 01558 } 01559 01560 else if (fieldSpecifier.startsWithIgnoreCase("y")) { 01561 this->_flushYearValue(when.mYear, fieldSpecifierLength, resultToAppendTo); 01562 } 01563 01564 else if (fieldSpecifier.startsWith('M')) { // month (long or short name, or number 1-12) 01565 this->_flushMonthValue(when.mMonth, fieldSpecifierLength, resultToAppendTo); 01566 } 01567 01568 else if (fieldSpecifier.startsWith('d')) { // day in month 1-31 01569 this->_flushNumberValue(when.mDay, fieldSpecifierLength, resultToAppendTo); 01570 } 01571 01572 else if (fieldSpecifier.startsWith('E')) { // day name in week (long or short name) 01573 this->_flushDayNameValue(when.mDayOfWeek, fieldSpecifierLength, resultToAppendTo); 01574 } 01575 01576 else if (fieldSpecifier.startsWith('u')) { // day number in week (1=Monday, ..., 7=Sunday) 01577 this->_flushDayNumberValue(when.mDayOfWeek, fieldSpecifierLength, resultToAppendTo); 01578 } 01579 01580 else if (fieldSpecifier.startsWith('a')) { // AM or PM 01581 this->_flushFixedLengthTextValue((when.mHour < 12) ? mLocaleInfo.AM_MARKER : mLocaleInfo.PM_MARKER, resultToAppendTo); 01582 } 01583 01584 else if (fieldSpecifier.startsWith('H')) { // hour in day 0-23 01585 this->_flushNumberValue(when.mHour, fieldSpecifierLength, resultToAppendTo); 01586 } 01587 01588 else if (fieldSpecifier.startsWith('k')) { // hour in day 1-24 01589 this->_flushNumberValue(when.mHour + 1, fieldSpecifierLength, resultToAppendTo); 01590 } 01591 01592 else if (fieldSpecifier.startsWith('K')) { // hour in am/pm 0-11 01593 this->_flushNumberValue(when.mHour % 12, fieldSpecifierLength, resultToAppendTo); 01594 } 01595 01596 else if (fieldSpecifier.startsWith('h')) { // hour in am/pm 1-12 01597 int hour = when.mHour % 12; 01598 if (hour == 0) { 01599 hour = 12; 01600 } 01601 this->_flushNumberValue(hour, fieldSpecifierLength, resultToAppendTo); 01602 } 01603 01604 else if (fieldSpecifier.startsWith('m')) { // minute in hour 0-59 01605 this->_flushNumberValue(when.mMinute, fieldSpecifierLength, resultToAppendTo); 01606 } 01607 01608 else if (fieldSpecifier.startsWith('s')) { // second in minute 0-59 01609 this->_flushNumberValue(when.mSecond, fieldSpecifierLength, resultToAppendTo); 01610 } 01611 01612 else if (fieldSpecifier.startsWith('S')) { // millisecond 0-999 01613 this->_flushNumberValue(when.mMillisecond, fieldSpecifierLength, resultToAppendTo); 01614 } 01615 01616 else if (fieldSpecifier.startsWith('z') || // time zone "general" format 01617 fieldSpecifier.startsWith('Z') || // time zone RFC 822 format 01618 fieldSpecifier.startsWith('X')) { // time zone ISO 8601 format 01619 this->_flushTimeZoneValue(utcOffsetMilliseconds, fieldSpecifier, resultToAppendTo); 01620 } 01621 01622 fieldSpecifier = VString::EMPTY(); 01623 } 01624 01625 void VInstantFormatter::_flushFixedLengthTextValue(const VString& value, VString& resultToAppendTo) const { 01626 resultToAppendTo += value; 01627 } 01628 01629 void VInstantFormatter::_flushVariableLengthTextValue(const VString& shortValue, const VString& longValue, int fieldLength, VString& resultToAppendTo) const { 01630 resultToAppendTo += (fieldLength < 4) ? shortValue : longValue; 01631 } 01632 01633 void VInstantFormatter::_flushNumberValue(int value, int fieldLength, VString& resultToAppendTo) const { 01634 VString numberFormatter(VSTRING_FORMAT("%%0%dd", fieldLength)); // note double-percent to escape the first percent sign, and extra d: we want to end up with, say, "%05d" if the field length is 5. 01635 resultToAppendTo += VSTRING_FORMAT(numberFormatter, value); 01636 } 01637 01638 void VInstantFormatter::_flushYearValue(int year, int fieldLength, VString& resultToAppendTo) const { 01639 // Rules say if if fieldLength is 2, truncate to 2 digits; otherwise treat as "number". 01640 if (fieldLength == 2) { 01641 resultToAppendTo += VSTRING_FORMAT("%02d", year % 100); 01642 } else { 01643 this->_flushNumberValue(year, fieldLength, resultToAppendTo); 01644 } 01645 } 01646 01647 void VInstantFormatter::_flushMonthValue(int month, int fieldLength, VString& resultToAppendTo) const { 01648 VASSERT_IN_RANGE(month, 1, 12); 01649 01650 // Rules say if if fieldLength is 3 or more, use name; otherwise treat as "number". 01651 if (fieldLength >= 3) { 01652 this->_flushVariableLengthTextValue(mLocaleInfo.MONTH_NAMES_SHORT[month-1], mLocaleInfo.MONTH_NAMES_LONG[month-1], fieldLength, resultToAppendTo); 01653 } else { 01654 this->_flushNumberValue(month, fieldLength, resultToAppendTo); 01655 } 01656 } 01657 01658 void VInstantFormatter::_flushDayNameValue(int dayOfWeek/*0=sun ... 6=sat*/, int fieldLength, VString& resultToAppendTo) const { 01659 this->_flushVariableLengthTextValue(mLocaleInfo.DAY_NAMES_SHORT[dayOfWeek], mLocaleInfo.DAY_NAMES_LONG[dayOfWeek], fieldLength, resultToAppendTo); 01660 } 01661 01662 void VInstantFormatter::_flushDayNumberValue(int dayOfWeek/*0=sun ... 6=sat*/, int fieldLength, VString& resultToAppendTo) const { 01663 // SimpleDateFormat day-of-week numbers are 1=Monday ... 7=Sunday, so compensate for Sunday: 01664 this->_flushNumberValue(dayOfWeek == 0 ? 7 : dayOfWeek, fieldLength, resultToAppendTo); 01665 } 01666 01667 void VInstantFormatter::_flushTimeZoneValue(int utcOffsetMilliseconds, const VString& fieldSpecifier, VString& resultToAppendTo) const { 01668 int absOffsetHours = V_ABS(utcOffsetMilliseconds / (1000 * 60 * 60)); 01669 int absOffsetMinutes = V_ABS((utcOffsetMilliseconds / (1000 * 60)) % 60); 01670 01671 if (fieldSpecifier.startsWith('z')) { // general 01672 resultToAppendTo += VSTRING_FORMAT("GMT%c%02d:%02d", (utcOffsetMilliseconds < 0 ? '-':'+'), absOffsetHours, absOffsetMinutes); 01673 } else if (fieldSpecifier.startsWith('Z')) { // RFC 822 01674 resultToAppendTo += VSTRING_FORMAT("%c%02d%02d", (utcOffsetMilliseconds < 0 ? '-':'+'), absOffsetHours, absOffsetMinutes); 01675 } else if (fieldSpecifier.startsWith('X')) { // ISO 8601 01676 const int fieldSpecifierLength = fieldSpecifier.length(); 01677 VASSERT_IN_RANGE(fieldSpecifierLength, 1, 4); 01678 01679 if (utcOffsetMilliseconds == 0) { 01680 resultToAppendTo += 'Z'; 01681 } else if (fieldSpecifier.length() == 1) { // rule says: sign followed by two-digit hours only 01682 resultToAppendTo += VSTRING_FORMAT("%c%02dZ", (utcOffsetMilliseconds < 0 ? '-':'+'), absOffsetHours); 01683 } else if (fieldSpecifier.length() == 2) { // rule says: sign followed by two-digit hours and minutes 01684 resultToAppendTo += VSTRING_FORMAT("%c%02d%02dZ", (utcOffsetMilliseconds < 0 ? '-':'+'), absOffsetHours, absOffsetMinutes); 01685 } else if (fieldSpecifier.length() == 3) { // rule says: sign followed by two-digit hours, colon, and minutes 01686 resultToAppendTo += VSTRING_FORMAT("%c%02d:%02dZ", (utcOffsetMilliseconds < 0 ? '-':'+'), absOffsetHours, absOffsetMinutes); 01687 } 01688 } 01689 }