$include_dir="/home/hyper-archives/boost-commit/include"; include("$include_dir/msg-header.inc") ?>
Subject: [Boost-commit] svn:boost r49893 - in branches/release: boost/date_time boost/date_time/gregorian libs/date_time/src/gregorian libs/date_time/test/gregorian libs/date_time/test/posix_time libs/date_time/xmldoc
From: andrey.semashev_at_[hidden]
Date: 2008-11-23 06:13:36
Author: andysem
Date: 2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
New Revision: 49893
URL: http://svn.boost.org/trac/boost/changeset/49893
Log:
Merged fixes from trunk: tickets 2521, 2523.
Text files modified: 
   branches/release/boost/date_time/date_parsing.hpp                          |    15 ++--                                    
   branches/release/boost/date_time/filetime_functions.hpp                    |   112 ++++++++++++++++++++++----------------- 
   branches/release/boost/date_time/gregorian/greg_facet.hpp                  |    14 +++-                                    
   branches/release/boost/date_time/microsec_time_clock.hpp                   |     9 +-                                      
   branches/release/boost/date_time/time_facet.hpp                            |     4                                         
   branches/release/libs/date_time/src/gregorian/greg_month.cpp               |     2                                         
   branches/release/libs/date_time/test/gregorian/testdate_input_facet.cpp    |     6 ++                                      
   branches/release/libs/date_time/test/posix_time/testfiletime_functions.cpp |    62 ++++++++++++++++-----                   
   branches/release/libs/date_time/test/posix_time/testtime_facet.cpp         |    26 +++++++++                               
   branches/release/libs/date_time/xmldoc/changes.xml                         |    20 ++++++                                  
   10 files changed, 186 insertions(+), 84 deletions(-)
Modified: branches/release/boost/date_time/date_parsing.hpp
==============================================================================
--- branches/release/boost/date_time/date_parsing.hpp	(original)
+++ branches/release/boost/date_time/date_parsing.hpp	2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
@@ -74,22 +74,23 @@
     }
  
     //! Find index of a string in either of 2 arrays
