[keymap] add more keys to the keymap

This commit is contained in:
Timothy Stack 2018-08-12 08:37:16 -07:00
parent c3097db7af
commit 00ed12557b
14 changed files with 3803 additions and 1629 deletions

5
NEWS
View File

@ -22,6 +22,9 @@ lnav v0.8.4:
that hide/show lines based on whether they are bookmarked.
* Added the "json_contains()" SQL function to check if a JSON value
contains a number of a string.
* The relative time parser recognizes "next" at the beginning of the
input, for example, "next hour" or "next day". Handy for use in the
":goto" command.
Interface Changes:
* When typing in a search, instead of moving the view to the first match
@ -37,6 +40,8 @@ lnav v0.8.4:
to the beginning of the line.
* The :delete-filter command should now tab-complete existing filters.
* Milliseconds can now be used in relative times (e.g. 10:00:00.123)
* The J/K hotkeys were not marking lines correctly when the bottom of
the view was reached.
lnav v0.8.3:
Features:

View File

@ -547,7 +547,7 @@ string execute_from_file(exec_context &ec, const string &path, int line_number,
}
break;
case ';':
setup_logline_table();
setup_logline_table(ec);
retval = execute_sql(ec, cmdline, alt_msg);
break;
case '|':
@ -589,7 +589,7 @@ string execute_any(exec_context &ec, const string &cmdline_with_mode)
}
break;
case ';':
setup_logline_table();
setup_logline_table(ec);
retval = execute_sql(ec, cmdline, alt_msg);
break;
case '|': {
@ -636,7 +636,7 @@ void execute_init_commands(exec_context &ec, vector<pair<string, string> > &msgs
}
break;
case ';':
setup_logline_table();
setup_logline_table(ec);
msg = execute_sql(ec, cmd.substr(1), alt_msg);
break;
case '|':

File diff suppressed because it is too large Load Diff

View File

@ -140,25 +140,6 @@ static void copy_to_xclip(void)
lnav_data.ld_rl_view->set_value(buffer);
}
static void back_ten(int ten_minute)
{
textview_curses * tc = lnav_data.ld_view_stack.back();
logfile_sub_source *lss;
lss = dynamic_cast<logfile_sub_source *>(tc->get_sub_source());
if (!lss)
return;
time_t hour = rounddown_offset(lnav_data.ld_top_time,
60 * 60,
ten_minute * 10 * 60);
vis_line_t line = lss->find_from_time(hour);
--line;
lnav_data.ld_view_stack.back()->set_top(line);
}
void handle_paging_key(int ch)
{
if (lnav_data.ld_view_stack.empty()) {
@ -248,32 +229,6 @@ void handle_paging_key(int ch)
lnav_data.ld_rl_view->set_value("Cleared bookmarks");
break;
case 'w':
moveto_cluster(&bookmark_vector<vis_line_t>::next,
&logfile_sub_source::BM_WARNINGS,
tc->get_top());
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
6, ^,
"to move to next/previous hour boundary"));
break;
case 'W':
moveto_cluster(&bookmark_vector<vis_line_t>::prev,
&logfile_sub_source::BM_WARNINGS,
tc->get_top());
lnav_data.ld_rl_view->set_alt_value(HELP_MSG_2(
6, ^,
"to move to next/previous hour boundary"));
break;
case 'y':
tc->set_top(bm[&BM_QUERY].next(tc->get_top()));
break;
case 'Y':
tc->set_top(bm[&BM_QUERY].prev(tc->get_top()));
break;
case '>':
{
std::pair<int, int> range;
@ -422,7 +377,7 @@ void handle_paging_key(int ch)
unsigned long width;
tc->get_dimensions(height, width);
if (lnav_data.ld_last_user_mark[tc] > tc->get_bottom() - 2 &&
if (lnav_data.ld_last_user_mark[tc] > (tc->get_bottom() - 2) &&
tc->get_top() + height < tc->get_inner_height()) {
tc->shift_top(vis_line_t(1));
}
@ -558,49 +513,6 @@ void handle_paging_key(int ch)
}
break;
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
if (lss) {
int ten_minute = (ch - '0') * 10 * 60;
time_t hour = rounddown(lnav_data.ld_top_time +
(60 * 60) -
ten_minute +
1,
60 * 60);
vis_line_t line = lss->find_from_time(hour + ten_minute);
tc->set_top(line);
}
break;
case '!':
back_ten(1);
break;
case '@':
back_ten(2);
break;
case '#':
back_ten(3);
break;
case '$':
back_ten(4);
break;
case '%':
back_ten(5);
break;
case '^':
back_ten(6);
break;
case '9':
if (lss) {
double tenth = ((double)tc->get_inner_height()) / 10.0;
@ -866,7 +778,7 @@ void handle_paging_key(int ch)
lnav_data.ld_exec_context.ec_top_line = tc->get_top();
lnav_data.ld_mode = LNM_SQL;
setup_logline_table();
setup_logline_table(lnav_data.ld_exec_context);
lnav_data.ld_rl_view->focus(LNM_SQL, ";");
lnav_data.ld_bottom_source.update_loading(0, 0);
@ -942,16 +854,6 @@ void handle_paging_key(int ch)
tc->reload_data();
break;
case 'i':
if (toggle_view(&lnav_data.ld_views[LNV_HISTOGRAM])) {
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_2(z, Z, "to zoom in/out"));
}
else {
lnav_data.ld_rl_view->set_alt_value("");
}
break;
case 'I':
{
time_t log_top = lnav_data.ld_top_time;
@ -1088,10 +990,6 @@ void handle_paging_key(int ch)
tc->set_needs_update();
break;
case 'X':
lnav_data.ld_rl_view->set_value(execute_command(ec, "close"));
break;
case '\\':
{
string ex;
@ -1116,38 +1014,40 @@ void handle_paging_key(int ch)
"Use the 'goto' command to set the relative time to move by");
}
else {
vis_line_t vl = tc->get_top();
vis_line_t vl = tc->get_top(), new_vl;
relative_time rt = lnav_data.ld_last_relative_time;
struct timeval tv;
content_line_t cl;
struct exttm tm;
bool done = false;
if (ch == 'R') {
rt.negate();
if (ch == 'r') {
if (rt.is_negative()) {
rt.negate();
}
} else if (ch == 'R') {
if (!rt.is_negative()) {
rt.negate();
}
}
cl = lnav_data.ld_log_source.at(vl);
logline *ll = lnav_data.ld_log_source.find_line(cl);
ll->to_exttm(tm);
rt.add(tm);
tv.tv_sec = timegm(&tm.et_tm);
tv.tv_usec = tm.et_nsec / 1000;
vl = lnav_data.ld_log_source.find_from_time(tv);
if (rt.is_negative() && (vl > vis_line_t(0))) {
--vl;
if (vl == tc->get_top()) {
vl = vis_line_t(0);
do {
rt.add(tm);
new_vl = lnav_data.ld_log_source.find_from_time(tm);
if (new_vl == 0_vl || new_vl != vl || !rt.is_relative()) {
vl = new_vl;
done = true;
}
}
} while (!done);
tc->set_top(vl);
lnav_data.ld_rl_view->set_value(" " + rt.to_string());
}
}
break;
case KEY_CTRL_R:
reset_session();
break;
case KEY_CTRL_W:
execute_command(ec, lnav_data.ld_views[LNV_LOG].get_word_wrap() ?
"disable-word-wrap" : "enable-word-wrap");

View File

@ -1,17 +1,29 @@
{
"global": {
"keymap_def_alt_warning": "Press ${ansi_bold}w${ansi_norm}/${ansi_bold}W${ansi_norm} to move forward/backward through ${ansi_yellow}warning${ansi_norm} messages",
"keymap_def_alt_hour_boundary": "Press ${ansi_bold}6${ansi_bold}/${ansi_bold}^${ansi_norm} to move to the next/previous hour boundary",
"keymap_def_scroll_horiz": "Press \\'${ansi_bold}>${ansi_norm}\\' or \\'${ansi_bold}<${ansi_norm}\\' to scroll horizontally to a search result",
"keymap_def_next_user_mark": "Press ${ansi_bold}u${ansi_norm}/${ansi_bold}U${ansi_norm} to move forward/backward through user bookmarks",
"keymap_def_db_view": "Press ${ansi_bold}v${ansi_norm}/${ansi_bold}V${ansi_norm} to switch to the SQL result view",
"keymap_def_hist_view": "Press ${ansi_bold}i${ansi_norm}/${ansi_bold}I${ansi_norm} to switch to the histogram view",
"keymap_def_text_view": "Press ${ansi_bold}t${ansi_norm} to switch to the text view"
"keymap_def_text_view": "Press ${ansi_bold}t${ansi_norm} to switch to the text view",
"keymap_def_pop_view": "Press ${ansi_bold}q${ansi_norm} to return to the previous view",
"keymap_def_zoom": "Press ${ansi_bold}z${ansi_norm}/${ansi_bold}z${ansi_norm} to zoom in/out"
},
"keymap_def": {
"default": {
"x0c": [
":write-raw-to -"
],
"x12": [
":reset-session"
],
"x58": [
":close"
],
"x45": [
":prev-mark error",
":eval :alt-msg ${keymap_def_alt_warning}"
@ -21,6 +33,22 @@
":eval :alt-msg ${keymap_def_alt_warning}"
],
"x57": [
":prev-mark warning",
":eval :alt-msg ${keymap_def_alt_hour_boundary}"
],
"x77": [
":next-mark warning",
":eval :alt-msg ${keymap_def_alt_hour_boundary}"
],
"x59": [
":prev-mark query"
],
"x79": [
":next-mark query"
],
"x67": [":goto 0"],
"x6d": [
":mark",
@ -36,8 +64,29 @@
":eval :alt-msg ${keymap_def_scroll_horiz}"
],
"x31": [":goto next 10 minutes after the hour"],
"x32": [":goto next 20 minutes after the hour"],
"x33": [":goto next 30 minutes after the hour"],
"x34": [":goto next 40 minutes after the hour"],
"x35": [":goto next 50 minutes after the hour"],
"x36": [":goto next hour"],
"x21": [":goto last 10 minutes after the hour"],
"x40": [":goto last 20 minutes after the hour"],
"x23": [":goto last 30 minutes after the hour"],
"x24": [":goto last 40 minutes after the hour"],
"x25": [":goto last 50 minutes after the hour"],
"x5e": [":goto last hour"],
"x3f": [":toggle-view help"],
"x50": [":toggle-view pretty"],
"x69": [
":toggle-view histogram",
":eval :alt-msg ${keymap_def_zoom}"
],
"x50": [
":toggle-view pretty",
":eval :alt-msg ${keymap_def_pop_view}"
],
"x76": [":toggle-view db"],
"x71": ["|lnav-pop-view ${keyseq}"],

View File

@ -262,7 +262,7 @@ static void regenerate_unique_file_names()
upg.generate();
}
bool setup_logline_table()
bool setup_logline_table(exec_context &ec)
{
// Hidden columns don't show up in the table_info pragma.
static const char *hidden_table_columns[] = {
@ -284,7 +284,7 @@ bool setup_logline_table()
bool retval = false;
bool update_possibilities = (
lnav_data.ld_rl_view != NULL &&
lnav_data.ld_exec_context.ec_local_vars.size() == 1);
ec.ec_local_vars.size() == 1);
if (update_possibilities) {
lnav_data.ld_rl_view->clear_possibilities(LNM_SQL, "*");
@ -1358,7 +1358,7 @@ static void clear_last_user_mark(void *, listview_curses *lv)
{
textview_curses *tc = (textview_curses *) lv;
if (lnav_data.ld_select_start.find(tc) != lnav_data.ld_select_start.end() &&
lnav_data.ld_select_start[tc] != tc->get_top()) {
!tc->is_visible(vis_line_t(lnav_data.ld_last_user_mark[tc]))) {
lnav_data.ld_select_start.erase(tc);
lnav_data.ld_last_user_mark.erase(tc);
}

View File

@ -334,7 +334,7 @@ void rebuild_indexes(bool force);
bool ensure_view(textview_curses *expected_tc);
bool toggle_view(textview_curses *toggle_tc);
bool setup_logline_table();
bool setup_logline_table(exec_context &ec);
void execute_search(lnav_view_t view, const std::string &regex);

View File

@ -270,10 +270,11 @@ static string com_goto(exec_context &ec, string cmdline, vector<string> &args)
if (rt.parse(all_args, pe)) {
if (tc == &lnav_data.ld_views[LNV_LOG]) {
content_line_t cl;
vis_line_t vl;
vis_line_t vl, new_vl;
logline *ll;
bool done = false;
if (!rt.is_absolute()) {
if (rt.is_relative()) {
lnav_data.ld_last_relative_time = rt;
}
@ -281,17 +282,23 @@ static string com_goto(exec_context &ec, string cmdline, vector<string> &args)
cl = lnav_data.ld_log_source.at(vl);
ll = lnav_data.ld_log_source.find_line(cl);
ll->to_exttm(tm);
rt.add(tm);
tv.tv_sec = timegm(&tm.et_tm);
tv.tv_usec = tm.et_nsec / 1000;
vl = lnav_data.ld_log_source.find_from_time(tv);
do {
rt.add(tm);
new_vl = lnav_data.ld_log_source.find_from_time(tm);
if (new_vl == 0_vl || new_vl != vl || !rt.is_relative()) {
vl = new_vl;
done = true;
}
} while (!done);
if (ec.ec_dry_run) {
retval = "info: will move to line " + to_string((int) vl);
} else {
tc->set_top(vl);
retval = "";
if (!rt.is_absolute() && lnav_data.ld_rl_view != NULL) {
if (!rt.is_absolute() && lnav_data.ld_rl_view != nullptr) {
lnav_data.ld_rl_view->set_alt_value(
HELP_MSG_2(r, R,
"to move forward/backward the same amount of time"));
@ -301,7 +308,8 @@ static string com_goto(exec_context &ec, string cmdline, vector<string> &args)
retval = "error: relative time values only work in the log view";
}
}
else if (dts.scan(args[1].c_str(), args[1].size(), NULL, &tm, tv) != NULL) {
else if (dts.scan(args[1].c_str(), args[1].size(), nullptr, &tm, tv) !=
nullptr) {
if (tc == &lnav_data.ld_views[LNV_LOG]) {
vis_line_t vl;
@ -2174,7 +2182,7 @@ static string com_summarize(exec_context &ec, string cmdline, vector<string> &ar
args.emplace_back("colname");
return retval;
}
else if (!setup_logline_table()) {
else if (!setup_logline_table(ec)) {
retval = "error: no log data available";
}
else if (args.size() == 1) {

View File

@ -419,6 +419,15 @@ public:
return this->find_from_time(tv);
};
vis_line_t find_from_time(exttm &etm) {
struct timeval tv;
tv.tv_sec = timegm(&etm.et_tm);
tv.tv_usec = etm.et_nsec / 1000;
return this->find_from_time(tv);
};
time_t time_for_row(int row) {
return this->find_line(this->at(vis_line_t(row)))->get_time();
};

View File

@ -66,11 +66,15 @@ static struct {
{ "tomo", pcrepp("\\Atomorrow\\b") },
{ "noon", pcrepp("\\Anoon\\b") },
{ "and", pcrepp("\\Aand\\b") },
{ "the", pcrepp("\\Athe\\b") },
{ "ago", pcrepp("\\Aago\\b") },
{ "lter", pcrepp("\\Alater\\b") },
{ "bfor", pcrepp("\\Abefore\\b") },
{ "aft", pcrepp("\\Aafter\\b") },
{ "now", pcrepp("\\Anow\\b") },
{ "here", pcrepp("\\Ahere\\b") },
{ "next", pcrepp("\\Anext\\b") },
{ "previous", pcrepp("\\Aprevious|last\\b") },
};
static int64_t TIME_SCALES[] = {
@ -80,12 +84,24 @@ static int64_t TIME_SCALES[] = {
24,
};
const char relative_time::FIELD_CHARS[] = {
'u',
's',
'm',
'h',
'd',
'M',
'y',
};
bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_out)
{
pcre_input pi(str, 0, len);
pcre_context_static<30> pc;
int64_t number = 0;
bool number_set = false;
bool number_set = false, number_was_set = false;
bool next_set = false;
token_t base_token = RTT_INVALID;
pe_out.pe_column = -1;
pe_out.pe_msg.clear();
@ -112,9 +128,17 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
found = true;
if (RTT_MICROS <= token && token <= RTT_YEARS) {
if (!number_set) {
pe_out.pe_msg = "Expecting a number before time unit";
return false;
if (base_token != RTT_INVALID) {
base_token = RTT_INVALID;
this->rt_is_absolute = true;
continue;
}
if (!this->rt_next && !this->rt_previous) {
pe_out.pe_msg = "Expecting a number before time unit";
return false;
}
}
number_was_set = number_set;
number_set = false;
}
switch (token) {
@ -123,7 +147,6 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
case RTT_NOW: {
struct timeval tv;
struct exttm tm;
int abs_start = 0, abs_end = RTF__MAX;
gettimeofday(&tv, NULL);
localtime_r(&tv.tv_sec, &tm.et_tm);
@ -141,7 +164,7 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
this->rt_field[RTF_MICROSECONDS] = tm.et_nsec / 1000;
break;
case RTT_YESTERDAY:
this->rt_field[RTF_DAYS] -= 1;
this->rt_field[RTF_DAYS].value -= 1;
case RTT_TODAY:
this->rt_field[RTF_HOURS] = 0;
this->rt_field[RTF_MINUTES] = 0;
@ -151,39 +174,35 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
default:
break;
}
for (int lpc = abs_start; lpc <= abs_end; lpc++) {
this->rt_is_absolute[lpc] = true;
}
this->rt_is_absolute = true;
break;
}
case RTT_INVALID:
case RTT_WHITE:
case RTT_AND:
case RTT_THE:
break;
case RTT_AM:
case RTT_PM:
if (number_set) {
this->rt_field[RTF_HOURS] = number;
this->rt_is_absolute[RTF_HOURS] = true;
this->rt_field[RTF_MINUTES] = 0;
this->rt_is_absolute[RTF_MINUTES] = true;
this->rt_field[RTF_SECONDS] = 0;
this->rt_is_absolute[RTF_SECONDS] = true;
this->rt_field[RTF_MICROSECONDS] = 0;
this->rt_is_absolute[RTF_MICROSECONDS] = true;
this->rt_is_absolute = true;
number_set = false;
}
if (!this->rt_is_absolute[RTF_HOURS]) {
if (!this->rt_is_absolute) {
pe_out.pe_msg = "Expecting absolute time with A.M. or P.M.";
return false;
}
if (token == RTT_AM) {
if (this->rt_field[RTF_HOURS] == 12) {
if (this->rt_field[RTF_HOURS].value == 12) {
this->rt_field[RTF_HOURS] = 0;
}
}
else if (this->rt_field[RTF_HOURS] < 12) {
this->rt_field[RTF_HOURS] += 12;
else if (this->rt_field[RTF_HOURS].value < 12) {
this->rt_field[RTF_HOURS].value += 12;
}
break;
case RTT_A:
@ -197,9 +216,7 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
string hstr = pi.get_substr(pc[0]);
string mstr = pi.get_substr(pc[1]);
this->rt_field[RTF_HOURS] = atoi(hstr.c_str());
this->rt_is_absolute[RTF_HOURS] = true;
this->rt_field[RTF_MINUTES] = atoi(mstr.c_str());
this->rt_is_absolute[RTF_MINUTES] = true;
if (pc[2]->is_valid()) {
string sstr = pi.get_substr(pc[2]);
this->rt_field[RTF_SECONDS] = atoi(sstr.c_str());
@ -222,8 +239,7 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
this->rt_field[RTF_SECONDS] = 0;
this->rt_field[RTF_MICROSECONDS] = 0;
}
this->rt_is_absolute[RTF_SECONDS] = true;
this->rt_is_absolute[RTF_MICROSECONDS] = true;
this->rt_is_absolute = true;
break;
}
case RTT_NUMBER: {
@ -248,25 +264,70 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
this->rt_field[RTF_MICROSECONDS] = number * 1000;
break;
case RTT_SECONDS:
this->rt_field[RTF_SECONDS] = number;
if (number_was_set) {
this->rt_field[RTF_SECONDS] = number;
} else if (next_set) {
this->rt_field[RTF_MICROSECONDS] = 0;
this->rt_is_absolute = true;
}
break;
case RTT_MINUTES:
this->rt_field[RTF_MINUTES] = number;
if (number_was_set) {
this->rt_field[RTF_MINUTES] = number;
} else if (next_set) {
this->rt_field[RTF_MICROSECONDS] = 0;
this->rt_field[RTF_SECONDS] = 0;
this->rt_is_absolute = true;
}
break;
case RTT_HOURS:
this->rt_field[RTF_HOURS] = number;
if (number_was_set) {
this->rt_field[RTF_HOURS] = number;
} else if (next_set) {
this->rt_field[RTF_MICROSECONDS] = 0;
this->rt_field[RTF_SECONDS] = 0;
this->rt_field[RTF_MINUTES] = 0;
this->rt_is_absolute = true;
}
break;
case RTT_DAYS:
this->rt_field[RTF_DAYS] = number;
if (number_was_set) {
this->rt_field[RTF_DAYS] = number;
} else if (next_set) {
this->rt_field[RTF_MICROSECONDS] = 0;
this->rt_field[RTF_SECONDS] = 0;
this->rt_field[RTF_MINUTES] = 0;
this->rt_field[RTF_HOURS] = 0;
this->rt_is_absolute = true;
}
break;
case RTT_WEEKS:
this->rt_field[RTF_DAYS] = number * 7;
break;
case RTT_MONTHS:
this->rt_field[RTF_MONTHS] = number;
if (number_was_set) {
this->rt_field[RTF_MONTHS] = number;
} else if (next_set) {
this->rt_field[RTF_MICROSECONDS] = 0;
this->rt_field[RTF_SECONDS] = 0;
this->rt_field[RTF_MINUTES] = 0;
this->rt_field[RTF_HOURS] = 0;
this->rt_field[RTF_DAYS] = 0;
this->rt_is_absolute = true;
}
break;
case RTT_YEARS:
this->rt_field[RTF_YEARS] = number;
if (number_was_set) {
this->rt_field[RTF_YEARS] = number;
} else if (next_set) {
this->rt_field[RTF_MICROSECONDS] = 0;
this->rt_field[RTF_SECONDS] = 0;
this->rt_field[RTF_MINUTES] = 0;
this->rt_field[RTF_HOURS] = 0;
this->rt_field[RTF_DAYS] = 0;
this->rt_field[RTF_MONTHS] = 0;
this->rt_is_absolute = true;
}
break;
case RTT_BEFORE:
case RTT_AGO:
@ -275,11 +336,14 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
return false;
}
for (int field = 0; field < RTF__MAX; field++) {
if (this->rt_field[field] > 0) {
this->rt_field[field] = -this->rt_field[field];
if (this->rt_field[field].value > 0) {
this->rt_field[field] = -this->rt_field[field].value;
}
}
break;
case RTT_AFTER:
base_token = token;
break;
case RTT_LATER:
if (this->empty()) {
pe_out.pe_msg = "Expecting a time unit before 'later'";
@ -288,15 +352,24 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
break;
case RTT_HERE:
break;
case RTT_NEXT:
this->rt_next = true;
next_set = true;
break;
case RTT_PREVIOUS:
this->rt_previous = true;
next_set = true;
break;
case RTT_TOMORROW:
this->rt_field[RTF_DAYS] = 1;
break;
case RTT_NOON:
this->rt_field[RTF_HOURS] = 12;
this->rt_is_absolute[RTF_HOURS] = true;
for (int lpc = RTF_MICROSECONDS; lpc < RTF_HOURS; lpc++) {
this->rt_field[lpc] = 0;
this->rt_is_absolute[lpc] = true;
this->rt_is_absolute = true;
for (int lpc2 = RTF_MICROSECONDS;
lpc2 < RTF_HOURS;
lpc2++) {
this->rt_field[lpc2] = 0;
}
break;
@ -304,6 +377,14 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
assert(false);
break;
}
if (token != RTT_NEXT &&
token != RTT_PREVIOUS &&
token != RTT_WHITE) {
next_set = false;
}
number_was_set = false;
}
if (!found) {
@ -316,22 +397,96 @@ bool relative_time::parse(const char *str, size_t len, struct parse_error &pe_ou
void relative_time::rollover()
{
for (int lpc = 0; lpc < RTF_DAYS; lpc++) {
int64_t val = this->rt_field[lpc];
if (!this->rt_field[lpc].is_set) {
continue;
}
int64_t val = this->rt_field[lpc].value;
this->rt_field[lpc] = val % TIME_SCALES[lpc];
this->rt_field[lpc + 1] += val / TIME_SCALES[lpc];
this->rt_field[lpc + 1].value += val / TIME_SCALES[lpc];
}
if (std::abs(this->rt_field[RTF_DAYS]) > 31) {
int64_t val = this->rt_field[RTF_DAYS];
if (std::abs(this->rt_field[RTF_DAYS].value) > 31) {
int64_t val = this->rt_field[RTF_DAYS].value;
this->rt_field[RTF_DAYS] = val % 31;
this->rt_field[RTF_MONTHS] += val / 31;
this->rt_field[RTF_MONTHS].value += val / 31;
}
if (std::abs(this->rt_field[RTF_MONTHS]) > 12) {
int64_t val = this->rt_field[RTF_MONTHS];
if (std::abs(this->rt_field[RTF_MONTHS].value) > 12) {
int64_t val = this->rt_field[RTF_MONTHS].value;
this->rt_field[RTF_MONTHS] = val % 12;
this->rt_field[RTF_YEARS] += val / 12;
this->rt_field[RTF_YEARS].value += val / 12;
}
}
std::string relative_time::to_string()
{
char dst[128] = "";
char *pos = dst;
if (this->is_absolute()) {
pos += snprintf(pos, sizeof(dst) - (pos - dst),
"%s",
this->rt_next ? "next " :
(this->rt_previous ? "last " : ""));
if (this->rt_field[RTF_YEARS].is_set &&
(this->rt_next || this->rt_previous ||
this->rt_field[RTF_YEARS].value != 0)) {
pos += snprintf(pos, sizeof(dst) - (pos - dst),
"year %" PRId64 " ",
this->rt_field[RTF_YEARS].value);
} else if ((this->rt_next || this->rt_previous) &&
this->rt_field[RTF_MONTHS].is_set) {
pos += snprintf(pos, sizeof(dst) - (pos - dst), "year ");
}
if (this->rt_field[RTF_MONTHS].is_set &&
(this->rt_next || this->rt_previous ||
this->rt_field[RTF_MONTHS].value != 0)) {
pos += snprintf(pos, sizeof(dst) - (pos - dst),
"month %" PRId64 " ",
this->rt_field[RTF_MONTHS].value);
} else if ((this->rt_next || this->rt_previous) &&
this->rt_field[RTF_DAYS].is_set) {
pos += snprintf(pos, sizeof(dst) - (pos - dst), "month ");
}
if (this->rt_field[RTF_DAYS].is_set &&
(this->rt_next || this->rt_previous ||
this->rt_field[RTF_DAYS].value != 0)) {
pos += snprintf(pos, sizeof(dst) - (pos - dst),
"day %" PRId64 " ",
this->rt_field[RTF_DAYS].value);
} else if ((this->rt_next || this->rt_previous) &&
this->rt_field[RTF_HOURS].is_set) {
pos += snprintf(pos, sizeof(dst) - (pos - dst), "day ");
}
pos += snprintf(pos, sizeof(dst) - (pos - dst),
"%" PRId64 ":%02" PRId64,
this->rt_field[RTF_HOURS].value,
this->rt_field[RTF_MINUTES].value);
if (this->rt_field[RTF_SECONDS].is_set &&
this->rt_field[RTF_SECONDS].value != 0) {
pos += snprintf(pos, sizeof(dst) - (pos - dst),
":%.02" PRId64,
this->rt_field[RTF_SECONDS].value);
if (this->rt_field[RTF_MICROSECONDS].is_set &&
this->rt_field[RTF_MICROSECONDS].value != 0) {
pos += snprintf(pos, sizeof(dst) - (pos - dst),
".%.03" PRId64,
this->rt_field[RTF_MICROSECONDS].value / 1000);
}
}
} else {
for (int lpc = RTF__MAX - 1; lpc >= 0; lpc--) {
if (this->rt_field[lpc].value == 0) {
continue;
}
pos += snprintf(pos, sizeof(dst) - (pos - dst),
"%" PRId64 "%c",
this->rt_field[lpc].value,
FIELD_CHARS[lpc]);
}
}
return dst;
}
size_t str2reltime(int64_t millis, std::string &value_out)
{
/* 24h22m33s111 */

View File

@ -68,11 +68,15 @@ public:
RTT_TOMORROW,
RTT_NOON,
RTT_AND,
RTT_THE,
RTT_AGO,
RTT_LATER,
RTT_BEFORE,
RTT_AFTER,
RTT_NOW,
RTT_HERE,
RTT_NEXT,
RTT_PREVIOUS,
RTT__MAX
};
@ -83,20 +87,35 @@ public:
void clear() {
memset(this->rt_field, 0, sizeof(this->rt_field));
memset(this->rt_is_absolute, 0, sizeof(this->rt_is_absolute));
this->rt_next = false;
this->rt_previous = false;
this->rt_is_absolute = false;
};
void negate() {
if (this->rt_is_absolute) {
if (this->rt_next) {
this->rt_next = false;
this->rt_previous = true;
} else if (this->rt_previous) {
this->rt_next = true;
this->rt_previous = false;
}
return;
}
for (int lpc = 0; lpc < RTF__MAX; lpc++) {
if (!this->rt_is_absolute[lpc] && this->rt_field[lpc] != 0) {
this->rt_field[lpc] = -this->rt_field[lpc];
if (this->rt_field[lpc].value != 0) {
this->rt_field[lpc].value = -this->rt_field[lpc].value;
}
}
};
bool is_negative() const {
for (int lpc = 0; lpc < RTF__MAX; lpc++) {
if (this->rt_field[lpc] < 0) {
if (this->rt_previous) {
return true;
}
for (auto rtf : this->rt_field) {
if (rtf.value < 0) {
return true;
}
}
@ -104,17 +123,16 @@ public:
};
bool is_absolute() const {
for (int lpc = 0; lpc < RTF__MAX; lpc++) {
if (this->rt_is_absolute[lpc]) {
return true;
}
}
return false;
return this->rt_is_absolute;
};
bool is_relative() const {
return !this->rt_is_absolute || this->rt_next || this->rt_previous;
}
bool empty() const {
for (int lpc = 0; lpc < RTF__MAX; lpc++) {
if (this->rt_field[lpc]) {
for (auto rtf : this->rt_field) {
if (rtf.is_set) {
return false;
}
}
@ -144,59 +162,99 @@ public:
};
void add(struct exttm &tm) {
if (this->rt_is_absolute[RTF_MICROSECONDS]) {
tm.et_nsec = this->rt_field[RTF_MICROSECONDS] * 1000;
if (this->rt_field[RTF_MICROSECONDS].is_set && this->rt_is_absolute) {
tm.et_nsec = this->rt_field[RTF_MICROSECONDS].value * 1000;
}
else {
tm.et_nsec += this->rt_field[RTF_MICROSECONDS] * 1000;
tm.et_nsec += this->rt_field[RTF_MICROSECONDS].value * 1000;
}
if (this->rt_is_absolute[RTF_SECONDS]) {
tm.et_tm.tm_sec = this->rt_field[RTF_SECONDS];
if (this->rt_field[RTF_SECONDS].is_set && this->rt_is_absolute) {
if (this->rt_next &&
this->rt_field[RTF_SECONDS].value <= tm.et_tm.tm_sec) {
tm.et_tm.tm_min += 1;
}
if (this->rt_previous &&
this->rt_field[RTF_SECONDS].value >= tm.et_tm.tm_sec) {
tm.et_tm.tm_min -= 1;
}
tm.et_tm.tm_sec = this->rt_field[RTF_SECONDS].value;
}
else {
tm.et_tm.tm_sec += this->rt_field[RTF_SECONDS];
tm.et_tm.tm_sec += this->rt_field[RTF_SECONDS].value;
}
if (this->rt_is_absolute[RTF_MINUTES]) {
tm.et_tm.tm_min = this->rt_field[RTF_MINUTES];
if (this->rt_field[RTF_MINUTES].is_set && this->rt_is_absolute) {
if (this->rt_next &&
this->rt_field[RTF_MINUTES].value <= tm.et_tm.tm_min) {
tm.et_tm.tm_hour += 1;
}
if (this->rt_previous && (this->rt_field[RTF_MINUTES].value == 0 ||
(this->rt_field[RTF_MINUTES].value >= tm.et_tm.tm_min))) {
tm.et_tm.tm_hour -= 1;
}
tm.et_tm.tm_min = this->rt_field[RTF_MINUTES].value;
}
else {
tm.et_tm.tm_min += this->rt_field[RTF_MINUTES];
tm.et_tm.tm_min += this->rt_field[RTF_MINUTES].value;
}
if (this->rt_is_absolute[RTF_HOURS]) {
tm.et_tm.tm_hour = this->rt_field[RTF_HOURS];
if (this->rt_field[RTF_HOURS].is_set && this->rt_is_absolute) {
if (this->rt_next &&
this->rt_field[RTF_HOURS].value <= tm.et_tm.tm_hour) {
tm.et_tm.tm_mday += 1;
}
if (this->rt_previous &&
this->rt_field[RTF_HOURS].value >= tm.et_tm.tm_hour) {
tm.et_tm.tm_mday -= 1;
}
tm.et_tm.tm_hour = this->rt_field[RTF_HOURS].value;
}
else {
tm.et_tm.tm_hour += this->rt_field[RTF_HOURS];
tm.et_tm.tm_hour += this->rt_field[RTF_HOURS].value;
}
if (this->rt_is_absolute[RTF_DAYS]) {
tm.et_tm.tm_mday = this->rt_field[RTF_DAYS];
if (this->rt_field[RTF_DAYS].is_set && this->rt_is_absolute) {
if (this->rt_next &&
this->rt_field[RTF_DAYS].value <= tm.et_tm.tm_hour) {
tm.et_tm.tm_mon += 1;
}
if (this->rt_previous &&
this->rt_field[RTF_DAYS].value >= tm.et_tm.tm_hour) {
tm.et_tm.tm_mon -= 1;
}
tm.et_tm.tm_mday = this->rt_field[RTF_DAYS].value;
}
else {
tm.et_tm.tm_mday += this->rt_field[RTF_DAYS];
tm.et_tm.tm_mday += this->rt_field[RTF_DAYS].value;
}
if (this->rt_is_absolute[RTF_MONTHS]) {
tm.et_tm.tm_mon = this->rt_field[RTF_MONTHS];
if (this->rt_field[RTF_MONTHS].is_set && this->rt_is_absolute) {
if (this->rt_next &&
this->rt_field[RTF_MONTHS].value <= tm.et_tm.tm_mon) {
tm.et_tm.tm_year += 1;
}
if (this->rt_previous &&
this->rt_field[RTF_MONTHS].value >= tm.et_tm.tm_mon) {
tm.et_tm.tm_year -= 1;
}
tm.et_tm.tm_mon = this->rt_field[RTF_MONTHS].value;
}
else {
tm.et_tm.tm_mon += this->rt_field[RTF_MONTHS];
tm.et_tm.tm_mon += this->rt_field[RTF_MONTHS].value;
}
if (this->rt_is_absolute[RTF_YEARS]) {
tm.et_tm.tm_year = this->rt_field[RTF_YEARS];
if (this->rt_field[RTF_YEARS].is_set && this->rt_is_absolute) {
tm.et_tm.tm_year = this->rt_field[RTF_YEARS].value;
}
else {
tm.et_tm.tm_year += this->rt_field[RTF_YEARS];
tm.et_tm.tm_year += this->rt_field[RTF_YEARS].value;
}
};
int64_t to_microseconds() {
int64_t retval;
retval = this->rt_field[RTF_YEARS] * 12;
retval = (retval + this->rt_field[RTF_MONTHS]) * 30;
retval = (retval + this->rt_field[RTF_DAYS]) * 24;
retval = (retval + this->rt_field[RTF_HOURS]) * 60;
retval = (retval + this->rt_field[RTF_MINUTES]) * 60;
retval = (retval + this->rt_field[RTF_SECONDS]) * 1000 * 1000;
retval = this->rt_field[RTF_YEARS].value * 12;
retval = (retval + this->rt_field[RTF_MONTHS].value) * 30;
retval = (retval + this->rt_field[RTF_DAYS].value) * 24;
retval = (retval + this->rt_field[RTF_HOURS].value) * 60;
retval = (retval + this->rt_field[RTF_MINUTES].value) * 60;
retval = (retval + this->rt_field[RTF_SECONDS].value) * 1000 * 1000;
return retval;
};
@ -208,27 +266,7 @@ public:
tv_out.tv_usec = us % (1000 * 1000);
};
std::string to_string() {
char dst[128];
snprintf(dst, sizeof(dst),
"%" PRId64 "%c%" PRId64 "%c%" PRId64 "%c%" PRId64 "%c%" PRId64 "%c%" PRId64 "%c%" PRId64 "%c",
this->rt_field[RTF_YEARS],
this->rt_is_absolute[RTF_YEARS] ? 'Y' : 'y',
this->rt_field[RTF_MONTHS],
this->rt_is_absolute[RTF_MONTHS] ? 'M' : 'm',
this->rt_field[RTF_DAYS],
this->rt_is_absolute[RTF_DAYS] ? 'D' : 'd',
this->rt_field[RTF_HOURS],
this->rt_is_absolute[RTF_HOURS] ? 'H' : 'h',
this->rt_field[RTF_MINUTES],
this->rt_is_absolute[RTF_MINUTES] ? 'M' : 'm',
this->rt_field[RTF_SECONDS],
this->rt_is_absolute[RTF_SECONDS] ? 'S' : 's',
this->rt_field[RTF_MICROSECONDS],
this->rt_is_absolute[RTF_MICROSECONDS] ? 'U' : 'u');
return dst;
};
std::string to_string();
void rollover();
@ -244,8 +282,22 @@ public:
RTF__MAX
};
int64_t rt_field[RTF__MAX];
bool rt_is_absolute[RTF__MAX];
static const char FIELD_CHARS[RTF__MAX];
struct _rt_field {
_rt_field(int64_t value) : value(value), is_set(true) {
};
_rt_field() : value(0), is_set(false) {
};
int64_t value;
bool is_set;
} rt_field[RTF__MAX];
bool rt_next;
bool rt_previous;
bool rt_is_absolute;
};
size_t str2reltime(int64_t millis, std::string &value_out);

View File

@ -29,9 +29,9 @@ add_executable(test_abbrev test_abbrev.cc
../src/spookyhash/SpookyV2.cpp)
add_executable(drive_sql_anno drive_sql_anno.cc ../src/lnav_log.cc ../src/pcrepp.cc)
link_directories(/opt/local/lib)
target_link_libraries(test_pcrepp /opt/local/lib/libpcre.a)
target_link_libraries(test_reltime /opt/local/lib/libpcre.a)
target_link_libraries(lnav_doctests /opt/local/lib/libpcre.a)
target_link_libraries(test_date_time_scanner /opt/local/lib/libpcre.a)
target_link_libraries(test_abbrev /opt/local/lib/libpcre.a)
target_link_libraries(drive_sql_anno /opt/local/lib/libpcre.a)
target_link_libraries(test_pcrepp /usr/local/lib/libpcre.a)
target_link_libraries(test_reltime /usr/local/lib/libpcre.a)
target_link_libraries(lnav_doctests /usr/local/lib/libpcre.a)
target_link_libraries(test_date_time_scanner /usr/local/lib/libpcre.a)
target_link_libraries(test_abbrev /usr/local/lib/libpcre.a)
target_link_libraries(drive_sql_anno /usr/local/lib/libpcre.a)

View File

@ -771,6 +771,16 @@ check_output "rollover is not working with histogram" <<EOF
Wed Jan 03 09:20:00 1 normal 0 errors 0 warnings 0 marks
EOF
run_test ${lnav_test} -n \
-c ":goto 0" \
-c ":goto next year" \
logfile_rollover.1.live
check_output ":goto next year is not working" <<EOF
Feb 3 09:23:38 veridian automount[7998]: lookup(file): lookup for foobar failed
Jan 3 09:23:38 veridian automount[16442]: attempting to mount entry /auto/opt
EOF
touch -t 200711030923 ${srcdir}/logfile_syslog.0
run_test ${lnav_test} -n \
-c ":switch-to-view histogram" \

View File

@ -29,10 +29,12 @@
#include "config.h"
#include <assert.h>
#include <sys/time.h>
#include <intern_string.hh>
#include "intern_string.hh"
#define DOCTEST_CONFIG_IMPLEMENT_WITH_MAIN
#include "doctest.hh"
#include "relative_time.hh"
using namespace std;
@ -41,21 +43,29 @@ static struct {
const char *reltime;
const char *expected;
} TEST_DATA[] = {
{ "a minute ago", "0y0m0d0h-1m0s0u" },
{ "1m ago", "0y0m0d0h-1m0s0u" },
{ "a min ago", "0y0m0d0h-1m0s0u" },
{ "a m ago", "0y0m0d0h-1m0s0u" },
{ "+1 minute ago", "0y0m0d0h-1m0s0u" },
{ "-1 minute ago", "0y0m0d0h-1m0s0u" },
{ "-1 minute", "0y0m0d0h-1m0s0u" },
{ "1:40", "0y0m0d1H40M0S0U" },
{ "01:40", "0y0m0d1H40M0S0U" },
{ "1h40m", "0y0m0d1h40m0s0u" },
{ "1pm", "0y0m0d13H0M0S0U" },
{ "12pm", "0y0m0d12H0M0S0U" },
{ "00:27:18.567", "0y0m0d0H27M18S567000U" },
// { "10 minutes after the next hour", "next 0:10" },
{ "next day", "next day 0:00" },
{ "next month", "next month day 0 0:00" },
{ "next year", "next year month 0 day 0 0:00" },
{ "last hour", "last 0:00" },
{ "next 10 minutes after the hour", "next 0:10" },
{ "1h50m", "1h50m" },
{ "next hour", "next 0:00" },
{ "a minute ago", "-1m" },
{ "1m ago", "-1m" },
{ "a min ago", "-1m" },
{ "a m ago", "-1m" },
{ "+1 minute ago", "-1m" },
{ "-1 minute ago", "-1m" },
{ "-1 minute", "-1m" },
{ "10 minutes after the hour", "0:10" },
{ "1:40", "1:40" },
{ "01:30", "1:30" },
{ "1pm", "13:00" },
{ "12pm", "12:00" },
{ "00:27:18.567", "0:27:18.567" },
{ NULL, NULL }
{ NULL, NULL }
};
static struct {
@ -69,7 +79,7 @@ static struct {
{ NULL, NULL }
};
int main(int argc, char *argv[])
TEST_CASE("reltime")
{
time_t base_time = 1317913200;
struct exttm base_tm;
@ -86,67 +96,67 @@ int main(int argc, char *argv[])
rt.clear();
rc = rt.parse(TEST_DATA[lpc].reltime, pe);
printf("%s %s %s\n", TEST_DATA[lpc].reltime, TEST_DATA[lpc].expected, rt.to_string().c_str());
assert(rc);
assert(std::string(TEST_DATA[lpc].expected) == rt.to_string());
CHECK_MESSAGE(rc, TEST_DATA[lpc].reltime);
CHECK(std::string(TEST_DATA[lpc].expected) == rt.to_string());
}
for (int lpc = 0; BAD_TEST_DATA[lpc].reltime; lpc++) {
bool rc;
rt.clear();
rc = rt.parse(BAD_TEST_DATA[lpc].reltime, pe);
printf("%s -- %s\n", BAD_TEST_DATA[lpc].reltime, pe.pe_msg.c_str());
assert(!rc);
CHECK(!rc);
}
rt.clear();
rt.parse("", pe);
CHECK(rt.empty());
rt.clear();
rt.parse("a minute ago", pe);
assert(rt.rt_field[relative_time::RTF_MINUTES] == -1);
CHECK(rt.rt_field[relative_time::RTF_MINUTES].value == -1);
rt.clear();
rt.parse("5 milliseconds", pe);
assert(rt.rt_field[relative_time::RTF_MICROSECONDS] == 5 * 1000);
CHECK(rt.rt_field[relative_time::RTF_MICROSECONDS].value == 5 * 1000);
rt.clear();
rt.parse("5000 ms ago", pe);
assert(rt.rt_field[relative_time::RTF_SECONDS] == -5);
CHECK(rt.rt_field[relative_time::RTF_SECONDS].value == -5);
rt.clear();
rt.parse("5 hours 20 minutes ago", pe);
assert(rt.rt_field[relative_time::RTF_HOURS] == -5);
assert(rt.rt_field[relative_time::RTF_MINUTES] == -20);
CHECK(rt.rt_field[relative_time::RTF_HOURS].value == -5);
CHECK(rt.rt_field[relative_time::RTF_MINUTES].value == -20);
rt.clear();
rt.parse("5 hours and 20 minutes ago", pe);
assert(rt.rt_field[relative_time::RTF_HOURS] == -5);
assert(rt.rt_field[relative_time::RTF_MINUTES] == -20);
CHECK(rt.rt_field[relative_time::RTF_HOURS].value == -5);
CHECK(rt.rt_field[relative_time::RTF_MINUTES].value == -20);
rt.clear();
rt.parse("1:23", pe);
assert(rt.rt_field[relative_time::RTF_HOURS] == 1);
assert(rt.rt_is_absolute[relative_time::RTF_HOURS]);
assert(rt.rt_field[relative_time::RTF_MINUTES] == 23);
assert(rt.rt_is_absolute[relative_time::RTF_MINUTES]);
CHECK(rt.rt_field[relative_time::RTF_HOURS].value == 1);
CHECK(rt.rt_field[relative_time::RTF_MINUTES].value == 23);
CHECK(rt.rt_is_absolute);
rt.clear();
rt.parse("1:23:45", pe);
assert(rt.rt_field[relative_time::RTF_HOURS] == 1);
assert(rt.rt_is_absolute[relative_time::RTF_HOURS]);
assert(rt.rt_field[relative_time::RTF_MINUTES] == 23);
assert(rt.rt_is_absolute[relative_time::RTF_MINUTES]);
assert(rt.rt_field[relative_time::RTF_SECONDS] == 45);
assert(rt.rt_is_absolute[relative_time::RTF_SECONDS]);
CHECK(rt.rt_field[relative_time::RTF_HOURS].value == 1);
CHECK(rt.rt_field[relative_time::RTF_MINUTES].value == 23);
CHECK(rt.rt_field[relative_time::RTF_SECONDS].value == 45);
CHECK(rt.rt_is_absolute);
tm = base_tm;
rt.add(tm);
new_time = timegm(&tm.et_tm);
tm.et_tm = *gmtime(&new_time);
assert(tm.et_tm.tm_hour == 1);
assert(tm.et_tm.tm_min == 23);
CHECK(tm.et_tm.tm_hour == 1);
CHECK(tm.et_tm.tm_min == 23);
rt.clear();
rt.parse("5 minutes ago", pe);
@ -156,7 +166,7 @@ int main(int argc, char *argv[])
new_time = timegm(&tm.et_tm);
assert(new_time == (base_time - (5 * 60)));
CHECK(new_time == (base_time - (5 * 60)));
rt.clear();
rt.parse("today at 4pm", pe);
@ -176,12 +186,12 @@ int main(int argc, char *argv[])
tm2.et_tm.tm_gmtoff = 0;
tm2.et_tm.tm_zone = NULL;
#endif
assert(tm.et_tm.tm_year == tm2.et_tm.tm_year);
assert(tm.et_tm.tm_mon == tm2.et_tm.tm_mon);
assert(tm.et_tm.tm_mday == tm2.et_tm.tm_mday);
assert(tm.et_tm.tm_hour == tm2.et_tm.tm_hour);
assert(tm.et_tm.tm_min == tm2.et_tm.tm_min);
assert(tm.et_tm.tm_sec == tm2.et_tm.tm_sec);
CHECK(tm.et_tm.tm_year == tm2.et_tm.tm_year);
CHECK(tm.et_tm.tm_mon == tm2.et_tm.tm_mon);
CHECK(tm.et_tm.tm_mday == tm2.et_tm.tm_mday);
CHECK(tm.et_tm.tm_hour == tm2.et_tm.tm_hour);
CHECK(tm.et_tm.tm_min == tm2.et_tm.tm_min);
CHECK(tm.et_tm.tm_sec == tm2.et_tm.tm_sec);
rt.clear();
rt.parse("yesterday at 4pm", pe);
@ -200,10 +210,10 @@ int main(int argc, char *argv[])
tm2.et_tm.tm_gmtoff = 0;
tm2.et_tm.tm_zone = NULL;
#endif
assert(tm.et_tm.tm_year == tm2.et_tm.tm_year);
assert(tm.et_tm.tm_mon == tm2.et_tm.tm_mon);
assert(tm.et_tm.tm_mday == tm2.et_tm.tm_mday);
assert(tm.et_tm.tm_hour == tm2.et_tm.tm_hour);
assert(tm.et_tm.tm_min == tm2.et_tm.tm_min);
assert(tm.et_tm.tm_sec == tm2.et_tm.tm_sec);
CHECK(tm.et_tm.tm_year == tm2.et_tm.tm_year);
CHECK(tm.et_tm.tm_mon == tm2.et_tm.tm_mon);
CHECK(tm.et_tm.tm_mday == tm2.et_tm.tm_mday);
CHECK(tm.et_tm.tm_hour == tm2.et_tm.tm_hour);
CHECK(tm.et_tm.tm_min == tm2.et_tm.tm_min);
CHECK(tm.et_tm.tm_sec == tm2.et_tm.tm_sec);
}