Vault  4.1
vinstant.cpp
Go to the documentation of this file.
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 }

Copyright ©1997-2014 Trygve Isaacson. All rights reserved. This documentation was generated with Doxygen.