-    /*! find_match searches both arrays for a match to 's'. Indexing of the 
-     * arrays is from 0 to 'limit'. The index of the match is returned.
+    /*! find_match searches both arrays for a match to 's'. Both arrays
+     * must contain 'size' elements. The index of the match is returned.
+     * If no match is found, 'size' is returned.
      * Ex. "Jan" returns 0, "Dec" returns 11, "Tue" returns 2.
-     * 'limit' can be sent in with: greg_month::max(), 
-     * greg_weekday::max() or date_time::NumSpecialValues */
+     * 'size' can be sent in with: greg_month::max() (which 12),
+     * greg_weekday::max() + 1 (which is 7) or date_time::NumSpecialValues */
     template<class charT>
     short find_match(const charT* const* short_names, 
                      const charT* const* long_names, 
-                     short limit,
+                     short size,
                      const std::basic_string<charT>& s) {
-      for(short i = 0; i <= limit; ++i){
+      for(short i = 0; i < size; ++i){
         if(short_names[i] == s || long_names[i] == s){
           return i;
         }
       }
-      return static_cast<short>(limit + 1); // not-found, return a value out of range
+      return size; // not-found, return a value out of range
     }
     
     //! Generic function to parse a delimited date (eg: 2002-02-10)
Modified: branches/release/boost/date_time/filetime_functions.hpp
==============================================================================
--- branches/release/boost/date_time/filetime_functions.hpp	(original)
+++ branches/release/boost/date_time/filetime_functions.hpp	2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
@@ -27,37 +27,38 @@
 #include <boost/date_time/time.hpp>
 
 namespace boost {
+
 namespace date_time {
 
 namespace winapi {
 
 #if !defined(BOOST_USE_WINDOWS_H)
 
-extern "C" {
+    extern "C" {
 
-    struct FILETIME
-    {
-        boost::uint32_t dwLowDateTime;
-        boost::uint32_t dwHighDateTime;
-    };
-    struct SYSTEMTIME
-    {
-        boost::uint16_t wYear;
-        boost::uint16_t wMonth;
-        boost::uint16_t wDayOfWeek;
-        boost::uint16_t wDay;
-        boost::uint16_t wHour;
-        boost::uint16_t wMinute;
-        boost::uint16_t wSecond;
-        boost::uint16_t wMilliseconds; 
-    };
-
-    __declspec(dllimport) void __stdcall GetSystemTimeAsFileTime(FILETIME* lpFileTime); 
-    __declspec(dllimport) int __stdcall FileTimeToLocalFileTime(const FILETIME* lpFileTime, FILETIME* lpLocalFileTime); 
-    __declspec(dllimport) void __stdcall GetSystemTime(SYSTEMTIME* lpSystemTime); 
-    __declspec(dllimport) int __stdcall SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime, FILETIME* lpFileTime); 
+        struct FILETIME
+        {
+            boost::uint32_t dwLowDateTime;
+            boost::uint32_t dwHighDateTime;
+        };
+        struct SYSTEMTIME
+        {
+            boost::uint16_t wYear;
+            boost::uint16_t wMonth;
+            boost::uint16_t wDayOfWeek;
+            boost::uint16_t wDay;
+            boost::uint16_t wHour;
+            boost::uint16_t wMinute;
+            boost::uint16_t wSecond;
+            boost::uint16_t wMilliseconds; 
+        };
+
+        __declspec(dllimport) void __stdcall GetSystemTimeAsFileTime(FILETIME* lpFileTime); 
+        __declspec(dllimport) int __stdcall FileTimeToLocalFileTime(const FILETIME* lpFileTime, FILETIME* lpLocalFileTime); 
+        __declspec(dllimport) void __stdcall GetSystemTime(SYSTEMTIME* lpSystemTime); 
+        __declspec(dllimport) int __stdcall SystemTimeToFileTime(const SYSTEMTIME* lpSystemTime, FILETIME* lpFileTime); 
 
-} // extern "C"
+    } // extern "C"
 
 #endif // defined(BOOST_USE_WINDOWS_H)
 
@@ -81,16 +82,18 @@
     }
 
     /*!
-     * The function converts file_time into number of nanoseconds elapsed since 1970-Jan-01
+     * The function converts file_time into number of microseconds elapsed since 1970-Jan-01
+     *
+     * \note Only dates after 1970-Jan-01 are supported. Dates before will be wrapped.
      *
      * \note The function is templated on the FILETIME type, so that
      *       it can be used with both native FILETIME and the ad-hoc
      *       boost::date_time::winapi::file_time type.
      */
     template< typename FileTimeT >
-    inline boost::uint64_t file_time_to_nanoseconds(FileTimeT const& ft)
+    inline boost::uint64_t file_time_to_microseconds(FileTimeT const& ft)
     {
-        /* shift is difference between 1970-Jan-01 & 1601-Jan-01 
+        /* shift is difference between 1970-Jan-01 & 1601-Jan-01
         * in 100-nanosecond intervals */
         const uint64_t c1 = 27111902UL;
         const uint64_t c2 = 3577643008UL; // issues warning without 'UL'
@@ -98,41 +101,52 @@
 
         union {
             FileTimeT as_file_time;
-            uint64_t as_integer;
+            uint64_t as_integer; // 100-nanos since 1601-Jan-01
         } caster;
         caster.as_file_time = ft;
 
         caster.as_integer -= shift; // filetime is now 100-nanos since 1970-Jan-01
-        return (caster.as_integer * 100); // upscale to nanoseconds
+        return (caster.as_integer / 10); // truncate to microseconds
     }
 
 } // namespace winapi
 
-  //! Create a time object from an initialized FILETIME struct.
-  /*!
-   * Create a time object from an initialized FILETIME struct.
-   * A FILETIME struct holds 100-nanosecond units (0.0000001). When 
-   * built with microsecond resolution the file_time's sub second value 
-   * will be truncated. Nanosecond resolution has no truncation.
-   *
-   * \note The function is templated on the FILETIME type, so that
-   *       it can be used with both native FILETIME and the ad-hoc
-   *       boost::date_time::winapi::file_time type.
-   */
-  template< typename TimeT, typename FileTimeT >
-  inline
-  TimeT time_from_ftime(const FileTimeT& ft)
-  {
+//! Create a time object from an initialized FILETIME struct.
+/*!
+ * Create a time object from an initialized FILETIME struct.
+ * A FILETIME struct holds 100-nanosecond units (0.0000001). When 
+ * built with microsecond resolution the file_time's sub second value 
+ * will be truncated. Nanosecond resolution has no truncation.
+ *
+ * \note The function is templated on the FILETIME type, so that
+ *       it can be used with both native FILETIME and the ad-hoc
+ *       boost::date_time::winapi::file_time type.
+ */
+template< typename TimeT, typename FileTimeT >
+inline
+TimeT time_from_ftime(const FileTimeT& ft)
+{
     typedef typename TimeT::date_type date_type;
     typedef typename TimeT::date_duration_type date_duration_type;
     typedef typename TimeT::time_duration_type time_duration_type;
 
-    uint64_t nanos = winapi::file_time_to_nanoseconds(ft);
+    // https://svn.boost.org/trac/boost/ticket/2523
+    // Since this function can be called with arbitrary times, including ones that
+    // are before 1970-Jan-01, we'll have to cast the time a bit differently,
+    // than it is done in the file_time_to_microseconds function. This allows to
+    // avoid integer wrapping for dates before 1970-Jan-01.
+    union {
+        FileTimeT as_file_time;
+        uint64_t as_integer; // 100-nanos since 1601-Jan-01
+    } caster;
+    caster.as_file_time = ft;
 
-    uint64_t sec = nanos / 1000000000UL;
-    uint32_t sub_sec = (nanos % 1000000000UL); // nanoseconds since the last second
+    uint64_t sec = caster.as_integer / 10000000UL;
+    uint32_t sub_sec = (caster.as_integer % 10000000UL) // 100-nanoseconds since the last second
 #if !defined(BOOST_DATE_TIME_POSIX_TIME_STD_CONFIG)
-    sub_sec /= 1000; // truncate to microseconds
+        / 10; // microseconds since the last second
+#else
+        * 100; // nanoseconds since the last second
 #endif
 
     // split sec into usable chunks: days, hours, minutes, & seconds
@@ -146,9 +160,9 @@
     uint32_t seconds = tmp; // seconds
 
     date_duration_type dd(days);
-    date_type d = date_type(1970, Jan, 01) + dd;
+    date_type d = date_type(1601, Jan, 01) + dd;
     return TimeT(d, time_duration_type(hours, minutes, seconds, sub_sec));
-  }
+}
 
 }} // boost::date_time
 
