Boost logo

Boost-Commit :

Subject: [Boost-commit] svn:boost r49874 - in trunk: boost/date_time libs/date_time/test/posix_time libs/date_time/xmldoc
From: andrey.semashev_at_[hidden]
Date: 2008-11-22 07:49:21


Author: andysem
Date: 2008-11-22 07:49:20 EST (Sat, 22 Nov 2008)
New Revision: 49874
URL: http://svn.boost.org/trac/boost/changeset/49874

Log:
In FILETIME support functions fixed a possible integer wrapping that would cause to invalid conversion of FILETIME before 1970-01-01 into a DateTime time. In time facet changed the reaction on a too long hours string to an assert rather than truncating the string. Updated docs and tests to reflect these changes.
Text files modified:
   trunk/boost/date_time/filetime_functions.hpp | 112 ++++++++++++++++++++++-----------------
   trunk/boost/date_time/microsec_time_clock.hpp | 9 +-
   trunk/boost/date_time/time_facet.hpp | 4
   trunk/libs/date_time/test/posix_time/testfiletime_functions.cpp | 62 ++++++++++++++++-----
   trunk/libs/date_time/test/posix_time/testtime_facet.cpp | 26 +++++++++
   trunk/libs/date_time/xmldoc/changes.xml | 12 +++
   6 files changed, 154 insertions(+), 71 deletions(-)

Modified: trunk/boost/date_time/filetime_functions.hpp
==============================================================================
--- trunk/boost/date_time/filetime_functions.hpp (original)
+++ trunk/boost/date_time/filetime_functions.hpp 2008-11-22 07:49:20 EST (Sat, 22 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: trunk/boost/date_time/microsec_time_clock.hpp
==============================================================================
--- trunk/boost/date_time/microsec_time_clock.hpp (original)
+++ trunk/boost/date_time/microsec_time_clock.hpp 2008-11-22 07:49:20 EST (Sat, 22 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: trunk/boost/date_time/time_facet.hpp
==============================================================================
--- trunk/boost/date_time/time_facet.hpp (original)
+++ trunk/boost/date_time/time_facet.hpp 2008-11-22 07:49:20 EST (Sat, 22 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: trunk/libs/date_time/test/posix_time/testfiletime_functions.cpp
==============================================================================
--- trunk/libs/date_time/test/posix_time/testfiletime_functions.cpp (original)
+++ trunk/libs/date_time/test/posix_time/testfiletime_functions.cpp 2008-11-22 07:49:20 EST (Sat, 22 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: trunk/libs/date_time/test/posix_time/testtime_facet.cpp
==============================================================================
--- trunk/libs/date_time/test/posix_time/testtime_facet.cpp (original)
+++ trunk/libs/date_time/test/posix_time/testtime_facet.cpp 2008-11-22 07:49:20 EST (Sat, 22 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: trunk/libs/date_time/xmldoc/changes.xml
==============================================================================
--- trunk/libs/date_time/xmldoc/changes.xml (original)
+++ trunk/libs/date_time/xmldoc/changes.xml 2008-11-22 07:49:20 EST (Sat, 22 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,14 @@
         </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>
           </entry>


Boost-Commit list run by bdawes at acm.org, david.abrahams at rcn.com, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk