[cmds] fix some timezone issues with :convert-time-to and other spots

Related to #1236
pull/1242/head
Tim Stack 4 months ago
parent c68b7ec7a3
commit 9cf2832ff3

@ -174,9 +174,11 @@ Breaking changes:
the data is now stored (and cleaned up) as well as being
spread across multiple files, this option doesn't make
sense anymore.
* Removed the `-t` command-line flag. Text data fed in
on stdin and captured from a `:sh` execution is
automatically timestamped.
* The `-t` command-line flag behaves a little differently
behind the scenes now. Timestamps will always be
recorded for each line piped into lnav. This flag means
that the data should be treated as a log file instead of
plain text.
* Data piped into **lnav** is now stored in the work
directory instead of the `stdin-captures` dot-lnav
directory.

@ -116,6 +116,7 @@ test_base_SOURCES = \
test_base_LDADD = \
libbase.a \
../fmtlib/libcppfmt.a \
../third-party/date/src/libdatepp.a \
../third-party/scnlib/src/libscnlib.a \
../pcrepp/libpcrepp.a

@ -30,10 +30,15 @@
*/
#include <chrono>
#include <map>
#include "time_util.hh"
#include <date/ptz.h>
#include "config.h"
#include "lnav_log.hh"
#include "optional.hpp"
namespace lnav {
@ -75,6 +80,64 @@ strftime_rfc3339(
return index;
}
static nonstd::optional<Posix::time_zone>
get_posix_zone(const char* name)
{
if (name == nullptr) {
return nonstd::nullopt;
}
try {
return date::zoned_traits<Posix::time_zone>::locate_zone(name);
} catch (const std::runtime_error& e) {
log_error("invalid TZ value: %s -- %s", name, e.what());
return nonstd::nullopt;
}
}
static const date::time_zone*
get_date_zone(const char* name)
{
if (name == nullptr) {
return date::current_zone();
}
try {
return date::locate_zone(name);
} catch (const std::runtime_error& e) {
log_error("invalid TZ value: %s -- %s", name, e.what());
return date::current_zone();
}
}
date::sys_seconds
to_sys_time(date::local_seconds secs)
{
static const auto* TZ = getenv("TZ");
static const auto TZ_POSIX_ZONE = get_posix_zone(TZ);
static const auto* TZ_DATE_ZONE = get_date_zone(TZ);
if (TZ_POSIX_ZONE) {
return TZ_POSIX_ZONE.value().to_sys(secs);
}
return TZ_DATE_ZONE->to_sys(secs);
}
date::local_seconds
to_local_time(date::sys_seconds secs)
{
static const auto* TZ = getenv("TZ");
static const auto TZ_POSIX_ZONE = get_posix_zone(TZ);
static const auto* TZ_DATE_ZONE = get_date_zone(TZ);
if (TZ_POSIX_ZONE) {
return TZ_POSIX_ZONE.value().to_local(secs);
}
return TZ_DATE_ZONE->to_local(secs);
}
} // namespace lnav
static time_t BAD_DATE = -1;

@ -32,6 +32,7 @@
#include <chrono>
#include <date/date.h>
#include <inttypes.h>
#include <string.h>
#include <sys/time.h>
@ -50,6 +51,10 @@ ssize_t strftime_rfc3339(char* buffer,
int millis,
char sep = ' ');
date::sys_seconds to_sys_time(date::local_seconds secs);
date::local_seconds to_local_time(date::sys_seconds secs);
} // namespace lnav
struct tm* secs2tm(lnav::time64_t tim, struct tm* res);

@ -1239,6 +1239,11 @@ line_buffer::load_next_line(file_range prev_line)
retval.li_timestamp.tv_usec,
level);
if (scan_res) {
retval.li_timestamp.tv_sec
= lnav::to_local_time(date::sys_seconds{std::chrono::seconds{
retval.li_timestamp.tv_sec}})
.time_since_epoch()
.count();
retval.li_level = abbrev2level(&level, 1);
}
}

