This is an automated email from the ASF dual-hosted git repository. zwoop pushed a commit to branch 8.0.x in repository https://gitbox.apache.org/repos/asf/trafficserver.git
commit 605589296bba0edc6aa4d26ede7d937c45d8cb3c Author: Alan M. Carroll <a...@apache.org> AuthorDate: Fri Jun 8 14:30:53 2018 -0500 BWF: Add date / time stamp support. (cherry picked from commit 1a7d10af373f4a86348b42f659b0f42711b95a68) --- doc/conf.py | 1 + .../internal-libraries/buffer-writer.en.rst | 29 +++++++++++++++- lib/ts/BufferWriterFormat.cc | 39 +++++++++++++++++++++- lib/ts/bwf_std_format.h | 9 +++++ lib/ts/unit-tests/test_BufferWriterFormat.cc | 22 ++++++++++++ 5 files changed, 98 insertions(+), 2 deletions(-) diff --git a/doc/conf.py b/doc/conf.py index 322769d..2ba2050 100644 --- a/doc/conf.py +++ b/doc/conf.py @@ -174,6 +174,7 @@ nitpick_ignore = [ ('cpp:typeOrConcept', 'std') , ('cpp:typeOrConcept', 'std::shared_ptr') , ('cpp:typeOrConcept', 'std::ostream') , ('cpp:typeOrConcept', 'std::string') + , ('cpp:typeOrConcept', 'std::string_view') , ('cpp:typeOrConcept', 'std::tuple') , ('cpp:typeOrConcept', 'V') # template arguments which should be matched but aren't. , ('cpp:typeOrConcept', 'Args') diff --git a/doc/developer-guide/internal-libraries/buffer-writer.en.rst b/doc/developer-guide/internal-libraries/buffer-writer.en.rst index ced90d4..4f7a416 100644 --- a/doc/developer-guide/internal-libraries/buffer-writer.en.rst +++ b/doc/developer-guide/internal-libraries/buffer-writer.en.rst @@ -728,6 +728,33 @@ For :code:`errno` this is handy in another way as :code:`ts::bwf::Errno` will pr // some other code generating diagnostics that might tweak errno. w.print("File not open - {}", last_err); +These are the existing format classes in header file ``bfw_std_format.h``. All are in the :code:`ts::bwf` namespace. + +.. class:: Errno + + Formating for :code:`errno`. + + .. function:: Errno(int errno) + +.. class:: Date + + Date formatting in the :code:`strftime` style. + + .. function:: Date(time_t epoch, std::string_view fmt = "%Y %b %d %H:%M:%S") + + :arg:`epoch` is the time to print. :arg:`fmt` is the format for printing which is identical to that of `strftime <https://linux.die.net/man/3/strftime>`__. The default format looks like "2018 Jun 08 13:55:37". + + .. function:: Date(std::string_view fmt = "%Y %b %d %H:%M:%S") + + As previous except the epoch is the current epoch at the time the constructor is invoked. Therefore if the current time is to be printed the default constructor can be used. + + When used the format specification can take an extention of "local" which formats the time as local time. Otherwise it is GMT. + ``w.print("{}", Date("%H:%M"));`` will print the hour and minute as GMT values. ``w.print("{::local}", Date("%H:%M"));`` will + When used the format specification can take an extention of "local" which formats the time as local time. Otherwise it is GMT. + ``w.print("{}", Date("%H:%M"));`` will print the hour and minute as GMT values. ``w.print("{::local}", Date("%H:%M"));`` will + print the hour and minute in the local time zone. ``w.print("{::gmt}"), ...);`` will output in GMT if additional explicitness is + desired. + Global Names ++++++++++++ @@ -826,7 +853,7 @@ Reference Write to the buffer starting at :arg:`data` for at most :arg:`length` bytes. If there is not enough room to fit all the data, none is written. - .. function:: BufferWriter & write(string_view str) + .. function:: BufferWriter & write(std::string_view str) Write the string :arg:`str` to the buffer. If there is not enough room to write the string no data is written. diff --git a/lib/ts/BufferWriterFormat.cc b/lib/ts/BufferWriterFormat.cc index 18384c9..e265276 100644 --- a/lib/ts/BufferWriterFormat.cc +++ b/lib/ts/BufferWriterFormat.cc @@ -914,6 +914,43 @@ bwformat(BufferWriter &w, BWFSpec const &spec, bwf::Errno const &e) return w; } +bwf::Date::Date(std::string_view fmt) : _epoch(std::chrono::system_clock::to_time_t(std::chrono::system_clock::now())), _fmt(fmt) {} + +BufferWriter & +bwformat(BufferWriter &w, BWFSpec const &spec, bwf::Date const &date) +{ + if (spec.has_numeric_type()) { + bwformat(w, spec, date._epoch); + } else { + struct tm t; + auto r = w.remaining(); + size_t n{0}; + // Verify @a fmt is null terminated, even outside the bounds of the view. + ink_assert(date._fmt.data()[date._fmt.size() - 1] == 0 || date._fmt.data()[date._fmt.size()] == 0); + // Get the time, GMT or local if specified. + if (spec._ext == "local"sv) { + localtime_r(&date._epoch, &t); + } else { + gmtime_r(&date._epoch, &t); + } + // Try a direct write, faster if it works. + if (r > 0) { + n = strftime(w.auxBuffer(), r, date._fmt.data(), &t); + } + if (n > 0) { + w.fill(n); + } else { + // Direct write didn't work. Unfortunately need to write to a temporary buffer or the sizing + // isn't correct if @a w is clipped because @c strftime returns 0 if the buffer isn't large + // enough. + char buff[256]; // hope for the best - no real way to resize appropriately on failure. + n = strftime(buff, sizeof(buff), date._fmt.data(), &t); + w.write(buff, n); + } + } + return w; +} + } // namespace ts namespace @@ -926,7 +963,7 @@ BWF_Timestamp(ts::BufferWriter &w, ts::BWFSpec const &spec) char buff[32]; std::time_t t = std::time(nullptr); auto n = strftime(buff, sizeof(buff), "%Y %b %d %H:%M:%S", std::localtime(&t)); - w.write(std::string_view{buff, n}); + w.write(buff, n); } void diff --git a/lib/ts/bwf_std_format.h b/lib/ts/bwf_std_format.h index fdc998f..60776c4 100644 --- a/lib/ts/bwf_std_format.h +++ b/lib/ts/bwf_std_format.h @@ -24,6 +24,7 @@ #pragma once #include <atomic> +#include <string_view> namespace std { @@ -43,8 +44,16 @@ namespace bwf int _e; explicit Errno(int e) : _e(e) {} }; + + struct Date { + time_t _epoch; + std::string_view _fmt; + Date(time_t t, std::string_view fmt = "%Y %b %d %H:%M:%S"sv) : _epoch(t), _fmt(fmt) {} + Date(std::string_view fmt = "%Y %b %d %H:%M:%S"sv); + }; } // namespace bwf BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, bwf::Errno const &e); +BufferWriter &bwformat(BufferWriter &w, BWFSpec const &spec, bwf::Date const &date); } // namespace ts diff --git a/lib/ts/unit-tests/test_BufferWriterFormat.cc b/lib/ts/unit-tests/test_BufferWriterFormat.cc index f1f5f3a..cda89e7 100644 --- a/lib/ts/unit-tests/test_BufferWriterFormat.cc +++ b/lib/ts/unit-tests/test_BufferWriterFormat.cc @@ -489,6 +489,28 @@ TEST_CASE("bwstring std formats", "[libts][bwprint]") REQUIRE(w.view() == "EACCES: Permission denied [13]"sv); w.reset().print("{}", ts::bwf::Errno(134)); REQUIRE(w.view().substr(0, 22) == "Unknown: Unknown error"sv); + + time_t t = 1528484137; + // default is GMT + w.reset().print("{} is {}", t, ts::bwf::Date(t)); + REQUIRE(w.view() == "1528484137 is 2018 Jun 08 18:55:37"); + w.reset().print("{} is {}", t, ts::bwf::Date(t, "%a, %d %b %Y at %H.%M.%S")); + REQUIRE(w.view() == "1528484137 is Fri, 08 Jun 2018 at 18.55.37"); + // OK to be explicit + w.reset().print("{} is {::gmt}", t, ts::bwf::Date(t)); + REQUIRE(w.view() == "1528484137 is 2018 Jun 08 18:55:37"); + w.reset().print("{} is {::gmt}", t, ts::bwf::Date(t, "%a, %d %b %Y at %H.%M.%S")); + REQUIRE(w.view() == "1528484137 is Fri, 08 Jun 2018 at 18.55.37"); + // Local time - set it to something specific or the test will be geographically sensitive. + setenv("TZ", "CST6", 1); + tzset(); + w.reset().print("{} is {::local}", t, ts::bwf::Date(t)); + REQUIRE(w.view() == "1528484137 is 2018 Jun 08 12:55:37"); + w.reset().print("{} is {::local}", t, ts::bwf::Date(t, "%a, %d %b %Y at %H.%M.%S")); + REQUIRE(w.view() == "1528484137 is Fri, 08 Jun 2018 at 12.55.37"); + + // Verify these compile and run, not really much hope to check output. + w.reset().print("|{}| |{}|", ts::bwf::Date(), ts::bwf::Date("%a, %d %b %Y")); } // Normally there's no point in running the performance tests, but it's worth keeping the code