summaryrefslogtreecommitdiffstats
path: root/src/lib/http/date_time.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/lib/http/date_time.cc')
-rw-r--r--src/lib/http/date_time.cc158
1 files changed, 158 insertions, 0 deletions
diff --git a/src/lib/http/date_time.cc b/src/lib/http/date_time.cc
new file mode 100644
index 0000000..cd7824a
--- /dev/null
+++ b/src/lib/http/date_time.cc
@@ -0,0 +1,158 @@
+// Copyright (C) 2016-2019 Internet Systems Consortium, Inc. ("ISC")
+//
+// This Source Code Form is subject to the terms of the Mozilla Public
+// License, v. 2.0. If a copy of the MPL was not distributed with this
+// file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+#include <config.h>
+
+#include <http/date_time.h>
+#include <boost/date_time/time_facet.hpp>
+#include <boost/date_time/local_time/local_time.hpp>
+#include <locale>
+#include <sstream>
+
+using namespace boost::local_time;
+using namespace boost::posix_time;
+
+namespace isc {
+namespace http {
+
+HttpDateTime::HttpDateTime()
+ : time_(boost::posix_time::second_clock::universal_time()) {
+}
+
+HttpDateTime::HttpDateTime(const boost::posix_time::ptime& t)
+ : time_(t) {
+}
+
+std::string
+HttpDateTime::rfc1123Format() const {
+ return (toString("%a, %d %b %Y %H:%M:%S GMT", "RFC 1123"));
+}
+
+std::string
+HttpDateTime::rfc850Format() const {
+ return (toString("%A, %d-%b-%y %H:%M:%S GMT", "RFC 850"));
+}
+
+std::string
+HttpDateTime::asctimeFormat() const {
+ return (toString("%a %b %e %H:%M:%S %Y", "asctime"));
+}
+
+HttpDateTime
+HttpDateTime::fromRfc1123(const std::string& time_string) {
+ return (HttpDateTime(fromString(time_string,
+ "%a, %d %b %Y %H:%M:%S %ZP",
+ "RFC 1123")));
+}
+
+HttpDateTime
+HttpDateTime::fromRfc850(const std::string& time_string) {
+ return (HttpDateTime(fromString(time_string,
+ "%A, %d-%b-%y %H:%M:%S %ZP",
+ "RFC 850")));
+}
+
+HttpDateTime
+HttpDateTime::fromAsctime(const std::string& time_string) {
+ // The asctime() puts space instead of leading 0 in days of
+ // month. The %e # formatter of time_input_facet doesn't deal
+ // with this. To deal with this, we make a copy of the string
+ // holding formatted time and replace a space preceding day
+ // number with 0. Thanks to this workaround we can use the
+ // %d formatter which seems to work fine. This has a side
+ // effect of accepting timestamps such as Sun Nov 06 08:49:37 1994,
+ // but it should be ok to be liberal in this case.
+ std::string time_string_copy(time_string);
+ boost::replace_all(time_string_copy, " ", " 0");
+ return (HttpDateTime(fromString(time_string_copy,
+ "%a %b %d %H:%M:%S %Y",
+ "asctime",
+ false)));
+}
+
+HttpDateTime
+HttpDateTime::fromAny(const std::string& time_string) {
+ HttpDateTime date_time;
+ // Try to parse as a timestamp specified in RFC 1123 format.
+ try {
+ date_time = fromRfc1123(time_string);
+ return (date_time);
+ } catch (...) {
+ // Ignore errors, simply try different format.
+ }
+
+ // Try to parse as a timestamp specified in RFC 850 format.
+ try {
+ date_time = fromRfc850(time_string);
+ return (date_time);
+ } catch (...) {
+ // Ignore errors, simply try different format.
+ }
+
+ // Try to parse as a timestamp output by asctime() function.
+ try {
+ date_time = fromAsctime(time_string);
+ } catch (...) {
+ isc_throw(HttpTimeConversionError,
+ "unsupported time format of the '" << time_string
+ << "'");
+ }
+
+ return (date_time);
+
+}
+
+std::string
+HttpDateTime::toString(const std::string& format,
+ const std::string& method_name) const {
+ std::ostringstream s;
+ // Create raw pointer. The output stream will take responsibility for
+ // deleting the object.
+ time_facet* df(new time_facet(format.c_str()));
+ s.imbue(std::locale(std::locale::classic(), df));
+
+ // Convert time value to a string.
+ s << time_;
+ if (s.fail()) {
+ isc_throw(HttpTimeConversionError, "unable to convert "
+ << "time value of '" << time_ << "'"
+ << " to " << method_name << " format");
+ }
+ return (s.str());
+}
+
+
+ptime
+HttpDateTime::fromString(const std::string& time_string,
+ const std::string& format,
+ const std::string& method_name,
+ const bool zone_check) {
+ std::istringstream s(time_string);
+ // Create raw pointer. The input stream will take responsibility for
+ // deleting the object.
+ time_input_facet* tif(new time_input_facet(format));
+ s.imbue(std::locale(std::locale::classic(), tif));
+
+ time_zone_ptr zone(new posix_time_zone("GMT"));
+ local_date_time ldt = local_microsec_clock::local_time(zone);
+
+ // Parse the time value. The stream will not automatically detect whether
+ // the zone is GMT. We need to check it on our own.
+ s >> ldt;
+ if (s.fail() ||
+ (zone_check && (!ldt.zone() ||
+ ldt.zone()->std_zone_abbrev() != "GMT"))) {
+ isc_throw(HttpTimeConversionError, "unable to parse "
+ << method_name << " time value of '"
+ << time_string << "'");
+ }
+
+ return (ldt.local_time());
+}
+
+
+} // namespace http
+} // namespace isc