@ -647,6 +647,10 @@ make it easier to navigate through files quickly.
.append(" ")
.append("Execute a shell command-line.\n")
.append(" ")
.append("-t"_symbol)
.append(" ")
.append("Treat data piped into standard in as a log file.\n")
.append(" ")
.append("-n"_symbol)
.append(" ")
.append("Run without the curses UI. (headless mode)\n")
@ -2469,6 +2473,8 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
"-R", lnav_data.ld_active_files.fc_rotated, "rotated");
auto* recurse_flag = app.add_flag(
"-r", lnav_data.ld_active_files.fc_recursive, "recurse");
auto* as_log_flag
= app.add_flag("-t", lnav_data.ld_treat_stdin_as_log, "as-log");
app.add_flag("-W", mode_flags.mf_print_warnings);
auto* headless_flag = app.add_flag(
"-n",
@ -2549,6 +2555,7 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
install_flag->needs(file_opt);
install_flag->excludes(no_default_flag,
as_log_flag,
rotated_flag,
recurse_flag,
headless_flag,
@ -2947,6 +2954,9 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
lnav_data.ld_vtab_manager->register_vtab(
std::make_shared<log_format_vtab_impl>(
*log_format::find_root_format("generic_log")));
lnav_data.ld_vtab_manager->register_vtab(
std::make_shared<log_format_vtab_impl>(
*log_format::find_root_format("lnav_piper_log")));
for (auto& iter : log_format::get_root_formats()) {
auto lvi = iter->get_vtab_impl();
@ -3253,10 +3263,12 @@ SELECT tbl_name FROM sqlite_master WHERE sql LIKE 'CREATE VIRTUAL TABLE%'
auto stdin_piper = stdin_piper_res.unwrap();
stdin_url = stdin_piper.get_url();
stdin_dir = stdin_piper.get_out_dir();
lnav_data.ld_active_files
.fc_file_names[stdin_piper.get_name()]
.with_piper(stdin_piper)
.with_include_in_session(false);
auto& loo = lnav_data.ld_active_files
.fc_file_names[stdin_piper.get_name()];
loo.with_piper(stdin_piper).with_include_in_session(false);
if (lnav_data.ld_treat_stdin_as_log) {
loo.with_text_format(text_format_t::TF_LOG);
}
}
}
} else if (S_ISREG(stdin_st.st_mode)) {

@ -253,6 +253,7 @@ struct lnav_data_t {
bool ld_initial_build{false};
bool ld_show_help_view{false};
bool ld_treat_stdin_as_log{false};
lnav_exec_phase ld_exec_phase{lnav_exec_phase::INIT};
lnav::func::scoped_cb ld_status_refresher;

@ -503,9 +503,8 @@ com_clear_file_timezone_prompt(exec_context& ec, const std::string& cmdline)
pattern_arg = match_res->first;
}
retval = fmt::format(FMT_STRING("{} {}"),
trim(cmdline),
pattern_arg);
retval = fmt::format(
FMT_STRING("{} {}"), trim(cmdline), pattern_arg);
} catch (const std::runtime_error& e) {
log_error("cannot get timezones: %s", e.what());
}
@ -595,15 +594,26 @@ com_convert_time_to(exec_context& ec,
const auto* ll = lss->find_line(lss->at(tc->get_selection()));
try {
auto* tz = date::locate_zone(args[1]);
auto utime = std::chrono::system_clock::from_time_t(ll->get_time());
auto ztime = date::make_zoned(tz, utime);
auto* dst_tz = date::locate_zone(args[1]);
auto utime = date::local_time<std::chrono::seconds>{
std::chrono::seconds{ll->get_time()}};
auto cz_time = lnav::to_sys_time(utime);
auto dz_time = date::make_zoned(dst_tz, cz_time);
auto etime = std::chrono::duration_cast<std::chrono::seconds>(
ztime.get_local_time().time_since_epoch());
dz_time.get_local_time().time_since_epoch());
char ftime[128];
sql_strftime(
ftime, sizeof(ftime), etime.count(), ll->get_millis(), 'T');
retval = ftime;
off_t off = 0;
exttm tm;
tm.et_flags |= ETF_ZONE_SET;
tm.et_gmtoff = dz_time.get_info().offset.count();
ftime_Z(ftime, off, sizeof(ftime), tm);
ftime[off] = '\0';
retval.append(" ");
retval.append(ftime);
} catch (const std::runtime_error& e) {
return ec.make_error(FMT_STRING("Unable to get timezone: {} -- {}"),
args[1],

@ -45,6 +45,76 @@
#include "sql_util.hh"
#include "yajlpp/yajlpp.hh"
class piper_log_format : public log_format {
public:
const intern_string_t get_name() const override
{
static const intern_string_t RETVAL
= intern_string::lookup("lnav_piper_log");
return RETVAL;
}
scan_result_t scan(logfile& lf,
std::vector<logline>& dst,
const line_info& li,
shared_buffer_ref& sbr,
scan_batch_context& sbc) override
{
if (lf.has_line_metadata()
&& lf.get_text_format() == text_format_t::TF_LOG)
{
dst.emplace_back(
li.li_file_range.fr_offset, li.li_timestamp, li.li_level);
return scan_match{100};
}
return scan_no_match{""};
}
void annotate(uint64_t line_number,
string_attrs_t& sa,
logline_value_vector& values,
bool annotate_module) const override
{
auto lr = line_range{0, 0};
sa.emplace_back(lr, logline::L_TIMESTAMP.value());
}
void get_subline(const logline& ll,
shared_buffer_ref& sbr,
bool full_message) override
{
this->plf_cached_line.resize(23);
sql_strftime(this->plf_cached_line.data(),
this->plf_cached_line.size(),
ll.get_timeval(),
'T');
this->plf_cached_line.push_back(' ');
const auto prefix_len = this->plf_cached_line.size();
this->plf_cached_line.resize(this->plf_cached_line.size()
+ sbr.length());
memcpy(
&this->plf_cached_line[prefix_len], sbr.get_data(), sbr.length());
sbr.share(this->plf_share_manager,
this->plf_cached_line.data(),
this->plf_cached_line.size());
}
std::shared_ptr<log_format> specialized(int fmt_lock) override
{
auto retval = std::make_shared<piper_log_format>(*this);
retval->lf_specialized = true;
return retval;
}
private:
shared_buffer plf_share_manager;
std::vector<char> plf_cached_line;
};
class generic_log_format : public log_format {
static const pcre_format* get_pcre_log_formats()
{
@ -1946,4 +2016,5 @@ static auto format_binder = injector::bind_multiple<log_format>()
.add<logfmt_format>()
.add<bro_log_format>()
.add<w3c_log_format>()
.add<generic_log_format>();
.add<generic_log_format>()
.add<piper_log_format>();

@ -133,6 +133,8 @@ logfile::open(std::string filename, const logfile_open_options& loo, auto_fd fd)
lf->lf_index.reserve(INDEX_RESERVE_INCREMENT);
lf->lf_indexing = lf->lf_options.loo_is_visible;
lf->lf_text_format
= lf->lf_options.loo_text_format.value_or(text_format_t::TF_UNKNOWN);
const auto& hdr = lf->lf_line_buffer.get_header_data();
if (hdr.valid()) {

@ -171,6 +171,8 @@ public:
text_format_t get_text_format() const { return this->lf_text_format; }
void set_text_format(text_format_t tf) { this->lf_text_format = tf; }
/**
* @return The last modified time of the file when the file was last
* indexed.

@ -38,6 +38,7 @@
#include "base/auto_fd.hh"
#include "file_format.hh"
#include "piper.looper.hh"
#include "text_format.hh"
#include "vis_line.hh"
using ui_clock = std::chrono::steady_clock;
@ -70,6 +71,7 @@ struct logfile_open_options_base {
bool loo_tail{true};
file_format_t loo_file_format{file_format_t::UNKNOWN};
nonstd::optional<std::string> loo_format_name;
nonstd::optional<text_format_t> loo_text_format;
nonstd::optional<lnav::piper::running_handle> loo_piper;
file_location_t loo_init_location{mapbox::util::no_init{}};
};
@ -167,6 +169,13 @@ struct logfile_open_options : public logfile_open_options_base {
return *this;
}
logfile_open_options& with_text_format(text_format_t tf)
{
this->loo_text_format = tf;
return *this;
}
};
#endif

@ -379,9 +379,7 @@ logfile_sub_source::text_value_for_line(textview_curses& tc,
if (format->lf_timestamp_flags & ETF_ZONE_SET
&& format->lf_date_time.dts_zoned_to_local)
{
if (format->lf_timestamp_flags & ETF_Z_IS_UTC) {
adjusted_tm.et_flags &= ~ETF_Z_IS_UTC;
}
adjusted_tm.et_flags &= ~ETF_Z_IS_UTC;
}
adjusted_tm.et_gmtoff
= format->lf_date_time.dts_local_offset_cache;

@ -76,6 +76,7 @@ tailer_LDADD = libtailercommon.a
drive_tailer_CPPFLAGS = \
-I$(srcdir)/.. \
-I$(srcdir)/../fmtlib \
-I$(top_srcdir)/src/third-party/date/include \
-I$(top_srcdir)/src/third-party/scnlib/include
drive_tailer_SOURCES = \

@ -27,8 +27,12 @@
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include <chrono>
#include "textfile_sub_source.hh"
#include <date/date.h>
#include "base/ansi_scrubber.hh"
#include "base/attr_line.builder.hh"
#include "base/fs_util.hh"

@ -36,8 +36,24 @@
// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
// zoned_time<system_clock::duration, Posix::time_zone> zt{tz, system_clock::now()};
//
// If the rule set is missing (everything starting with ','), then the rule is that the
// alternate offset is never enabled.
// In C++17 CTAD simplifies this to:
//
// Posix::time_zone tz{"EST5EDT,M3.2.0,M11.1.0"};
// zoned_time zt{tz, system_clock::now()};
//
// Extension to the Posix rules to allow a constant daylight saving offset:
//
// If the rule set is missing (everything starting with ','), then
// there must be exactly one abbreviation (std or daylight) with
// length 3 or greater, and that will be used as the constant offset. If
// there are two, the std abbreviation is silently set to "", and the
// result is constant daylight saving. If there are zero abbreviations
// with no rule set, an exception is thrown.
//
// Example:
// "EST5" yields a constant offset of -5h with 0h save and "EST abbreviation.
// "5EDT" yields a constant offset of -4h with 1h save and "EDT" abbreviation.
// "EST5EDT" and "5EDT4" are both equal to "5EDT".
//
// Note, Posix-style time zones are not recommended for all of the reasons described here:
// https://stackoverflow.com/tags/timezone/info
@ -46,6 +62,7 @@
// have to have Posix time zones, you're welcome to use this one.
#include "date/tz.h"
#include <algorithm>
#include <cctype>
#include <ostream>
#include <string>
@ -322,6 +339,7 @@ time_zone::get_next_end(date::year y) const
return date::sys_seconds{(end_rule_(++y) - (offset_ + save_)).time_since_epoch()};
}
inline
date::sys_info
time_zone::contant_offset() const
{
@ -331,11 +349,22 @@ time_zone::contant_offset() const
using date::January;
using date::December;
using date::last;
using std::chrono::minutes;
sys_info r;
r.begin = sys_days{year::min()/January/1};
r.end = sys_days{year::max()/December/last};
r.abbrev = std_abbrev_;
r.offset = offset_;
if (std_abbrev_.size() > 0)
{
r.abbrev = std_abbrev_;
r.offset = offset_;
r.save = {};
}
else
{
r.abbrev = dst_abbrev_;
r.offset = offset_ + save_;
r.save = date::ceil<minutes>(save_);
}
return r;
}
@ -346,11 +375,14 @@ time_zone::time_zone(const detail::string_t& s)
using detail::read_signed_time;
using detail::throw_invalid;
auto i = read_name(s, 0, std_abbrev_);
auto std_name_i = i;
auto abbrev_name_i = i;
i = read_signed_time(s, i, offset_);
offset_ = -offset_;
if (i != s.size())
{
i = read_name(s, i, dst_abbrev_);
abbrev_name_i = i;
if (i != s.size())
{
if (s[i] != ',')
@ -373,6 +405,32 @@ time_zone::time_zone(const detail::string_t& s)
}
}
}
if (start_rule_.ok())
{
if (std_abbrev_.size() < 3)
throw_invalid(s, std_name_i, "Zone with rules must have a std"
" abbreviation of length 3 or greater");
if (dst_abbrev_.size() < 3)
throw_invalid(s, abbrev_name_i, "Zone with rules must have a daylight"
" abbreviation of length 3 or greater");
}
else
{
if (dst_abbrev_.size() >= 3)
{
std_abbrev_.clear();
}
else if (std_abbrev_.size() < 3)
{
throw_invalid(s, std_name_i, "Zone must have at least one abbreviation"
" of length 3 or greater");
}
else
{
dst_abbrev_.clear();
save_ = {};
}
}
}
template <class Duration>
@ -395,6 +453,10 @@ time_zone::get_info(date::sys_time<Duration> st) const
if (start_rule_.ok())
{
auto y = year_month_day{floor<days>(st)}.year();
if (st >= get_next_start(y))
++y;
else if (st < get_prev_end(y))
--y;
auto start = get_start(y);
auto end = get_end(y);
if (start <= end) // (northern hemisphere)
@ -448,6 +510,7 @@ time_zone::get_info(date::sys_time<Duration> st) const
}
else
r = contant_offset();
assert(r.begin <= st && st < r.end);
return r;
}
@ -589,7 +652,18 @@ time_zone::name() const
{
using namespace date;
using namespace std::chrono;
auto nm = std_abbrev_;
auto print_abbrev = [](std::string const& nm)
{
if (std::any_of(nm.begin(), nm.end(),
[](char c)
{
return !std::isalpha(c);
}))
{
return '<' + nm + '>';
}
return nm;
};
auto print_offset = [](seconds off)
{
std::string nm;
@ -613,10 +687,11 @@ time_zone::name() const
}
return nm;
};
auto nm = print_abbrev(std_abbrev_);
nm += print_offset(offset_);
if (!dst_abbrev_.empty())
{
nm += dst_abbrev_;
nm += print_abbrev(dst_abbrev_);
if (save_ != hours{1})
nm += print_offset(offset_+save_);
if (start_rule_.ok())
@ -678,6 +753,8 @@ read_date(const string_t& s, unsigned i, rule& r)
++i;
unsigned n;
i = read_unsigned(s, i, 3, n, "Expected to find the Julian day [1, 365]");
if (!(1 <= n && n <= 365))
throw_invalid(s, i-1, "Expected Julian day to be in the range [1, 365]");
r.mode_ = rule::J;
r.n_ = n;
}
@ -686,16 +763,22 @@ read_date(const string_t& s, unsigned i, rule& r)
++i;
unsigned m;
i = read_unsigned(s, i, 2, m, "Expected to find month [1, 12]");
if (!(1 <= m && m <= 12))
throw_invalid(s, i-1, "Expected month to be in the range [1, 12]");
if (i == s.size() || s[i] != '.')
throw_invalid(s, i, "Expected '.' after month");
++i;
unsigned n;
i = read_unsigned(s, i, 1, n, "Expected to find week number [1, 5]");
if (!(1 <= n && n <= 5))
throw_invalid(s, i-1, "Expected week number to be in the range [1, 5]");
if (i == s.size() || s[i] != '.')
throw_invalid(s, i, "Expected '.' after weekday index");
++i;
unsigned wd;
i = read_unsigned(s, i, 1, wd, "Expected to find day of week [0, 6]");
if (wd > 6)
throw_invalid(s, i-1, "Expected day of week to be in the range [0, 6]");
r.mode_ = rule::M;
r.m_ = month{m};
r.wd_ = weekday{wd};
@ -705,6 +788,8 @@ read_date(const string_t& s, unsigned i, rule& r)
{
unsigned n;
i = read_unsigned(s, i, 3, n);
if (n > 365)
throw_invalid(s, i-1, "Expected Julian day to be in the range [0, 365]");
r.mode_ = rule::N;
r.n_ = n;
}
@ -749,8 +834,6 @@ read_name(const string_t& s, unsigned i, std::string& name)
++i;
}
}
if (name.size() < 3)
throw_invalid(s, i, "Found name to be shorter than 3 characters");
return i;
}
@ -786,16 +869,22 @@ read_unsigned_time(const string_t& s, unsigned i, std::chrono::seconds& t)
throw_invalid(s, i, "Expected to read unsigned time, but found end of string");
unsigned x;
i = read_unsigned(s, i, 2, x, "Expected to find hours [0, 24]");
if (x > 24)
throw_invalid(s, i-1, "Expected hours to be in the range [0, 24]");
t = hours{x};
if (i != s.size() && s[i] == ':')
{
++i;
i = read_unsigned(s, i, 2, x, "Expected to find minutes [0, 59]");
if (x > 59)
throw_invalid(s, i-1, "Expected minutes to be in the range [0, 59]");
t += minutes{x};
if (i != s.size() && s[i] == ':')
{
++i;
i = read_unsigned(s, i, 2, x, "Expected to find seconds [0, 59]");
if (x > 59)
throw_invalid(s, i-1, "Expected seconds to be in the range [0, 59]");
t += seconds{x};
}
}

@ -220,6 +220,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_cmds.sh_d836c84398c831c976df46f46fe3bf5983c44c37.out \
$(srcdir)/%reldir%/test_cmds.sh_d8eeef53a58bdeddbc1028d7c525413e3ca1c8df.err \
$(srcdir)/%reldir%/test_cmds.sh_d8eeef53a58bdeddbc1028d7c525413e3ca1c8df.out \
$(srcdir)/%reldir%/test_cmds.sh_da5f7160b967e60dbd772573614e2da89c5e22b2.err \
$(srcdir)/%reldir%/test_cmds.sh_da5f7160b967e60dbd772573614e2da89c5e22b2.out \
$(srcdir)/%reldir%/test_cmds.sh_dbdd62995fdefc8318053af05a32416eccfa79fc.err \
$(srcdir)/%reldir%/test_cmds.sh_dbdd62995fdefc8318053af05a32416eccfa79fc.out \
$(srcdir)/%reldir%/test_cmds.sh_dd41fbbcd71699314af232156d4155fbdf849131.err \
@ -236,6 +238,8 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_cmds.sh_eb22c3e94c536a1bfaeae0c40d271b5b4b08f4fc.out \
$(srcdir)/%reldir%/test_cmds.sh_ec2b28c6ea328e3ea56b13ab8ca3d9ee856a9dda.err \
$(srcdir)/%reldir%/test_cmds.sh_ec2b28c6ea328e3ea56b13ab8ca3d9ee856a9dda.out \
$(srcdir)/%reldir%/test_cmds.sh_ec3a64cad41b070a1d04e2bfc3dc14cb2d964091.err \
$(srcdir)/%reldir%/test_cmds.sh_ec3a64cad41b070a1d04e2bfc3dc14cb2d964091.out \
$(srcdir)/%reldir%/test_cmds.sh_ed5b73be0b991e0e8d6735e31df5b37c4286321b.err \
$(srcdir)/%reldir%/test_cmds.sh_ed5b73be0b991e0e8d6735e31df5b37c4286321b.out \
$(srcdir)/%reldir%/test_cmds.sh_f788d5f5932905d09ecbd581040ec5ce76459da5.err \
@ -1202,6 +1206,10 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_text_file.sh_143a40164c93c7ec44a66e7940b92b128a421147.out \
$(srcdir)/%reldir%/test_text_file.sh_1ce4056d72b871f8bb844c86aade2a9b1da58030.err \
$(srcdir)/%reldir%/test_text_file.sh_1ce4056d72b871f8bb844c86aade2a9b1da58030.out \
$(srcdir)/%reldir%/test_text_file.sh_25cef06efcbe106c2e1cc4a166b673e7b244c6d7.err \
$(srcdir)/%reldir%/test_text_file.sh_25cef06efcbe106c2e1cc4a166b673e7b244c6d7.out \
$(srcdir)/%reldir%/test_text_file.sh_265a8a5825e6c7dbc85cbe496dab6be7a349f3db.err \
$(srcdir)/%reldir%/test_text_file.sh_265a8a5825e6c7dbc85cbe496dab6be7a349f3db.out \
$(srcdir)/%reldir%/test_text_file.sh_4226123565a53b4e3f80e602c1f294721e8e07bf.err \
$(srcdir)/%reldir%/test_text_file.sh_4226123565a53b4e3f80e602c1f294721e8e07bf.out \
$(srcdir)/%reldir%/test_text_file.sh_4dd174410d702a7b4be794fb6fa2c8889bd768d6.err \
@ -1240,4 +1248,6 @@ EXPECTED_FILES = \
$(srcdir)/%reldir%/test_text_file.sh_e556fa91b91579df20d38540a5db9cedbaf68a62.out \
$(srcdir)/%reldir%/test_text_file.sh_f586ef080a86dfe1f981b345bcf8d7a279b2b247.err \
$(srcdir)/%reldir%/test_text_file.sh_f586ef080a86dfe1f981b345bcf8d7a279b2b247.out \
$(srcdir)/%reldir%/test_text_file.sh_f7522b0a99550a3ff91aae6582eb861547c535e1.err \
$(srcdir)/%reldir%/test_text_file.sh_f7522b0a99550a3ff91aae6582eb861547c535e1.out \
$()

@ -0,0 +1,6 @@
✘ error: Unable to get timezone: bad-zone -- bad-zone not found in timezone database
 --> command-option:2
 | :convert-time-to bad-zone 
 = help: :convert-time-to zone
══════════════════════════════════════════════════════════════════════
Convert the focused timestamp to the given timezone

@ -0,0 +1,25 @@
[
{
"top_meta": {
"file": "stdin",
"breadcrumbs": [
{
"display_value": "stdin",
"search_placeholder": "",
"possibilities": [
{
"display_value": "stdin"
}
]
},
{
"display_value": "2013-06-06T12:13:20.123",
"search_placeholder": "",
"possibilities": [
]
}
]
}
}
]

@ -0,0 +1,25 @@
[
{
"top_meta": {
"file": "stdin",
"breadcrumbs": [
{
"display_value": "stdin",
"search_placeholder": "",
"possibilities": [
{
"display_value": "stdin"
}
]
},
{
"display_value": "2013-06-06T19:13:20.123",
"search_placeholder": "",
"possibilities": [
]
}
]
}
}
]

@ -4,6 +4,16 @@ export TZ=UTC
export YES_COLOR=1
export DUMP_CRASH=1
run_cap_test ${lnav_test} -n \
-c ":goto 0" \
-c ":convert-time-to bad-zone" \
${test_dir}/logfile_access_log.0
run_cap_test ${lnav_test} -n \
-c ":goto 0" \
-c ":convert-time-to America/Los_Angeles" \
${test_dir}/logfile_access_log.0
run_cap_test ${lnav_test} -nN \
-c ";SELECT ':echo Hello' || char(10) || ':echo World' AS cmds" \
-c ':eval ${cmds}'

@ -1,5 +1,6 @@
#! /bin/bash
export TZ=UTC
export YES_COLOR=1
unset XDG_CONFIG_HOME
@ -97,3 +98,17 @@ run_cap_test ${lnav_test} -n \
run_cap_test ${lnav_test} -n \
-c ':goto #/catalog/1/title' \
< ${test_dir}/books.json
echo "Hello, World!" | run_cap_test env TEST_COMMENT="piper crumbs" ${lnav_test} -n \
-c ';SELECT top_meta FROM lnav_top_view' \
-c ':write-json-to -'
echo "Hello, World!" | run_cap_test \
env TEST_COMMENT="piper crumbs" TZ=America/Los_Angeles \
${lnav_test} -n \
-c ';SELECT top_meta FROM lnav_top_view' \
-c ':write-json-to -'
echo "Hello, World!" | run_cap_test \
env TEST_COMMENT="piper crumbs" TZ=America/Los_Angeles \
${lnav_test} -nt

Loading…
Cancel
Save