You are viewing a plain text version of this content. The canonical link for it is here.
Posted to log4cxx-dev@logging.apache.org by ca...@apache.org on 2005/02/19 02:59:37 UTC

cvs commit: logging-log4cxx/tests/src/helpers localechanger.cpp localechanger.h Makefile.am cacheddateformattestcase.cpp datetimedateformattestcase.cpp

carnold     2005/02/18 17:59:37

  Modified:    include/log4cxx/helpers cacheddateformat.h
               src      cacheddateformat.cpp patternparser.cpp timezone.cpp
               tests/src/helpers Makefile.am cacheddateformattestcase.cpp
                        datetimedateformattestcase.cpp
  Added:       tests/src/helpers localechanger.cpp localechanger.h
  Log:
  LOGCXX-49: Migration of updated CachedDateFormat from log4j
  
  Revision  Changes    Path
  1.11      +176 -31   logging-log4cxx/include/log4cxx/helpers/cacheddateformat.h
  
  Index: cacheddateformat.h
  ===================================================================
  RCS file: /home/cvs/logging-log4cxx/include/log4cxx/helpers/cacheddateformat.h,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- cacheddateformat.h	15 Feb 2005 23:55:59 -0000	1.10
  +++ cacheddateformat.h	19 Feb 2005 01:59:37 -0000	1.11
  @@ -21,48 +21,193 @@
   
   namespace log4cxx
   {
  -        namespace helpers
  +        namespace pattern
           {
  -          class LOG4CXX_EXPORT CachedDateFormat : public DateFormat {
  +          class LOG4CXX_EXPORT CachedDateFormat : public log4cxx::helpers::DateFormat {
             public:
  -               CachedDateFormat(DateFormatPtr& baseFormatter);
  +              enum {
  +                /*
  +                 *  Constant used to represent that there was no change
  +                 *  observed when changing the millisecond count.
  +                 */
  +                 NO_MILLISECONDS = -2,
  +                 /*
  +                  *  Constant used to represent that there was an
  +                  *  observed change, but was an expected change.
  +                  */
  +                 UNRECOGNIZED_MILLISECONDS = -1
  +              };
   
  -               virtual void format(LogString &s,
  +          private:
  +             /**
  +              *  Supported digit set.  If the wrapped DateFormat uses
  +              *  a different unit set, the millisecond pattern
  +              *  will not be recognized and duplicate requests
  +              *  will use the cache.
  +              */
  +               static const logchar* const digits;
  +
  +              enum {
  +               /**
  +                *  First magic number used to detect the millisecond position.
  +                */
  +               magic1 = 654000,
  +               /**
  +                *  Second magic number used to detect the millisecond position.
  +                */
  +               magic2 = 987000
  +              };
  +
  +              /**
  +               *  Expected representation of first magic number.
  +               */
  +              static const logchar* const magicString1;
  +
  +
  +              /**
  +               *  Expected representation of second magic number.
  +               */
  +              static const logchar* const magicString2;
  +
  +
  +              /**
  +               *  Expected representation of 0 milliseconds.
  +               */
  +             static const logchar* const zeroString;
  +
  +            /**
  +             *   Wrapped formatter.
  +             */
  +            log4cxx::helpers::DateFormatPtr formatter;
  +
  +            /**
  +             *  Index of initial digit of millisecond pattern or
  +             *   UNRECOGNIZED_MILLISECONDS or NO_MILLISECONDS.
  +             */
  +            mutable int millisecondStart;
  +
  +            /**
  +             *  Integral second preceding the previous convered Date.
  +             */
  +            mutable log4cxx_time_t slotBegin;
  +
  +
  +            /**
  +             *  Cache of previous conversion.
  +             */
  +            mutable LogString cache;
  +
  +
  +            /**
  +             *  Maximum validity period for the cache.
  +             *  Typically 1, use cache for duplicate requests only, or
  +             *  1000000, use cache for requests within the same integral second.
  +             */
  +            const int expiration;
  +
  +            /**
  +             *  Date requested in previous conversion.
  +             */
  +            mutable log4cxx_time_t previousTime;
  +
  +       public:
  +          /**
  +           *  Creates a new CachedDateFormat object.
  +           *  @param dateFormat Date format, may not be null.
  +           *  @param expiration maximum cached range in microseconds.
  +           *    If the dateFormat is known to be incompatible with the
  +           *      caching algorithm, use a value of 0 to totally disable
  +           *      caching or 1 to only use cache for duplicate requests.
  +           */
  +            CachedDateFormat(const log4cxx::helpers::DateFormatPtr& dateFormat, int expiration);
  +
  +            /**
  +             * Finds start of millisecond field in formatted time.
  +             * @param time long time, must be integral number of seconds
  +             * @param formatted String corresponding formatted string
  +             * @param formatter DateFormat date format
  +             * @return int position in string of first digit of milliseconds,
  +             *    -1 indicates no millisecond field, -2 indicates unrecognized
  +             *    field (likely RelativeTimeDateFormat)
  +             */
  +            static int findMillisecondStart(
  +              log4cxx_time_t time, const LogString& formatted,
  +                  const log4cxx::helpers::DateFormatPtr& formatter,
  +                  log4cxx::helpers::Pool& pool);
  +
  +            /**
  +             * Formats a Date into a date/time string.
  +             *
  +             *  @param date the date to format.
  +             *  @param sbuf the string buffer to write to.
  +             *  @param pool memory pool.
  +             */
  +               virtual void format(LogString &sbuf,
                      log4cxx_time_t date,
                      log4cxx::helpers::Pool& p) const;
  -               virtual void setTimeZone(const TimeZonePtr& zone);
  +
  +           private:
  +               /**
  +                *   Formats a count of milliseconds (0-999) into a numeric representation.
  +                *   @param millis Millisecond coun between 0 and 999.
  +                *   @buf String buffer, may not be null.
  +                *   @offset Starting position in buffer, the length of the
  +                *       buffer must be at least offset + 3.
  +                */
  +                static void millisecondFormat(int millis,
  +                    LogString& buf,
  +                    int offset);
  +
  +
  +           public:
  +               /**
  +                * Set timezone.
  +                *
  +                * @remarks Setting the timezone using getCalendar().setTimeZone()
  +                * will likely cause caching to misbehave.
  +                * @param timeZone TimeZone new timezone
  +                */
  +               virtual void setTimeZone(const log4cxx::helpers::TimeZonePtr& zone);
  +
  +                /**
  +                * Format an integer consistent with the format method.
  +                * @param s string to which the numeric string is appended.
  +                * @param n integer value.
  +                * @param p memory pool used during formatting.
  +                */
                  virtual void numberFormat(LogString& s,
                                            int n,
                                            log4cxx::helpers::Pool& p) const;
   
  +              /**
  +               * Gets maximum cache validity for the specified SimpleDateTime
  +               *    conversion pattern.
  +               *  @param pattern conversion pattern, may not be null.
  +               *  @returns Duration in microseconds from an integral second
  +               *      that the cache will return consistent results.
  +               */
  +               static int getMaximumCacheValidity(const LogString& pattern);
   
             private:
  -          /**
  -           * Finds start of millisecond field in formatted time.
  -           * @param time long time, must be integral number of seconds
  -           * @param formatted String corresponding formatted string
  -           * @param zeroDigit char digit used to represent zero
  -           * @param formatter DateFormat date format
  -           * @return int position in string of first digit of milliseconds,
  -           *    -1 indicates no millisecond field, -2 indicates unrecognized
  -           *    field (likely RelativeTimeDateFormat)
  -           */
  -               static int findMillisecondStart(const log4cxx_time_t time,
  -                                          const LogString& formatted,
  -                                          const logchar zeroDigit,
  -                                          const logchar nineDigit,
  -                                          const DateFormatPtr& formatter,
  -                                          log4cxx::helpers::Pool& p);
  -
  -               DateFormatPtr formatter;
  -               mutable int millisecondStart;
  -               mutable LogString cache;
  -               mutable log4cxx_time_t previousTime;
  -               logchar zeroDigit;
  -               logchar nineDigit;
  -                           enum {
  -                   UNRECOGNIZED_MILLISECOND_PATTERN = -2,
  -                                           NO_MILLISECOND_PATTERN = -1 };
  +               CachedDateFormat(const CachedDateFormat&);
  +               CachedDateFormat& operator=(const CachedDateFormat&);
  +
  +               /**
  +               * Tests if two string regions are equal.
  +               * @param target target string.
  +               * @param toffset character position in target to start comparison.
  +               * @param other other string.
  +               * @param ooffset character position in other to start comparison.
  +               * @param len length of region.
  +               * @return true if regions are equal.
  +               */
  +               static bool regionMatches(
  +                   const LogString& target,
  +                   size_t toffset,
  +                   const LogString& other,
  +                   size_t ooffset,
  +                   size_t len);
  +
             };
   
   
  
  
  
  1.11      +258 -123  logging-log4cxx/src/cacheddateformat.cpp
  
  Index: cacheddateformat.cpp
  ===================================================================
  RCS file: /home/cvs/logging-log4cxx/src/cacheddateformat.cpp,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- cacheddateformat.cpp	15 Feb 2005 23:56:01 -0000	1.10
  +++ cacheddateformat.cpp	19 Feb 2005 01:59:37 -0000	1.11
  @@ -20,147 +20,282 @@
   
   #include <apr_time.h>
   #include <log4cxx/helpers/pool.h>
  +#include <limits>
  +#include <log4cxx/helpers/exception.h>
   
   using namespace log4cxx;
   using namespace log4cxx::helpers;
  +using namespace log4cxx::pattern;
   
  -CachedDateFormat::CachedDateFormat(DateFormatPtr& formatter) :
  -    formatter(formatter) {
  +/**
  +*  Supported digit set.  If the wrapped DateFormat uses
  +*  a different unit set, the millisecond pattern
  +*  will not be recognized and duplicate requests
  +*  will use the cache.
  +*/
  +const logchar* const CachedDateFormat::digits = LOG4CXX_STR("0123456789");
  +
  +/**
  + *  Expected representation of first magic number.
  + */
  +const logchar* const CachedDateFormat::magicString1 = LOG4CXX_STR("654");
  +
  +
  +/**
  + *  Expected representation of second magic number.
  + */
  +const logchar* const CachedDateFormat::magicString2 = LOG4CXX_STR("987");
  +
  +
  +/**
  + *  Expected representation of 0 milliseconds.
  + */
  +const logchar* const CachedDateFormat::zeroString = LOG4CXX_STR("000");
  +
  +
  +/**
  + *  Creates a new CachedDateFormat object.
  + *  @param dateFormat Date format, may not be null.
  + *  @param expiration maximum cached range in milliseconds.
  + *    If the dateFormat is known to be incompatible with the
  + *      caching algorithm, use a value of 0 to totally disable
  + *      caching or 1 to only use cache for duplicate requests.
  + */
  +CachedDateFormat::CachedDateFormat(const DateFormatPtr& dateFormat,
  +        int expiration) :
  +       formatter(dateFormat),
  +       expiration(expiration),
  +       millisecondStart(0),
  +       previousTime(std::numeric_limits<log4cxx_time_t>::min()),
  +       slotBegin(std::numeric_limits<log4cxx_time_t>::min()),
  +       cache(50, LOG4CXX_STR(' ')) {
  +  if (dateFormat == NULL) {
  +    throw IllegalArgumentException("dateFormat cannot be null");
  +  }
  +  if (expiration < 0) {
  +    throw IllegalArgumentException("expiration must be non-negative");
  +  }
  +}
  +
  +
  +/**
  + * Finds start of millisecond field in formatted time.
  + * @param time long time, must be integral number of seconds
  + * @param formatted String corresponding formatted string
  + * @param formatter DateFormat date format
  + * @return int position in string of first digit of milliseconds,
  + *    -1 indicates no millisecond field, -2 indicates unrecognized
  + *    field (likely RelativeTimeDateFormat)
  + */
  +int CachedDateFormat::findMillisecondStart(
  +  apr_time_t time, const LogString& formatted,
  +  const DateFormatPtr& formatter,
  +  Pool& pool) {
  +
  +  apr_time_t slotBegin = (time / 1000000) * 1000000;
  +  if (slotBegin > time) {
  +     slotBegin -= 1000000;
  +  }
  +  int millis = (int) (time - slotBegin)/1000;
  +
  +  int magic = magic1;
  +  LogString magicString(magicString1);
  +  if (millis == magic1) {
  +      magic = magic2;
  +      magicString = magicString2;
  +  }
   
  -    apr_time_t nowTime = apr_time_now();
  -    previousTime = (nowTime / APR_USEC_PER_SEC) * APR_USEC_PER_SEC;
  -    //
  -    //    if now is before 1970 and previousTime was truncated forward
  -    //       set cached time back one second
  -    if (nowTime - previousTime < 0) {
  -      previousTime -= APR_USEC_PER_SEC;
  -    }
  -    Pool p;
  -    formatter->format(cache, previousTime, p);
  -    LogString digits;
  -    formatter->numberFormat(digits, 90, p);
  -    nineDigit = digits[0];
  -    zeroDigit = digits[1];
  -    millisecondStart = findMillisecondStart(previousTime,
  -           cache,
  -           zeroDigit,
  -           nineDigit,
  -           formatter,
  -           p);
  -}
  -
  -int CachedDateFormat::findMillisecondStart(const log4cxx_time_t time,
  -                                          const LogString& formatted,
  -                                          logchar zeroDigit,
  -                                          logchar nineDigit,
  -                                          const DateFormatPtr& formatter,
  -                                          Pool& p) {
  -      LogString plus987;
  -      formatter->format(plus987, time + 987000, p);
  -      //
  -      //    find first difference between values
  -      //
  -      for (size_t i = 0; i < formatted.length(); i++) {
  -        if (formatted[i] != plus987[i]) {
  -          if (formatted[i] == zeroDigit && plus987[i] == nineDigit) {
  -            return i;
  -          } else {
  -            return UNRECOGNIZED_MILLISECOND_PATTERN;
  +  LogString plusMagic;
  +  formatter->format(plusMagic, slotBegin + magic, pool);
  +
  +  /**
  +   *   If the string lengths differ then
  +   *      we can't use the cache except for duplicate requests.
  +   */
  +  if (plusMagic.length() != formatted.length()) {
  +      return UNRECOGNIZED_MILLISECONDS;
  +  } else {
  +      // find first difference between values
  +     for (int i = 0; i < formatted.length(); i++) {
  +        if (formatted[i] != plusMagic[i]) {
  +           //
  +           //   determine the expected digits for the base time
  +           LogString formattedMillis(LOG4CXX_STR("ABC"));
  +           millisecondFormat(millis, formattedMillis, 0);
  +
  +           LogString plusZero;
  +           formatter->format(plusZero, slotBegin, pool);
  +
  +           //   If the next 3 characters match the magic
  +           //      strings and the remaining fragments are identical
  +           //
  +           //
  +           if (plusZero.length() == formatted.length()
  +              && regionMatches(magicString, 0, plusMagic, i, magicString.length())
  +              && regionMatches(formattedMillis, 0, formatted, i, magicString.length())
  +              && regionMatches(zeroString, 0, plusZero, i, 3)
  +              && (formatted.length() == i + 3
  +                 || plusZero.compare(i + 3,
  +                       LogString::npos, plusMagic, i+3, LogString::npos) == 0)) {
  +              return i;
  +           } else {
  +              return UNRECOGNIZED_MILLISECONDS;
             }
           }
  -      }
  -    return NO_MILLISECOND_PATTERN;
  +     }
  +  }
  +  return  NO_MILLISECONDS;
   }
   
   
  -  /**
  -   * Converts a Date utilizing a previously converted
  -   * value if possible.
  +/**
  + * Formats a millisecond count into a date/time string.
  + *
  + *  @param now Number of milliseconds after midnight 1 Jan 1970 GMT.
  + *  @param sbuf the string buffer to write to
  + */
  + void CachedDateFormat::format(LogString& buf, log4cxx_time_t now, Pool& p) const {
  +
  +  //
  +  // If the current requested time is identical to the previously
  +  //     requested time, then append the cache contents.
  +  //
  +  if (now == previousTime) {
  +       buf.append(cache);
  +       return;
  +  }
   
  -     @param date the date to format
  -     @param sbuf the string buffer to write to
  -     @param fieldPosition remains untouched
  -   */
  -void CachedDateFormat::format(LogString& s, log4cxx_time_t date, Pool& p) const {
  -    if (millisecondStart == UNRECOGNIZED_MILLISECOND_PATTERN) {
  -      formatter->format(s, date, p);
  -      return;
  -    }
  -    if (date < previousTime + APR_USEC_PER_SEC && date >= previousTime) {
  -      if (millisecondStart >= 0) {
  -        cache.erase(millisecondStart, 3);
  -        int millis = apr_time_as_msec(date - previousTime);
  -        int cacheLength = cache.length();
  -        formatter->numberFormat(cache, millis, p);
  -        int milliLength = cache.length() - cacheLength;
  -        //
  -        //   if it didn't belong at the end, then move it
  -        if (cacheLength != millisecondStart) {
  -          LogString milli = cache.substr(cacheLength);
  -          cache.erase(cache.begin() + cacheLength, cache.end());
  -          cache.insert(millisecondStart, milli);
  -        }
  -        if (milliLength < 3) {
  -           cache.insert(millisecondStart,
  -                 3 - milliLength, zeroDigit);
  -        }
  -      }
  -    } else {
  -      apr_time_t prev = (date / APR_USEC_PER_SEC) * APR_USEC_PER_SEC;
  -      //
  -      //   if earlier than 1970 and rounded toward 1970
  -      //      then move back one second
  -      if (date - prev < 0) {
  -        prev -= APR_USEC_PER_SEC;
  -      }
  -          previousTime = prev;
  -      size_t prevLength = cache.length();
  -      cache.erase(cache.begin(), cache.end());
  -      formatter->format(cache, date, p);
  -      //
  -      //   if the length changed then
  -      //      recalculate the millisecond position
  -      if (cache.length() != prevLength) {
  -        LogString formattedPreviousTime;
  -        formatter->format(formattedPreviousTime, previousTime, p);
  -        millisecondStart =
  -            findMillisecondStart(previousTime,
  -                                 formattedPreviousTime,
  -                                 zeroDigit,
  -                                 nineDigit,
  -                                 formatter, p);
  +  //
  +  //   If millisecond pattern was not unrecognized
  +  //     (that is if it was found or milliseconds did not appear)
  +  //
  +  if (millisecondStart != UNRECOGNIZED_MILLISECONDS) {
  +
  +      //    Check if the cache is still valid.
  +      //    If the requested time is within the same integral second
  +      //       as the last request and a shorter expiration was not requested.
  +      if (now < slotBegin + expiration
  +          && now >= slotBegin
  +          && now < slotBegin + 1000000L) {
  +
  +          //
  +          //    if there was a millisecond field then update it
  +          //
  +          if (millisecondStart >= 0 ) {
  +              millisecondFormat((int) ((now - slotBegin)/1000), cache, millisecondStart);
  +          }
  +          //
  +          //   update the previously requested time
  +          //      (the slot begin should be unchanged)
  +          previousTime = now;
  +          buf.append(cache);
  +          return;
         }
  -    }
  -    s.append(cache);
     }
   
   
  -  /**
  -   * Set timezone.
  -   *
  -   * @remarks Setting the timezone using getCalendar().setTimeZone()
  -   * will likely cause caching to misbehave.
  -   * @param timeZone TimeZone new timezone
  -   */
  -void CachedDateFormat::setTimeZone(const TimeZonePtr& timeZone) {
  -    formatter->setTimeZone(timeZone);
  -    size_t prevLength = cache.length();
  -    cache.erase(cache.begin(), cache.end());
  -        Pool p;
  -    formatter->format(cache, previousTime, p);
  -    //
  -    //   if the length changed then
  -    //      recalculate the millisecond position
  -    if (cache.length() != prevLength) {
  -      millisecondStart = findMillisecondStart(previousTime,
  -                                              cache,
  -                                              zeroDigit,
  -                                              nineDigit,
  -                                              formatter, p);
  -    }
  +  //
  +  //  could not use previous value.
  +  //    Call underlying formatter to format date.
  +  cache.erase(cache.begin(), cache.end());
  +  formatter->format(cache, now, p);
  +  buf.append(cache);
  +  previousTime = now;
  +  slotBegin = (previousTime / 1000000) * 1000000;
  +  if (slotBegin > previousTime) {
  +      slotBegin -= 1000000;
  +  }
  +
  +
  +  //
  +  //    if the milliseconds field was previous found
  +  //       then reevaluate in case it moved.
  +  //
  +  if (millisecondStart >= 0) {
  +      millisecondStart = findMillisecondStart(now, cache, formatter, p);
     }
  +}
  +
  +
  +/**
  + *   Formats a count of milliseconds (0-999) into a numeric representation.
  + *   @param millis Millisecond coun between 0 and 999.
  + *   @buf String buffer, may not be null.
  + *   @offset Starting position in buffer, the length of the
  + *       buffer must be at least offset + 3.
  + */
  +void CachedDateFormat::millisecondFormat(int millis,
  +     LogString& buf,
  +     int offset) {
  +     buf[offset] = digits[ millis / 100];
  +     buf[offset + 1] = digits[(millis / 10) % 10];
  +     buf[offset + 2] = digits[millis  % 10];
  + }
  +
  +/**
  + * Set timezone.
  + *
  + * @remarks Setting the timezone using getCalendar().setTimeZone()
  + * will likely cause caching to misbehave.
  + * @param timeZone TimeZone new timezone
  + */
  +void CachedDateFormat::setTimeZone(const TimeZonePtr& timeZone) {
  +  formatter->setTimeZone(timeZone);
  +  previousTime = std::numeric_limits<log4cxx_time_t>::min();
  +  slotBegin = std::numeric_limits<log4cxx_time_t>::min();
  +}
  +
   
   
   void CachedDateFormat::numberFormat(LogString& s, int n, Pool& p) const {
     formatter->numberFormat(s, n, p);
   }
  +
  +
  +/**
  + * Gets maximum cache validity for the specified SimpleDateTime
  + *    conversion pattern.
  + *  @param pattern conversion pattern, may not be null.
  + *  @returns Duration in microseconds from an integral second
  + *      that the cache will return consistent results.
  + */
  +int CachedDateFormat::getMaximumCacheValidity(const LogString& pattern) {
  +   //
  +   //   If there are more "S" in the pattern than just one "SSS" then
  +   //      (for example, "HH:mm:ss,SSS SSS"), then set the expiration to
  +   //      one millisecond which should only perform duplicate request caching.
  +   //
  +   size_t firstS = pattern.find(LOG4CXX_STR('S'));
  +   size_t len = pattern.length();
  +   //
  +   //   if there are no S's or
  +   //      three that start with the first S and no fourth S in the string
  +   //
  +   if (firstS == LogString::npos ||
  +       (len >= firstS + 3 && pattern.compare(firstS, 3, LOG4CXX_STR("SSS")) == 0
  +           && (len == firstS + 3 ||
  +                pattern.find(LOG4CXX_STR('S'), firstS + 3) == LogString::npos))) {
  +           return 1000000;
  +   }
  +   return 1000;
  +}
  +
  +
  +/**
  +* Tests if two string regions are equal.
  +* @param target target string.
  +* @param toffset character position in target to start comparison.
  +* @param other other string.
  +* @param ooffset character position in other to start comparison.
  +* @param len length of region.
  +* @return true if regions are equal.
  +*/
  +bool CachedDateFormat::regionMatches(
  +    const LogString& target,
  +    size_t toffset,
  +    const LogString& other,
  +    size_t ooffset,
  +    size_t len) {
  +    return target.compare(toffset, len, other, ooffset, len) == 0;
  +}
  +
  
  
  
  1.30      +17 -3     logging-log4cxx/src/patternparser.cpp
  
  Index: patternparser.cpp
  ===================================================================
  RCS file: /home/cvs/logging-log4cxx/src/patternparser.cpp,v
  retrieving revision 1.29
  retrieving revision 1.30
  diff -u -r1.29 -r1.30
  --- patternparser.cpp	18 Feb 2005 17:18:53 -0000	1.29
  +++ patternparser.cpp	19 Feb 2005 01:59:37 -0000	1.30
  @@ -27,12 +27,14 @@
   #include <log4cxx/mdc.h>
   #include <log4cxx/helpers/transcoder.h>
   #include <sstream>
  +#include <log4cxx/helpers/exception.h>
   
   #include <apr_pools.h>
   
   using namespace log4cxx;
   using namespace log4cxx::helpers;
   using namespace log4cxx::spi;
  +using namespace log4cxx::pattern;
   
   #define ESCAPE_CHAR LOG4CXX_STR('%')
   
  @@ -209,7 +211,7 @@
     while ((i < patternLength) && (pattern.at(i) == LOG4CXX_STR('{'))) {
       size_t end = pattern.find(LOG4CXX_STR('}'), i);
   
  -    if (end > i) {
  +    if (end != LogString::npos && end > i) {
         LogString r(pattern.substr(i + 1, end - (i + 1)));
         options.push_back(r);
          i = end+1;
  @@ -461,6 +463,7 @@
   DateFormatPtr PatternParser::DatePatternConverter::createDateFormat(
       const std::vector<LogString>& options) {
       DateFormatPtr df;
  +    int maximumCacheValidity = 1000000;
       if (options.size() == 0) {
           df = new ISO8601DateFormat();
       } else {
  @@ -478,7 +481,16 @@
               df = new DateTimeDateFormat();
          } else {
            if (dateFormatStr.find(LOG4CXX_STR('%')) == std::string::npos) {
  -            df = new SimpleDateFormat(dateFormatStr);
  +            try {
  +               df = new SimpleDateFormat(dateFormatStr);
  +               maximumCacheValidity =
  +                  CachedDateFormat::getMaximumCacheValidity(dateFormatStr);
  +            } catch(IllegalArgumentException& e) {
  +               df = new ISO8601DateFormat();
  +               LogLog::warn(((LogString)
  +                  LOG4CXX_STR("Could not instantiate SimpleDateFormat with pattern "))
  +                     + dateFormatStr, e);
  +            }
            } else {
               df = new StrftimeDateFormat(dateFormatStr);
            }
  @@ -490,7 +502,9 @@
            }
          }
       }
  -    df = new CachedDateFormat(df);
  +    if (maximumCacheValidity > 0) {
  +        df = new CachedDateFormat(df, maximumCacheValidity);
  +    }
       return df;
   }
   
  
  
  
  1.13      +34 -3     logging-log4cxx/src/timezone.cpp
  
  Index: timezone.cpp
  ===================================================================
  RCS file: /home/cvs/logging-log4cxx/src/timezone.cpp,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- timezone.cpp	18 Feb 2005 17:18:54 -0000	1.12
  +++ timezone.cpp	19 Feb 2005 01:59:37 -0000	1.13
  @@ -23,6 +23,7 @@
   #include <log4cxx/helpers/transcoder.h>
   #include <log4cxx/helpers/stringhelper.h>
   #include <log4cxx/helpers/pool.h>
  +#include <log4cxx/logger.h>
   
   using namespace log4cxx;
   using namespace log4cxx::helpers;
  @@ -49,7 +50,17 @@
           /** Explode time to human readable form. */
           log4cxx_status_t explode( apr_time_exp_t * result, log4cxx_time_t input ) const
           {
  -          return apr_time_exp_gmt( result, input );
  +           apr_status_t stat;
  +           //  APR 1.1 and early mishandles microseconds on dates
  +           //   before 1970, APR bug 32520
  +           if (LOG4CXX_UNLIKELY(input < 0 && apr_time_usec(input) < 0)) {
  +              apr_time_t floorTime = (apr_time_sec(input) -1) * APR_USEC_PER_SEC;
  +              stat = apr_time_exp_gmt(result, floorTime);
  +              result->tm_usec = input - floorTime;
  +           } else {
  +              stat = apr_time_exp_gmt( result, input );
  +           }
  +           return stat;
           }
   
         private:
  @@ -74,7 +85,17 @@
           /** Explode time to human readable form. */
           log4cxx_status_t explode( apr_time_exp_t * result, log4cxx_time_t input ) const
           {
  -          return apr_time_exp_lt( result, input );
  +          apr_status_t stat;
  +          //  APR 1.1 and early mishandles microseconds on dates
  +          //   before 1970, APR bug 32520
  +          if (LOG4CXX_UNLIKELY(input < 0 && apr_time_usec(input) < 0)) {
  +             apr_time_t floorTime = (apr_time_sec(input) -1) * APR_USEC_PER_SEC;
  +             stat = apr_time_exp_lt(result, floorTime);
  +             result->tm_usec = input - floorTime;
  +          } else {
  +             stat = apr_time_exp_lt( result, input );
  +          }
  +          return stat;
           }
   
   
  @@ -112,7 +133,17 @@
           /** Explode time to human readable form. */
           log4cxx_status_t explode( apr_time_exp_t * result, log4cxx_time_t input ) const
           {
  -          return apr_time_exp_tz( result, input, offset );
  +          apr_status_t stat;
  +          //  APR 1.1 and early mishandles microseconds on dates
  +          //   before 1970, APR bug 32520
  +          if (LOG4CXX_UNLIKELY(input < 0 && apr_time_usec(input) < 0)) {
  +             apr_time_t floorTime = (apr_time_sec(input) -1) * APR_USEC_PER_SEC;
  +             stat = apr_time_exp_tz(result, floorTime, offset);
  +             result->tm_usec = input - floorTime;
  +          } else {
  +             stat = apr_time_exp_tz( result, input, offset );
  +          }
  +          return stat;
           }
   
   
  
  
  
  1.9       +2 -1      logging-log4cxx/tests/src/helpers/Makefile.am
  
  Index: Makefile.am
  ===================================================================
  RCS file: /home/cvs/logging-log4cxx/tests/src/helpers/Makefile.am,v
  retrieving revision 1.8
  retrieving revision 1.9
  diff -u -r1.8 -r1.9
  --- Makefile.am	3 Jan 2005 06:43:44 -0000	1.8
  +++ Makefile.am	19 Feb 2005 01:59:37 -0000	1.9
  @@ -1,4 +1,4 @@
  -EXTRA_DIST = *.cpp
  +EXTRA_DIST = *.cpp *.h
   
   if TESTS
   
  @@ -12,6 +12,7 @@
           cyclicbuffertestcase.cpp\
           datetimedateformattestcase.cpp \
           iso8601dateformattestcase.cpp \
  +        localechanger.cpp\
           optionconvertertestcase.cpp       \
           propertiestestcase.cpp \
           relativetimedateformattestcase.cpp \
  
  
  
  1.11      +354 -53   logging-log4cxx/tests/src/helpers/cacheddateformattestcase.cpp
  
  Index: cacheddateformattestcase.cpp
  ===================================================================
  RCS file: /home/cvs/logging-log4cxx/tests/src/helpers/cacheddateformattestcase.cpp,v
  retrieving revision 1.10
  retrieving revision 1.11
  diff -u -r1.10 -r1.11
  --- cacheddateformattestcase.cpp	15 Feb 2005 23:56:04 -0000	1.10
  +++ cacheddateformattestcase.cpp	19 Feb 2005 01:59:37 -0000	1.11
  @@ -23,19 +23,21 @@
   #include "../insertwide.h"
   #include <apr.h>
   #include <apr_time.h>
  +#include "localechanger.h"
   
   using namespace log4cxx;
   using namespace log4cxx::helpers;
  -
  +using namespace log4cxx::pattern;
   
   #if defined(_WIN32)
  -#define LOCALE_US "us"
  -#define LOCALE_JP "jpn"
  +#define LOCALE_US "English_us"
  +#define LOCALE_JP "Japanese_japan"
   #else
   #define LOCALE_US "en_US"
   #define LOCALE_JP "ja_JP"
   #endif
   
  +
   //Define INT64_C for compilers that don't have it
   #if (!defined(INT64_C))
   #define INT64_C(value)  value ##LL
  @@ -45,7 +47,7 @@
   /**
      Unit test {@link CachedDateFormat}.
      @author Curt Arnold
  -   @since 1.3.0 */
  +   @since 0.9.8 */
      class CachedDateFormatTestCase : public CppUnit::TestFixture
      {
        CPPUNIT_TEST_SUITE( CachedDateFormatTestCase );
  @@ -53,10 +55,22 @@
        CPPUNIT_TEST( test2 );
        CPPUNIT_TEST( test3 );
        CPPUNIT_TEST( test4 );
  -//     CPPUNIT_TEST( test5 );
  +     CPPUNIT_TEST( test5 );
        CPPUNIT_TEST( test6 );
  -     CPPUNIT_TEST( test7 );
        CPPUNIT_TEST( test8 );
  +     CPPUNIT_TEST( test9 );
  +     CPPUNIT_TEST( test10 );
  +     CPPUNIT_TEST( test11);
  +     CPPUNIT_TEST( test12 );
  +     CPPUNIT_TEST( test13 );
  +     CPPUNIT_TEST( test14 );
  +     CPPUNIT_TEST( test15 );
  +     CPPUNIT_TEST( test16 );
  +     CPPUNIT_TEST( test17);
  +     CPPUNIT_TEST( test18);
  +     CPPUNIT_TEST( test19);
  +     CPPUNIT_TEST( test20);
  +     CPPUNIT_TEST( test21);
        CPPUNIT_TEST_SUITE_END();
   
   
  @@ -72,7 +86,7 @@
       //     are optimized to reuse previous formatted value
       //     make a couple of nearly spaced calls
       DateFormatPtr baseFormatter(new AbsoluteTimeDateFormat());
  -    CachedDateFormat gmtFormat(baseFormatter);
  +    CachedDateFormat gmtFormat(baseFormatter, 1000000);
       gmtFormat.setTimeZone(TimeZone::getGMT());
   
       apr_time_t jul1 = MICROSECONDS_PER_DAY * 12601L;
  @@ -109,28 +123,25 @@
     void test2() {
         apr_time_t jul2 = MICROSECONDS_PER_DAY * 12602;
         DateFormatPtr baseFormatter(new AbsoluteTimeDateFormat());
  -      CachedDateFormat gmtFormat(baseFormatter);
  +      CachedDateFormat gmtFormat(baseFormatter, 1000000);
         gmtFormat.setTimeZone(TimeZone::getGMT());
   
        DateFormatPtr chicagoBase(new AbsoluteTimeDateFormat());
  -     CachedDateFormat chicagoFormat(chicagoBase);
  +     CachedDateFormat chicagoFormat(chicagoBase, 1000000);
        chicagoFormat.setTimeZone(TimeZone::getTimeZone(LOG4CXX_STR("GMT-5")));
   
        Pool p;
  -
        LogString actual;
  -
        gmtFormat.format(actual, jul2, p);
        CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,000"), actual);
  +
        actual.erase(actual.begin(), actual.end());
  +     chicagoFormat.format(actual, jul2, p);
  +     CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("19:00:00,000"), actual);
   
  -      chicagoFormat.format(actual, jul2, p);
  -      CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("19:00:00,000"), actual);
         actual.erase(actual.begin(), actual.end());
  -
  -    gmtFormat.format(actual, jul2, p);
  -    CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,000"), actual);
  -    actual.erase(actual.begin(), actual.end());
  +      gmtFormat.format(actual, jul2, p);
  +      CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,000"), actual);
     }
   
     /**
  @@ -141,7 +152,7 @@
       //     are optimized to reuse previous formatted value
       //     make a couple of nearly spaced calls
       DateFormatPtr baseFormatter(new AbsoluteTimeDateFormat());
  -    CachedDateFormat gmtFormat(baseFormatter);
  +    CachedDateFormat gmtFormat(baseFormatter, 1000000);
       gmtFormat.setTimeZone(TimeZone::getGMT());
   
       apr_time_t ticks = MICROSECONDS_PER_DAY * -7;
  @@ -155,7 +166,6 @@
       CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,000"), actual);
       actual.erase(actual.begin(), actual.end());
   
  -#if defined(_WIN32)
      //
      //   APR's explode_time method does not properly calculate tm_usec
      //     prior to 1 Jan 1970 on Unix
  @@ -171,11 +181,8 @@
       CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,237"), actual);
       actual.erase(actual.begin(), actual.end());
   
  -//    gmtFormat.format(actual, ticks + 1415000, p);
  -//    Fails on both Linux and Win32
  -//    CPPUNIT_ASSERT_EQUAL((std::string) "00:00:01,415", actual);
  -#endif
  -
  +    gmtFormat.format(actual, ticks + 1423000, p);
  +    CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:01,423"), actual);
     }
   
     void assertFormattedEquals(
  @@ -189,6 +196,7 @@
           baseFormat->format(expected, date, p);
           cachedFormat.format(actual, date, p);
   
  +
           CPPUNIT_ASSERT_EQUAL(expected, actual);
     }
   
  @@ -199,7 +207,7 @@
       std::locale localeEN(LOCALE_US);
       DateFormatPtr baseFormat(
            new SimpleDateFormat(LOG4CXX_STR("EEE, MMM dd, HH:mm:ss.SSS Z"), localeEN));
  -    CachedDateFormat cachedFormat(baseFormat);
  +    CachedDateFormat cachedFormat(baseFormat, 1000000);
       //
       //   use a date in 2000 to attempt to confuse the millisecond locator
       apr_time_t ticks = MICROSECONDS_PER_DAY * 11141;
  @@ -218,10 +226,10 @@
       //   subsequent calls within one minute
       //     are optimized to reuse previous formatted value
       //     make a couple of nearly spaced calls
  -    std::locale localeJP(LOCALE_JP);
  -    DateFormatPtr baseFormat(
  -         new SimpleDateFormat(LOG4CXX_STR("EEE, MMM dd, HH:mm:ss.SSS Z"), localeJP));
  -    CachedDateFormat cachedFormat(baseFormat);
  +    LocaleChanger localeChange(LOCALE_JP);
  +    DateFormatPtr baseFormat(new SimpleDateFormat(
  +               LOG4CXX_STR("EEE, MMM dd, HH:mm:ss.SSS Z")));
  +    CachedDateFormat cachedFormat(baseFormat, 1000000);
       //
       //   use a date in 2000 to attempt to confuse the millisecond locator
       apr_time_t ticks = MICROSECONDS_PER_DAY * 11141;
  @@ -246,46 +254,339 @@
       CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("87"), numb);
     }
   
  -  /**
  -   * Attempt to cache a RelativeTimeDateFormat which isn't compatible
  -   * with caching.  Should just delegate to the RelativeTimeDateFormat.
  -   */
  -   void test7() {
  -     //   subsequent calls within one minute
  -     //     are optimized to reuse previous formatted value
  -     //     make a couple of nearly spaced calls
  -     DateFormatPtr baseFormat(new RelativeTimeDateFormat());
  -     CachedDateFormat cachedFormat(baseFormat);
  -     //
  -     //   use a date in 2000 to attempt to confuse the millisecond locator
  -     apr_time_t ticks = MICROSECONDS_PER_DAY * 11141;
  -
  -     Pool p;
  -
  -     assertFormattedEquals(baseFormat, cachedFormat, ticks, p);
  -     assertFormattedEquals(baseFormat, cachedFormat, ticks + 8000, p);
  -     assertFormattedEquals(baseFormat, cachedFormat, ticks + 17000, p);
  -     assertFormattedEquals(baseFormat, cachedFormat, ticks + 237000, p);
  -     assertFormattedEquals(baseFormat, cachedFormat, ticks + 1415000, p);
  -   }
   
     /**
      * Set time zone on cached and check that it is effective.
      */
     void test8() {
       DateFormatPtr baseFormat(new SimpleDateFormat(LOG4CXX_STR("yyyy-MM-dd HH:mm:ss,SSS")));
  -    CachedDateFormat cachedFormat(baseFormat);
  -    cachedFormat.setTimeZone(TimeZone::getTimeZone(LOG4CXX_STR("GMT-6")));
  +    baseFormat->setTimeZone(TimeZone::getGMT());
  +    CachedDateFormat cachedFormat(baseFormat, 1000000);
       apr_time_t jul4 = MICROSECONDS_PER_DAY * 12603;
   
       Pool p;
   
       LogString actual;
       cachedFormat.format(actual, jul4, p);
  +    CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("2004-07-04 00:00:00,000"), actual);
  +
  +    cachedFormat.setTimeZone(TimeZone::getTimeZone(LOG4CXX_STR("GMT-6")));
  +    actual.erase(actual.begin(), actual.end());
  +    cachedFormat.format(actual, jul4, p);
   
       CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("2004-07-03 18:00:00,000"), actual);
     }
   
  +
  +/**
  + * Test of caching when less than three millisecond digits are specified.
  + */
  +void test9() {
  +  std::locale localeUS(LOCALE_US);
  +
  +  DateFormatPtr baseFormat = new SimpleDateFormat(
  +      LOG4CXX_STR("yyyy-MMMM-dd HH:mm:ss,SS Z"), localeUS);
  +  DateFormatPtr cachedFormat = new CachedDateFormat(baseFormat, 1000000);
  +  TimeZonePtr cet = TimeZone::getTimeZone(LOG4CXX_STR("GMT+1"));
  +  cachedFormat->setTimeZone(cet);
  +
  +
  +   apr_time_exp_t c;
  +   memset(&c, 0, sizeof(c));
  +   c.tm_year = 104;
  +   c.tm_mon = 11;
  +   c.tm_mday = 12;
  +   c.tm_hour = 19;
  +   c.tm_sec = 37;
  +   c.tm_usec = 23000;
  +
  +   apr_time_t dec12;
  +   apr_status_t stat = apr_time_exp_gmt_get(&dec12, &c);
  +   const apr_status_t statOK = 0;
  +   CPPUNIT_ASSERT_EQUAL(statOK, stat);
  +
  +   Pool p;
  +
  +   LogString s;
  +   cachedFormat->format(s, dec12, p);
  +
  +   CPPUNIT_ASSERT_EQUAL(
  +       (LogString) LOG4CXX_STR("2004-December-12 20:00:37,23 +0100"), s);
  +
  +    memset(&c, 0, sizeof(c));
  +    c.tm_year = 104;
  +    c.tm_mon = 11;
  +    c.tm_mday = 31;
  +    c.tm_hour = 23;
  +    c.tm_sec = 13;
  +    c.tm_usec = 905000;
  +
  +    apr_time_t jan1;
  +    stat = apr_time_exp_gmt_get(&jan1, &c);
  +    CPPUNIT_ASSERT_EQUAL(statOK, stat);
  +
  +    s.erase(s.begin(), s.end());
  +    cachedFormat->format(s, jan1, p);
  +
  +    CPPUNIT_ASSERT_EQUAL(
  +       (LogString) LOG4CXX_STR("2005-January-01 00:00:13,905 +0100"), s);
  +}
  +
  +
  +/**
  + * Test when millisecond position moves but length remains constant.
  + */
  +void test10() {
  +  std::locale localeUS(LOCALE_US);
  +  DateFormatPtr baseFormat = new SimpleDateFormat(
  +      LOG4CXX_STR("MMMM SSS EEEEEE"), localeUS);
  +  DateFormatPtr cachedFormat = new CachedDateFormat(baseFormat, 1000000);
  +  TimeZonePtr cet = TimeZone::getTimeZone(LOG4CXX_STR("GMT+1"));
  +  cachedFormat->setTimeZone(cet);
  +
  +  apr_time_exp_t c;
  +  memset(&c, 0, sizeof(c));
  +  c.tm_year = 104;
  +  c.tm_mon = 9;
  +  c.tm_mday = 5;
  +  c.tm_hour = 21;
  +  c.tm_sec = 37;
  +  c.tm_usec = 23000;
  +
  +  apr_time_t oct5;
  +  apr_status_t stat = apr_time_exp_gmt_get(&oct5, &c);
  +  const apr_status_t statOK = 0;
  +  CPPUNIT_ASSERT_EQUAL(statOK, stat);
  +
  +  Pool p;
  +
  +  LogString s;
  +  cachedFormat->format(s, oct5, p);
  +
  +  CPPUNIT_ASSERT_EQUAL(
  +    (LogString) LOG4CXX_STR("October 023 Tuesday"), s);
  +
  +  memset(&c, 0, sizeof(c));
  +  c.tm_year = 104;
  +  c.tm_mon = 10;
  +  c.tm_mday = 1;
  +  c.tm_usec = 23000;
  +
  +  apr_time_t nov1;
  +  stat = apr_time_exp_gmt_get(&nov1, &c);
  +  CPPUNIT_ASSERT_EQUAL(statOK, stat);
  +
  +  s.erase(s.begin(), s.end());
  +  cachedFormat->format(s, nov1, p);
  +
  +  CPPUNIT_ASSERT_EQUAL(
  +     (LogString) LOG4CXX_STR("November 023 Monday"), s);
  +
  +   nov1 += 961000;
  +   s.erase(s.begin(), s.end());
  +   cachedFormat->format(s, nov1, p);
  +
  +   CPPUNIT_ASSERT_EQUAL(
  +      (LogString) LOG4CXX_STR("November 984 Monday"), s);
  +}
  +
  +/**
  + * Test that tests if caching is skipped if only "SS"
  + *     is specified.
  + */
  +void test11() {
  +   //
  +   //   Earlier versions could be tricked by "SS0" patterns.
  +   //
  +   LogString badPattern(LOG4CXX_STR("ss,SS0"));
  +   DateFormatPtr simpleFormat = new SimpleDateFormat(badPattern);
  +   DateFormatPtr gmtFormat = new CachedDateFormat(simpleFormat, 1000000);
  +   gmtFormat->setTimeZone(TimeZone::getGMT());
  +
  +   //
  +   // The first request has to 100 ms after an ordinal second
  +   //    to push the literal zero out of the pattern check
  +   apr_time_t ticks = MICROSECONDS_PER_DAY * 11142L;
  +   apr_time_t jul2 = ticks + 120000;
  +
  +   Pool p;
  +
  +   LogString s;
  +   gmtFormat->format(s, jul2, p);
  +
  +   CPPUNIT_ASSERT_EQUAL(
  +      (LogString) LOG4CXX_STR("00,1200"), s);
  +
  +   jul2 = ticks + 87000;
  +
  +   s.erase(s.begin(), s.end());
  +   gmtFormat->format(s, jul2, p);
  +
  +   CPPUNIT_ASSERT_EQUAL(
  +      (LogString) LOG4CXX_STR("00,870"), s);
  +}
  +
  +/**
  + * Check pattern location for ISO8601
  + */
  +void test12() {
  +   DateFormatPtr df = new SimpleDateFormat(LOG4CXX_STR("yyyy-MM-dd HH:mm:ss,SSS"));
  +   apr_time_t ticks = 11142L * MICROSECONDS_PER_DAY;
  +
  +   Pool p;
  +
  +   LogString formatted;
  +   df->format(formatted, ticks, p);
  +
  +   int millisecondStart = CachedDateFormat::findMillisecondStart(ticks,
  +       formatted, df, p);
  +   CPPUNIT_ASSERT_EQUAL(20, millisecondStart);
  +}
  +
  +/**
  + * Check pattern location for DATE
  + */
  +void test13() {
  +   DateFormatPtr df = new SimpleDateFormat(LOG4CXX_STR("yyyy-MM-dd"));
  +   apr_time_t ticks = 11142L * MICROSECONDS_PER_DAY;
  +
  +   Pool p;
  +
  +   LogString formatted;
  +   df->format(formatted, ticks, p);
  +
  +   int millisecondStart = CachedDateFormat::findMillisecondStart(ticks,
  +       formatted, df, p);
  +   CPPUNIT_ASSERT_EQUAL((int) CachedDateFormat::NO_MILLISECONDS, millisecondStart);
  +}
  +
  +/**
  + * Check pattern location for ABSOLUTE
  + */
  +void test14() {
  +   DateFormatPtr df = new SimpleDateFormat(LOG4CXX_STR("HH:mm:ss,SSS"));
  +   apr_time_t ticks = 11142L * MICROSECONDS_PER_DAY;
  +
  +   Pool p;
  +   LogString formatted;
  +   df->format(formatted, ticks, p);
  +
  +   int millisecondStart = CachedDateFormat::findMillisecondStart(ticks,
  +      formatted, df, p);
  +   CPPUNIT_ASSERT_EQUAL(9, millisecondStart);
  +}
  +
  +/**
  + * Check pattern location for single S
  + */
  +void test15() {
  +   DateFormatPtr df = new SimpleDateFormat(LOG4CXX_STR("HH:mm:ss,S"));
  +   apr_time_t ticks = 11142L * MICROSECONDS_PER_DAY;
  +
  +   Pool p;
  +   LogString formatted;
  +   df->format(formatted, ticks, p);
  +
  +   int millisecondStart = CachedDateFormat::findMillisecondStart(ticks,
  +      formatted, df, p);
  +   CPPUNIT_ASSERT_EQUAL((int) CachedDateFormat::UNRECOGNIZED_MILLISECONDS, millisecondStart);
  +}
  +
  +/**
  + * Check pattern location for single SS
  + */
  +void test16() {
  +   DateFormatPtr df = new SimpleDateFormat(LOG4CXX_STR("HH:mm:ss,SS"));
  +   apr_time_t ticks = 11142L * MICROSECONDS_PER_DAY;
  +
  +   Pool p;
  +   LogString formatted;
  +   df->format(formatted, ticks, p);
  +
  +   int millisecondStart =
  +      CachedDateFormat::findMillisecondStart(ticks, formatted, df, p);
  +   CPPUNIT_ASSERT_EQUAL((int) CachedDateFormat::UNRECOGNIZED_MILLISECONDS, millisecondStart);
  +}
  +
  +
  +/**
  + * Check caching when multiple SSS appear in pattern
  + */
  +void test17() {
  +    apr_time_t jul2 = 12602L * MICROSECONDS_PER_DAY;
  +    LogString badPattern(LOG4CXX_STR("HH:mm:ss,SSS HH:mm:ss,SSS"));
  +    DateFormatPtr simpleFormat = new SimpleDateFormat(badPattern);
  +    simpleFormat->setTimeZone(TimeZone::getGMT());
  +    DateFormatPtr cachedFormat = new CachedDateFormat(simpleFormat, 1000000);
  +
  +    Pool p;
  +    LogString s;
  +    cachedFormat->format(s, jul2, p);
  +
  +    CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,000 00:00:00,000"), s);
  +    jul2 += 120000;
  +
  +    s.erase(s.begin(), s.end());
  +    simpleFormat->format(s, jul2, p);
  +    CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,120 00:00:00,120"), s);
  +
  +    s.erase(s.begin(), s.end());
  +    cachedFormat->format(s, jul2, p);
  +
  +    CPPUNIT_ASSERT_EQUAL((LogString) LOG4CXX_STR("00:00:00,120 00:00:00,120"), s) ;
  +
  +    int maxValid = CachedDateFormat::getMaximumCacheValidity(badPattern);
  +    CPPUNIT_ASSERT_EQUAL(1000, maxValid);
  +}
  +
  +/**
  + * Check that patterns not containing microseconds
  + * are reported as being able to be cached for a full second.
  + */
  +void test18() {
  +
  +    int maxValid =
  +       CachedDateFormat::getMaximumCacheValidity(
  +          LOG4CXX_STR("yyyy-MM-dd"));
  +    CPPUNIT_ASSERT_EQUAL(1000000, maxValid);
  +}
  +
  +/**
  + * Check that patterns not containing 3 microseconds
  + * are reported as being able to be cached for a full second.
  + */
  +void test19() {
  +
  +    int maxValid =
  +       CachedDateFormat::getMaximumCacheValidity(
  +          LOG4CXX_STR("yyyy-MM-dd SSS"));
  +    CPPUNIT_ASSERT_EQUAL(1000000, maxValid);
  +}
  +
  +/**
  + * Check that patterns not containing 2 S's
  + * are reported as being able to be cached for only a millisecond.
  + */
  +void test20() {
  +
  +    int maxValid =
  +       CachedDateFormat::getMaximumCacheValidity(
  +          LOG4CXX_STR("yyyy-MM-dd SS"));
  +    CPPUNIT_ASSERT_EQUAL(1000, maxValid);
  +}
  +
  +/**
  + * Check that patterns not containing multi S groups
  + * are reported as being able to be cached for only a millisecond.
  + */
  +void test21() {
  +
  +    int maxValid =
  +       CachedDateFormat::getMaximumCacheValidity(
  +          LOG4CXX_STR("yyyy-MM-dd SSS SSS"));
  +    CPPUNIT_ASSERT_EQUAL(1000, maxValid);
  +}
  +
   };
   
   
  
  
  
  1.8       +2 -27     logging-log4cxx/tests/src/helpers/datetimedateformattestcase.cpp
  
  Index: datetimedateformattestcase.cpp
  ===================================================================
  RCS file: /home/cvs/logging-log4cxx/tests/src/helpers/datetimedateformattestcase.cpp,v
  retrieving revision 1.7
  retrieving revision 1.8
  diff -u -r1.7 -r1.8
  --- datetimedateformattestcase.cpp	15 Feb 2005 23:52:40 -0000	1.7
  +++ datetimedateformattestcase.cpp	19 Feb 2005 01:59:37 -0000	1.8
  @@ -21,6 +21,7 @@
   #include "../insertwide.h"
   #include <apr.h>
   #include <apr_time.h>
  +#include "localechanger.h"
   
   using namespace log4cxx;
   using namespace log4cxx::helpers;
  @@ -39,36 +40,10 @@
   #endif
   
   
  -class LocaleChanger {
  -public:
  -  LocaleChanger(const char* locale) {
  -    try {
  -        std::locale newLocale(locale);
  -        initial = std::locale::global(newLocale);
  -        effective = true;
  -    } catch(std::exception&) {
  -    }
  -  }
  -
  -  ~LocaleChanger() {
  -      if (effective) {
  -        std::locale::global(initial);
  -      }
  -  }
  -  inline bool isEffective() { return effective; }
  -
  -private:
  -  LocaleChanger(LocaleChanger&);
  -  LocaleChanger& operator=(LocaleChanger&);
  -  std::locale initial;
  -  bool effective;
  -};
  -
  -
   /**
      Unit test {@link DateTimeDateFormat}.
      @author Curt Arnold
  -   @since 1.3.0
  +   @since 0.9.8
   */
   class DateTimeDateFormatTestCase : public CppUnit::TestFixture
   {
  
  
  
  1.1                  logging-log4cxx/tests/src/helpers/localechanger.cpp
  
  Index: localechanger.cpp
  ===================================================================
  /*
   * Copyright 2004-2005 The Apache Software Foundation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #include "localechanger.h"
  
  using namespace log4cxx::helpers;
  
  /**
  *   Construction attemtps to change default locale.
  * @param locale locale.
  */
  LocaleChanger::LocaleChanger(const char* locale) {
      try {
          std::locale newLocale(locale);
          initial = std::locale::global(newLocale);
          effective = true;
      } catch(std::exception&) {
      }
    }
  
  /**
  * Restores previous locale.
  */
  LocaleChanger::~LocaleChanger() {
        if (effective) {
          std::locale::global(initial);
        }
    }
  
  
  
  
  1.1                  logging-log4cxx/tests/src/helpers/localechanger.h
  
  Index: localechanger.h
  ===================================================================
  /*
   * Copyright 2004-2005 The Apache Software Foundation.
   *
   * Licensed under the Apache License, Version 2.0 (the "License");
   * you may not use this file except in compliance with the License.
   * You may obtain a copy of the License at
   *
   *      http://www.apache.org/licenses/LICENSE-2.0
   *
   * Unless required by applicable law or agreed to in writing, software
   * distributed under the License is distributed on an "AS IS" BASIS,
   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   * See the License for the specific language governing permissions and
   * limitations under the License.
   */
  
  #ifndef _LOG4CXX_HELPERS_LOCALE_CHANGER_H
  #define _LOG4CXX_HELPERS_LOCALE_CHANGER_H
  
  #include <locale>
  
  namespace log4cxx {
    namespace helpers {
      /**
      *   Utility class to change the locale for the duration of a test.
      *
      * @author Curt Arnold
      * @since 0.9.8
      *
      */
      class LocaleChanger {
      public:
      /**
      *   Construction attemtps to change default locale.
      * @param locale locale.
      */
         LocaleChanger(const char* locale);
  
      /**
      * Restores previous locale.
      */
        ~LocaleChanger();
  
        /**
        * Determines whether locale change was effective.
        * @return true if effective.
        */
        inline bool isEffective() { return effective; }
  
      private:
        LocaleChanger(LocaleChanger&);
        LocaleChanger& operator=(LocaleChanger&);
        std::locale initial;
        bool effective;
      };
    }
  }
  #endif