Modified: branches/release/boost/date_time/gregorian/greg_facet.hpp
==============================================================================
--- branches/release/boost/date_time/gregorian/greg_facet.hpp	(original)
+++ branches/release/boost/date_time/gregorian/greg_facet.hpp	2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
@@ -284,7 +284,8 @@
       const facet_def& f = std::use_facet<facet_def>(is.getloc());
       num = date_time::find_match(f.get_short_month_names(), 
                                   f.get_long_month_names(), 
-                                  (greg_month::max)(), s); 
+                                  (greg_month::max)(), s); // greg_month spans 1..12, so max returns the array size,
+                                                           // which is needed by find_match
     }
     /* bad_cast will be thrown if the desired facet is not accessible
      * so we can generate the facet. This has the drawback of using english
@@ -294,10 +295,11 @@
       std::auto_ptr< const facet_def > f(create_facet_def(a));
       num = date_time::find_match(f->get_short_month_names(), 
                                   f->get_long_month_names(), 
-                                  (greg_month::max)(), s); 
+                                  (greg_month::max)(), s); // greg_month spans 1..12, so max returns the array size,
+                                                           // which is needed by find_match
     }
     
-    num += 1; // months numbered 1-12
+    ++num; // months numbered 1-12
     m = greg_month(num); 
 
     return is;
@@ -324,7 +326,8 @@
       const facet_def& f = std::use_facet<facet_def>(is.getloc());
       num = date_time::find_match(f.get_short_weekday_names(), 
                                   f.get_long_weekday_names(), 
-                                  (greg_weekday::max)(), s); 
+                                  (greg_weekday::max)() + 1, s); // greg_weekday spans 0..6, so increment is needed
+                                                                 // to form the array size which is needed by find_match
     }
     /* bad_cast will be thrown if the desired facet is not accessible
      * so we can generate the facet. This has the drawback of using english
@@ -334,7 +337,8 @@
       std::auto_ptr< const facet_def > f(create_facet_def(a));
       num = date_time::find_match(f->get_short_weekday_names(), 
                                   f->get_long_weekday_names(), 
-                                  (greg_weekday::max)(), s); 
+                                  (greg_weekday::max)() + 1, s); // greg_weekday spans 0..6, so increment is needed
+                                                                 // to form the array size which is needed by find_match
     }
    
     wd = greg_weekday(num); // weekdays numbered 0-6
Modified: branches/release/boost/date_time/microsec_time_clock.hpp
==============================================================================
--- branches/release/boost/date_time/microsec_time_clock.hpp	(original)
+++ branches/release/boost/date_time/microsec_time_clock.hpp	2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
@@ -87,10 +87,11 @@
 #elif defined(BOOST_HAS_FTIME)
       winapi::file_time ft;
       winapi::get_system_time_as_file_time(ft);
-      uint64_t nanos = winapi::file_time_to_nanoseconds(ft);
-      std::time_t t = static_cast<time_t>(nanos / 1000000000UL); // seconds since epoch
+      uint64_t micros = winapi::file_time_to_microseconds(ft); // it will not wrap, since ft is the current time
+                                                               // and cannot be before 1970-Jan-01
+      std::time_t t = static_cast<time_t>(micros / 1000000UL); // seconds since epoch
       // microseconds -- static casts supress warnings
-      boost::uint32_t sub_sec = static_cast<boost::uint32_t>((nanos % 1000000000UL) / 1000UL);
+      boost::uint32_t sub_sec = static_cast<boost::uint32_t>(micros % 1000000UL);
 #else
 #error Internal Boost.DateTime error: BOOST_DATE_TIME_HAS_HIGH_PRECISION_CLOCK is defined, however neither gettimeofday nor FILETIME support is detected.
 #endif
@@ -101,7 +102,7 @@
                   curr_ptr->tm_mon + 1,
                   curr_ptr->tm_mday);
 
-      //The following line will adjusts the fractional second tick in terms
+      //The following line will adjust the fractional second tick in terms
       //of the current time system.  For example, if the time system
       //doesn't support fractional seconds then res_adjust returns 0
       //and all the fractional seconds return 0.
Modified: branches/release/boost/date_time/time_facet.hpp
==============================================================================
--- branches/release/boost/date_time/time_facet.hpp	(original)
+++ branches/release/boost/date_time/time_facet.hpp	2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
@@ -18,6 +18,7 @@
 #include <iomanip>
 #include <iterator> // i/ostreambuf_iterator
 #include <exception>
+#include <boost/assert.hpp>
 #include <boost/lexical_cast.hpp>
 #include <boost/throw_exception.hpp>
 #include <boost/algorithm/string/erase.hpp>
@@ -445,8 +446,7 @@
       if (format.find(hours_format) != string_type::npos) {
         if (hours_str.empty())
           hours_str = hours_as_string(a_time_dur);
-        if (hours_str.length() > 2)
-          hours_str.erase(0, hours_str.length() - 2);
+        BOOST_ASSERT(hours_str.length() <= 2);
         boost::algorithm::replace_all(format, hours_format, hours_str);
       }
 
Modified: branches/release/libs/date_time/src/gregorian/greg_month.cpp
==============================================================================
--- branches/release/libs/date_time/src/gregorian/greg_month.cpp	(original)
+++ branches/release/libs/date_time/src/gregorian/greg_month.cpp	2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
@@ -75,7 +75,7 @@
                                     special_value_names,
                                     date_time::NumSpecialValues,
                                     s);
-    if(i > date_time::NumSpecialValues) { // match not found
+    if(i >= date_time::NumSpecialValues) { // match not found
       return not_special;
     }
     else {
Modified: branches/release/libs/date_time/test/gregorian/testdate_input_facet.cpp
==============================================================================
--- branches/release/libs/date_time/test/gregorian/testdate_input_facet.cpp	(original)
+++ branches/release/libs/date_time/test/gregorian/testdate_input_facet.cpp	2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
@@ -399,6 +399,12 @@
   check("Special values, default strings, not_a_date_time date",
       d == date(not_a_date_time));
 
+  // in addition check that special_value_from_string also works correctly for other special values
+  check("Special values, default strings, not_special test",
+      special_value_from_string("not_special") == not_special);
+  check("Special values, default strings, junk test",
+      special_value_from_string("junk") == not_special);
+
   // special values custom, strings
   special_values_parser svp("NADT", "MINF", "INF", "MINDT", "MAXDT");
   facet->special_values_parser(svp);
Modified: branches/release/libs/date_time/test/posix_time/testfiletime_functions.cpp
==============================================================================
--- branches/release/libs/date_time/test/posix_time/testfiletime_functions.cpp	(original)
+++ branches/release/libs/date_time/test/posix_time/testfiletime_functions.cpp	2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
@@ -14,6 +14,17 @@
 #include <windows.h>
 #endif
 
+template< typename T, typename U >
+inline bool check_equal(const std::string& testname, T const& left, U const& right)
+{
+  bool res = check(testname, left == right);
+  if (!res)
+  {
+    std::cout << "    left = " << left << ", right = " << right << std::endl;
+  }
+  return res;
+}
+
 int main() 
 {
 #if defined(BOOST_HAS_FTIME) // skip tests if no FILETIME
@@ -33,21 +44,20 @@
 
     ptime pt = from_ftime<ptime>(ft);
 
-    check("ptime year matches systemtime year", 
-        st.wYear == pt.date().year());
-    check("ptime month matches systemtime month", 
-        st.wMonth == pt.date().month());
-    check("ptime day matches systemtime day", 
-        st.wDay == pt.date().day());
-    check("ptime hour matches systemtime hour", 
-        st.wHour == pt.time_of_day().hours());
-    check("ptime minute matches systemtime minute", 
-        st.wMinute == pt.time_of_day().minutes());
-    check("ptime second matches systemtime second", 
-        st.wSecond == pt.time_of_day().seconds());
-    check("truncated ptime fractional second matches systemtime millisecond", 
-        st.wMilliseconds == (pt.time_of_day().fractional_seconds() / adjustor)
-         );
+    check_equal("ptime year matches systemtime year", 
+        st.wYear, pt.date().year());
+    check_equal("ptime month matches systemtime month", 
+        st.wMonth, pt.date().month());
+    check_equal("ptime day matches systemtime day", 
+        st.wDay, pt.date().day());
+    check_equal("ptime hour matches systemtime hour", 
+        st.wHour, pt.time_of_day().hours());
+    check_equal("ptime minute matches systemtime minute", 
+        st.wMinute, pt.time_of_day().minutes());
+    check_equal("ptime second matches systemtime second", 
+        st.wSecond, pt.time_of_day().seconds());
+    check_equal("truncated ptime fractional second matches systemtime millisecond", 
+        st.wMilliseconds, (pt.time_of_day().fractional_seconds() / adjustor));
 
     // burn up a little time
     for (int j=0; j<100000; j++)
@@ -58,6 +68,28 @@
 
   } // for loop
 
+  // check that time_from_ftime works for pre-1970-Jan-01 dates, too
+  // zero FILETIME should represent 1601-Jan-01 00:00:00.000
+  FILETIME big_bang_by_ms;
+  big_bang_by_ms.dwLowDateTime = big_bang_by_ms.dwHighDateTime = 0;
+  ptime pt = from_ftime<ptime>(big_bang_by_ms);
+
+  check_equal("big bang ptime year matches 1601", 
+      1601, pt.date().year());
+  check_equal("big bang ptime month matches Jan", 
+      1, pt.date().month());
+  check_equal("big bang ptime day matches 1", 
+      1, pt.date().day());
+  check_equal("big bang ptime hour matches 0", 
+      0, pt.time_of_day().hours());
+  check_equal("big bang ptime minute matches 0", 
+      0, pt.time_of_day().minutes());
+  check_equal("big bang ptime second matches 0", 
+      0, pt.time_of_day().seconds());
+  check_equal("big bang truncated ptime fractional second matches 0", 
+      0, (pt.time_of_day().fractional_seconds() / adjustor));
+
+
 #else // BOOST_HAS_FTIME
   // we don't want a forced failure here, not a shortcoming
   check("FILETIME not available for this compiler/platform", true);
Modified: branches/release/libs/date_time/test/posix_time/testtime_facet.cpp
==============================================================================
--- branches/release/libs/date_time/test/posix_time/testtime_facet.cpp	(original)
+++ branches/release/libs/date_time/test/posix_time/testtime_facet.cpp	2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
@@ -54,6 +54,8 @@
     ptime tf = t + microseconds(3);
     time_period tp(t, tf + days(7) + time_duration(1,1,1));
     time_duration td = hours(3) + minutes(2) + seconds(1) + milliseconds(9);
+    time_duration longer_td = hours(10) + minutes(22) + seconds(15) + milliseconds(980); // two characters in hours
+    time_duration long_td = hours(300) + minutes(2) + seconds(1) + milliseconds(9); // more than two characters in hours
     {
       std::stringstream ss;
       ss << t;
@@ -94,6 +96,18 @@
       ss << td;
       check("Multiple literal '%'s in time_duration format", ss.str() == std::string("03:02:01 %01"));
       ss.str("");
+
+      // Longer time durations
+      f->time_duration_format("%H:%M:%S");
+      ss << longer_td;
+      check("Longer time durations", ss.str() == std::string("10:22:15"));
+      ss.str("");
+
+      // Long time durations
+      f->time_duration_format("%O:%M:%S");
+      ss << long_td;
+      check("Long time durations", ss.str() == std::string("300:02:01"));
+      ss.str("");
     }
     { // negative time_duration tests
       std::string result;
@@ -168,7 +182,19 @@
       ss << td4 - td3;
       result = "-00 hours and -01 minutes";
       check("Negative time_duration two sign flags" + ss.str(), result == ss.str());
+      ss.str("");
+
+      // Longer time durations
+      f->time_duration_format("%-%H:%M:%S");
+      ss << -longer_td;
+      check("Longer negative time durations", ss.str() == std::string("-10:22:15"));
+      ss.str("");
 
+      // Long time durations
+      f->time_duration_format("%-%O:%M:%S");
+      ss << -long_td;
+      check("Long negative time durations", ss.str() == std::string("-300:02:01"));
+      ss.str("");
     }
       
 #if !defined(BOOST_NO_STD_WSTRING) 
Modified: branches/release/libs/date_time/xmldoc/changes.xml
==============================================================================
--- branches/release/libs/date_time/xmldoc/changes.xml	(original)
+++ branches/release/libs/date_time/xmldoc/changes.xml	2008-11-23 06:13:35 EST (Sun, 23 Nov 2008)
@@ -29,7 +29,9 @@
             A new formatter <code>%O</code> is used indicate such long durations in the
             format string. The old <code>%H</code> format specifier is thus restricted
             to represent durations that fit into two characters, in order to retain support
-            for reading durations in ISO format.
+            for reading durations in ISO format. In case if it is detected that the <code>%H</code>
+            format specifier is used with longer durations, the results are not specified
+            (an assertion in debug builds is raised).
           </entry>
         </row>
 
@@ -93,6 +95,22 @@
         </row>
 
         <row>
+          <entry>Bug fix</entry>
+          <entry>
+            On Windows platform function <code>from_ftime</code> could return incorrect time if
+            the <code>FILETIME</code> that is being passed to the function contained dates before 1970-Jan-01.
+          </entry>
+        </row>
+
+        <row>
+          <entry>Bug fix</entry>
+          <entry>
+            Fixed a possible crash in <code>gregorian::special_value_from_string</code> if the string
+            did not represent a valid special value.
+          </entry>
+        </row>
+
+        <row>
           <entry>Bug Fix</entry>
           <entry>
           </entry>