aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJack Lloyd <[email protected]>2017-12-04 16:58:19 -0500
committerJack Lloyd <[email protected]>2017-12-04 16:58:19 -0500
commitf82ee841f719926e91eb4a5533c964821f08488d (patch)
tree21c5924b896caa716e9a690a6cc2463bbede82ea
parente5f39dd483a08accc8a12e8b322a48037c5b3bf4 (diff)
Simplify date conversion by avoiding OS utilities
We have to rely on non-portable OS calls to convert UTC times, and they are not available on many systems (including Solaris and MinGW). But instead there is a simple algorithm due to Howard Hinnant that does the same job. Woo.
-rw-r--r--doc/manual/deprecated.rst2
-rw-r--r--src/build-data/os/cygwin.txt1
-rw-r--r--src/build-data/os/darwin.txt1
-rw-r--r--src/build-data/os/freebsd.txt1
-rw-r--r--src/build-data/os/ios.txt1
-rw-r--r--src/build-data/os/linux.txt1
-rw-r--r--src/build-data/os/mingw.txt1
-rw-r--r--src/build-data/os/netbsd.txt1
-rw-r--r--src/build-data/os/openbsd.txt1
-rw-r--r--src/build-data/os/windows.txt1
-rw-r--r--src/build-data/os/winphone.txt1
-rw-r--r--src/lib/asn1/asn1_time.cpp12
-rw-r--r--src/lib/utils/boost/info.txt1
-rw-r--r--src/lib/utils/calendar.cpp139
-rw-r--r--src/lib/utils/calendar.h26
-rw-r--r--src/tests/test_utils.cpp28
16 files changed, 77 insertions, 141 deletions
diff --git a/doc/manual/deprecated.rst b/doc/manual/deprecated.rst
index 8cfe7e590..a7dc4bf12 100644
--- a/doc/manual/deprecated.rst
+++ b/doc/manual/deprecated.rst
@@ -55,3 +55,5 @@ in the source.
- All built in MODP groups < 2048 bits
- All pre-created DSA groups
+
+- Directly accessing the member variables of calendar_point
diff --git a/src/build-data/os/cygwin.txt b/src/build-data/os/cygwin.txt
index a1234dc13..06f6d60f8 100644
--- a/src/build-data/os/cygwin.txt
+++ b/src/build-data/os/cygwin.txt
@@ -12,7 +12,6 @@ doc_dir docs
<target_features>
gettimeofday
-timegm
readdir
threads
filesystem
diff --git a/src/build-data/os/darwin.txt b/src/build-data/os/darwin.txt
index 41716e21a..6568a9783 100644
--- a/src/build-data/os/darwin.txt
+++ b/src/build-data/os/darwin.txt
@@ -20,7 +20,6 @@ memset_s
readdir
sockets
threads
-timegm
</target_features>
<aliases>
diff --git a/src/build-data/os/freebsd.txt b/src/build-data/os/freebsd.txt
index 0e60abd2e..fb869dfd0 100644
--- a/src/build-data/os/freebsd.txt
+++ b/src/build-data/os/freebsd.txt
@@ -9,7 +9,6 @@ posix_mlock
gmtime_r
dlopen
readdir
-timegm
sockets
threads
filesystem
diff --git a/src/build-data/os/ios.txt b/src/build-data/os/ios.txt
index ddd283cd9..96ac73721 100644
--- a/src/build-data/os/ios.txt
+++ b/src/build-data/os/ios.txt
@@ -16,7 +16,6 @@ memset_s
readdir
sockets
threads
-timegm
</target_features>
<aliases>
diff --git a/src/build-data/os/linux.txt b/src/build-data/os/linux.txt
index 59f995fc2..80ea3e63a 100644
--- a/src/build-data/os/linux.txt
+++ b/src/build-data/os/linux.txt
@@ -10,7 +10,6 @@ gmtime_r
dlopen
getauxval
readdir
-timegm
sockets
threads
filesystem
diff --git a/src/build-data/os/mingw.txt b/src/build-data/os/mingw.txt
index bf4333ec0..8d4e94249 100644
--- a/src/build-data/os/mingw.txt
+++ b/src/build-data/os/mingw.txt
@@ -19,7 +19,6 @@ mingw32
<target_features>
cryptgenrandom
loadlibrary
-mkgmtime
win32_virtual_lock
win32_get_systemtime
threads
diff --git a/src/build-data/os/netbsd.txt b/src/build-data/os/netbsd.txt
index 4c82e6239..50d97fd8f 100644
--- a/src/build-data/os/netbsd.txt
+++ b/src/build-data/os/netbsd.txt
@@ -9,7 +9,6 @@ posix_mlock
gmtime_r
dlopen
readdir
-timegm
threads
filesystem
</target_features>
diff --git a/src/build-data/os/openbsd.txt b/src/build-data/os/openbsd.txt
index 5c7ff3259..b858d497c 100644
--- a/src/build-data/os/openbsd.txt
+++ b/src/build-data/os/openbsd.txt
@@ -13,7 +13,6 @@ posix_mlock
gmtime_r
dlopen
readdir
-timegm
sockets
threads
filesystem
diff --git a/src/build-data/os/windows.txt b/src/build-data/os/windows.txt
index 898dfc65f..639b0512d 100644
--- a/src/build-data/os/windows.txt
+++ b/src/build-data/os/windows.txt
@@ -20,7 +20,6 @@ doc_dir docs
cryptgenrandom
gmtime_s
loadlibrary
-mkgmtime
query_perf_counter
virtual_lock
rtlsecurezeromemory
diff --git a/src/build-data/os/winphone.txt b/src/build-data/os/winphone.txt
index bedcc9d2c..0b0318fed 100644
--- a/src/build-data/os/winphone.txt
+++ b/src/build-data/os/winphone.txt
@@ -13,7 +13,6 @@ doc_dir docs
crypto_ng
gmtime_s
loadlibrary
-mkgmtime
query_perf_counter
rtlsecurezeromemory
#stl_filesystem_msvc
diff --git a/src/lib/asn1/asn1_time.cpp b/src/lib/asn1/asn1_time.cpp
index f6a0c414e..daa0c4e7d 100644
--- a/src/lib/asn1/asn1_time.cpp
+++ b/src/lib/asn1/asn1_time.cpp
@@ -20,12 +20,12 @@ X509_Time::X509_Time(const std::chrono::system_clock::time_point& time)
{
calendar_point cal = calendar_value(time);
- m_year = cal.year;
- m_month = cal.month;
- m_day = cal.day;
- m_hour = cal.hour;
- m_minute = cal.minutes;
- m_second = cal.seconds;
+ m_year = cal.get_year();
+ m_month = cal.get_month();
+ m_day = cal.get_day();
+ m_hour = cal.get_hour();
+ m_minute = cal.get_minutes();
+ m_second = cal.get_seconds();
m_tag = (m_year >= 2050) ? GENERALIZED_TIME : UTC_TIME;
}
diff --git a/src/lib/utils/boost/info.txt b/src/lib/utils/boost/info.txt
index c12b99a28..d54421147 100644
--- a/src/lib/utils/boost/info.txt
+++ b/src/lib/utils/boost/info.txt
@@ -1,7 +1,6 @@
<defines>
BOOST_FILESYSTEM -> 20131228
BOOST_ASIO -> 20131228
-BOOST_DATETIME -> 20150720
</defines>
load_on vendor
diff --git a/src/lib/utils/calendar.cpp b/src/lib/utils/calendar.cpp
index db933e648..d39e52823 100644
--- a/src/lib/utils/calendar.cpp
+++ b/src/lib/utils/calendar.cpp
@@ -1,6 +1,6 @@
/*
* Calendar Functions
-* (C) 1999-2010 Jack Lloyd
+* (C) 1999-2010,2017 Jack Lloyd
* (C) 2015 Simon Warta (Kullo GmbH)
*
* Botan is released under the Simplified BSD License (see license.txt)
@@ -11,13 +11,8 @@
#include <ctime>
#include <sstream>
#include <iomanip>
-#include <botan/mutex.h>
#include <stdlib.h>
-#if defined(BOTAN_HAS_BOOST_DATETIME)
-#include <boost/date_time/posix_time/posix_time_types.hpp>
-#endif
-
namespace Botan {
namespace {
@@ -40,119 +35,57 @@ std::tm do_gmtime(std::time_t time_val)
return tm;
}
-#if !defined(BOTAN_TARGET_OS_HAS_TIMEGM) && !(defined(BOTAN_TARGET_OS_HAS_MKGMTIME) && defined(BOTAN_BUILD_COMPILER_IS_MSVC))
+/*
+Portable replacement for timegm, _mkgmtime, etc
-#if defined(BOTAN_HAS_BOOST_DATETIME)
+Algorithm due to Howard Hinnant
-std::time_t boost_timegm(std::tm *tm)
+See https://howardhinnant.github.io/date_algorithms.html#days_from_civil
+for details and explaination. The code is slightly simplified by our assumption
+that the date is at least 1970, which is sufficient for our purposes.
+*/
+size_t days_since_epoch(uint32_t year, uint32_t month, uint32_t day)
{
- const int sec = tm->tm_sec;
- const int min = tm->tm_min;
- const int hour = tm->tm_hour;
- const int day = tm->tm_mday;
- const int mon = tm->tm_mon + 1;
- const int year = tm->tm_year + 1900;
-
- using namespace boost::posix_time;
- using namespace boost::gregorian;
- const auto epoch = ptime(date(1970, 01, 01));
- const auto time = ptime(date(year, mon, day),
- hours(hour) + minutes(min) + seconds(sec));
- const time_duration diff(time - epoch);
- std::time_t out = diff.ticks() / diff.ticks_per_second();
-
- return out;
+ if(month <= 2)
+ year -= 1;
+ const uint32_t era = year / 400;
+ const uint32_t yoe = year - era * 400; // [0, 399]
+ const uint32_t doy = (153*(month + (month > 2 ? -3 : 9)) + 2)/5 + day-1; // [0, 365]
+ const uint32_t doe = yoe * 365 + yoe/4 - yoe/100 + doy; // [0, 146096]
+ return era * 146097 + doe - 719468;
}
-#elif defined(BOTAN_OS_TYPE_IS_UNIX)
-
-#pragma message "Caution! A fallback version of timegm() is used which is not thread-safe"
-
-mutex_type ENV_TZ;
+}
-std::time_t fallback_timegm(std::tm *tm)
+std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const
{
- std::time_t out;
- std::string tz_backup;
-
- ENV_TZ.lock();
-
- // Store current value of env variable TZ
- const char* tz_env_pointer = ::getenv("TZ");
- if (tz_env_pointer != nullptr)
- tz_backup = std::string(tz_env_pointer);
-
- // Clear value of TZ
- ::setenv("TZ", "", 1);
- ::tzset();
+ if(get_year() < 1970)
+ throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years before 1970");
- out = ::mktime(tm);
-
- // Restore TZ
- if (!tz_backup.empty())
+ // 32 bit time_t ends at January 19, 2038
+ // https://msdn.microsoft.com/en-us/library/2093ets1.aspx
+ // Throw after 2037 if 32 bit time_t is used
+ if(get_year() > 2037 && sizeof(std::time_t) == 4)
{
- // setenv makes a copy of the second argument
- ::setenv("TZ", tz_backup.data(), 1);
+ throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2037 on this system");
}
- else
+ else if(get_year() >= 2400)
{
- ::unsetenv("TZ");
+ // This upper bound is somewhat arbitrary
+ throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2400");
}
- ::tzset();
-
- ENV_TZ.unlock();
- return out;
-}
-#endif // BOTAN_HAS_BOOST_DATETIME
+ const uint64_t seconds_64 = (days_since_epoch(get_year(), get_month(), get_day()) * 86400) +
+ (get_hour() * 60 * 60) + (get_minutes() * 60) + get_seconds();
-#endif
+ const time_t seconds_time_t = static_cast<time_t>(seconds_64);
-}
-
-std::chrono::system_clock::time_point calendar_point::to_std_timepoint() const
- {
- if (year < 1970)
- throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years before 1970.");
-
- // 32 bit time_t ends at January 19, 2038
- // https://msdn.microsoft.com/en-us/library/2093ets1.aspx
- // Throw after 2037 if 32 bit time_t is used
- if (year > 2037 && sizeof(std::time_t) == 4)
+ if(seconds_64 - seconds_time_t != 0)
{
- throw Invalid_Argument("calendar_point::to_std_timepoint() does not support years after 2037.");
+ throw Invalid_Argument("calendar_point::to_std_timepoint time_t overflow");
}
- // std::tm: struct without any timezone information
- std::tm tm;
- tm.tm_isdst = -1; // i.e. no DST information available
- tm.tm_sec = seconds;
- tm.tm_min = minutes;
- tm.tm_hour = hour;
- tm.tm_mday = day;
- tm.tm_mon = month - 1;
- tm.tm_year = year - 1900;
-
- // Define a function alias `botan_timegm`
- #if defined(BOTAN_TARGET_OS_HAS_TIMEGM)
- std::time_t (&botan_timegm)(std::tm *tm) = ::timegm;
- #elif defined(BOTAN_TARGET_OS_HAS_MKGMTIME) && defined(BOTAN_BUILD_COMPILER_IS_MSVC)
- // https://stackoverflow.com/questions/16647819/timegm-cross-platform
- std::time_t (&botan_timegm)(std::tm *tm) = ::_mkgmtime;
- #elif defined(BOTAN_HAS_BOOST_DATETIME)
- std::time_t (&botan_timegm)(std::tm *tm) = boost_timegm;
- #elif defined(BOTAN_OS_TYPE_IS_UNIX)
- std::time_t (&botan_timegm)(std::tm *tm) = fallback_timegm;
- #else
- std::time_t (&botan_timegm)(std::tm *tm) = ::mktime; // localtime instead...
- #endif
-
- // Convert std::tm to std::time_t
- std::time_t tt = botan_timegm(&tm);
- if (tt == -1)
- throw Invalid_Argument("calendar_point couldn't be converted: " + to_string());
-
- return std::chrono::system_clock::from_time_t(tt);
+ return std::chrono::system_clock::from_time_t(seconds_time_t);
}
std::string calendar_point::to_string() const
@@ -162,9 +95,9 @@ std::string calendar_point::to_string() const
{
using namespace std;
output << setfill('0')
- << setw(4) << year << "-" << setw(2) << month << "-" << setw(2) << day
+ << setw(4) << get_year() << "-" << setw(2) << get_month() << "-" << setw(2) << get_day()
<< "T"
- << setw(2) << hour << ":" << setw(2) << minutes << ":" << setw(2) << seconds;
+ << setw(2) << get_hour() << ":" << setw(2) << get_minutes() << ":" << setw(2) << get_seconds();
}
return output.str();
}
diff --git a/src/lib/utils/calendar.h b/src/lib/utils/calendar.h
index 665022599..3f6952524 100644
--- a/src/lib/utils/calendar.h
+++ b/src/lib/utils/calendar.h
@@ -21,25 +21,26 @@ namespace Botan {
class BOTAN_PUBLIC_API(2,0) calendar_point
{
public:
+
/** The year */
- uint32_t year;
+ uint32_t get_year() const { return year; }
/** The month, 1 through 12 for Jan to Dec */
- uint32_t month;
+ uint32_t get_month() const { return month; }
/** The day of the month, 1 through 31 (or 28 or 30 based on month */
- uint32_t day;
+ uint32_t get_day() const { return day; }
/** Hour in 24-hour form, 0 to 23 */
- uint32_t hour;
+ uint32_t get_hour() const { return hour; }
/** Minutes in the hour, 0 to 60 */
- uint32_t minutes;
+ uint32_t get_minutes() const { return minutes; }
/** Seconds in the minute, 0 to 60, but might be slightly
- larger to deal with leap seconds on some systems
+ larger to deal with leap seconds on some systems
*/
- uint32_t seconds;
+ uint32_t get_seconds() const { return seconds; }
/**
* Initialize a calendar_point
@@ -63,6 +64,17 @@ class BOTAN_PUBLIC_API(2,0) calendar_point
* Formatting might change over time. Currently it is RFC339 'iso-date-time'.
*/
std::string to_string() const;
+
+ /*
+ The member variables are public for historical reasons. Use the get_xxx() functions
+ defined above. These members will be made private in a future major release.
+ */
+ uint32_t year;
+ uint32_t month;
+ uint32_t day;
+ uint32_t hour;
+ uint32_t minutes;
+ uint32_t seconds;
};
/**
diff --git a/src/tests/test_utils.cpp b/src/tests/test_utils.cpp
index b8df9c270..3c4accab2 100644
--- a/src/tests/test_utils.cpp
+++ b/src/tests/test_utils.cpp
@@ -274,26 +274,26 @@ class Date_Format_Tests final : public Text_Based_Test
if(type == "valid" || type == "valid.not_std" || type == "valid.64_bit_time_t")
{
Botan::calendar_point c(d[0], d[1], d[2], d[3], d[4], d[5]);
- result.test_is_eq(date_str + " year", c.year, d[0]);
- result.test_is_eq(date_str + " month", c.month, d[1]);
- result.test_is_eq(date_str + " day", c.day, d[2]);
- result.test_is_eq(date_str + " hour", c.hour, d[3]);
- result.test_is_eq(date_str + " minute", c.minutes, d[4]);
- result.test_is_eq(date_str + " second", c.seconds, d[5]);
-
- if(type == "valid.not_std" || (type == "valid.64_bit_time_t" && c.year > 2037 && sizeof(std::time_t) == 4))
+ result.test_is_eq(date_str + " year", c.get_year(), d[0]);
+ result.test_is_eq(date_str + " month", c.get_month(), d[1]);
+ result.test_is_eq(date_str + " day", c.get_day(), d[2]);
+ result.test_is_eq(date_str + " hour", c.get_hour(), d[3]);
+ result.test_is_eq(date_str + " minute", c.get_minutes(), d[4]);
+ result.test_is_eq(date_str + " second", c.get_seconds(), d[5]);
+
+ if(type == "valid.not_std" || (type == "valid.64_bit_time_t" && c.get_year() > 2037 && sizeof(std::time_t) == 4))
{
result.test_throws("valid but out of std::timepoint range", [c]() { c.to_std_timepoint(); });
}
else
{
Botan::calendar_point c2 = Botan::calendar_value(c.to_std_timepoint());
- result.test_is_eq(date_str + " year", c2.year, d[0]);
- result.test_is_eq(date_str + " month", c2.month, d[1]);
- result.test_is_eq(date_str + " day", c2.day, d[2]);
- result.test_is_eq(date_str + " hour", c2.hour, d[3]);
- result.test_is_eq(date_str + " minute", c2.minutes, d[4]);
- result.test_is_eq(date_str + " second", c2.seconds, d[5]);
+ result.test_is_eq(date_str + " year", c2.get_year(), d[0]);
+ result.test_is_eq(date_str + " month", c2.get_month(), d[1]);
+ result.test_is_eq(date_str + " day", c2.get_day(), d[2]);
+ result.test_is_eq(date_str + " hour", c2.get_hour(), d[3]);
+ result.test_is_eq(date_str + " minute", c2.get_minutes(), d[4]);
+ result.test_is_eq(date_str + " second", c2.get_seconds(), d[5]);
}
}
else if(type == "invalid")