• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

tstack / lnav / 17589970077-2502

09 Sep 2025 05:00PM UTC coverage: 65.196% (-5.0%) from 70.225%
17589970077-2502

push

github

tstack
[format] add fields for source file/line

Knowing the source file/line context in a log
message can help find log messages when using
log2src.

56 of 70 new or added lines in 2 files covered. (80.0%)

13954 existing lines in 210 files now uncovered.

45516 of 69814 relevant lines covered (65.2%)

404154.37 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

97.8
/src/base/humanize.time.cc
1
/**
2
 * Copyright (c) 2021, Timothy Stack
3
 *
4
 * All rights reserved.
5
 *
6
 * Redistribution and use in source and binary forms, with or without
7
 * modification, are permitted provided that the following conditions are met:
8
 *
9
 * * Redistributions of source code must retain the above copyright notice, this
10
 * list of conditions and the following disclaimer.
11
 * * Redistributions in binary form must reproduce the above copyright notice,
12
 * this list of conditions and the following disclaimer in the documentation
13
 * and/or other materials provided with the distribution.
14
 * * Neither the name of Timothy Stack nor the names of its contributors
15
 * may be used to endorse or promote products derived from this software
16
 * without specific prior written permission.
17
 *
18
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ''AS IS'' AND ANY
19
 * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
20
 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
21
 * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY
22
 * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
23
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
24
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
25
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
26
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
27
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28
 */
29

30
#include <chrono>
31

32
#include "humanize.time.hh"
33

34
#include "config.h"
35
#include "fmt/format.h"
36
#include "math_util.hh"
37
#include "time_util.hh"
38

39
namespace humanize {
40
namespace time {
41

42
using namespace std::chrono_literals;
43

44
point
45
point::from_tv(const timeval& tv)
18✔
46
{
47
    return point(tv);
18✔
48
}
49

50
std::string
51
point::as_time_ago() const
24✔
52
{
53
    struct timeval current_time
54
        = this->p_recent_point.value_or(current_timeval());
24✔
55

56
    if (this->p_convert_to_local) {
24✔
57
        current_time.tv_sec = convert_log_time_to_local(current_time.tv_sec);
6✔
58
    }
59

60
    auto curr_secs = std::chrono::seconds(current_time.tv_sec);
24✔
61
    auto past_secs = std::chrono::seconds(this->p_past_point.tv_sec);
24✔
62
    auto delta = curr_secs - past_secs;
24✔
63
    if (delta < 0s) {
24✔
64
        return "in the future";
12✔
65
    }
66
    if (delta < 1min) {
18✔
67
        return "just now";
6✔
68
    }
69
    if (delta < 2min) {
15✔
70
        return "one minute ago";
2✔
71
    }
72
    if (delta < 1h) {
14✔
73
        return fmt::format(
74
            FMT_STRING("{} minutes ago"),
2✔
75
            std::chrono::duration_cast<std::chrono::minutes>(delta).count());
2✔
76
    }
77
    if (delta < 2h) {
13✔
78
        return "one hour ago";
4✔
79
    }
80
    if (delta < 24h) {
11✔
81
        return fmt::format(
82
            FMT_STRING("{} hours ago"),
4✔
83
            std::chrono::duration_cast<std::chrono::hours>(delta).count());
4✔
84
    }
85
    if (delta < 48h) {
9✔
86
        return "one day ago";
4✔
87
    }
88
    if (delta < 365 * 24h) {
7✔
89
        return fmt::format(FMT_STRING("{} days ago"), delta / 24h);
12✔
90
    }
91
    if (delta < (2 * 365 * 24h)) {
4✔
92
        return "over a year ago";
4✔
93
    }
94
    return fmt::format(FMT_STRING("over {} years ago"), delta / (365 * 24h));
8✔
95
}
96

97
std::string
98
point::as_precise_time_ago() const
15✔
99
{
100
    struct timeval now, diff;
101

102
    now = this->p_recent_point.value_or(current_timeval());
15✔
103
    if (this->p_convert_to_local) {
15✔
104
        now.tv_sec = convert_log_time_to_local(now.tv_sec);
6✔
105
    }
106

107
    timersub(&now, &this->p_past_point, &diff);
15✔
108
    if (diff.tv_sec < 0) {
15✔
109
        return this->as_time_ago();
6✔
110
    } else if (diff.tv_sec <= 1) {
9✔
UNCOV
111
        return "a second ago";
×
112
    } else if (diff.tv_sec < (10 * 60)) {
9✔
113
        if (diff.tv_sec < 60) {
3✔
114
            return fmt::format(FMT_STRING("{:2} seconds ago"), diff.tv_sec);
4✔
115
        }
116

117
        lnav::time64_t seconds = diff.tv_sec % 60;
2✔
118
        lnav::time64_t minutes = diff.tv_sec / 60;
2✔
119

120
        return fmt::format(FMT_STRING("{:2} minute{} and {:2} second{} ago"),
4✔
121
                           minutes,
122
                           minutes > 1 ? "s" : "",
2✔
123
                           seconds,
124
                           seconds == 1 ? "" : "s");
4✔
125
    } else {
126
        return this->as_time_ago();
6✔
127
    }
128
}
129

130
duration
131
duration::from_tv(const struct timeval& tv)
545✔
132
{
133
    return duration{tv};
545✔
134
}
135

136
std::string
137
duration::to_string() const
545✔
138
{
139
    /* 24h22m33s111 */
140

141
    static const struct rel_interval {
142
        uint64_t length;
143
        const char* format;
144
        const char* symbol;
145
    } intervals[] = {
146
        {1000, "%03lld%s", ""},
147
        {60, "%lld%s", "s"},
148
        {60, "%lld%s", "m"},
149
        {24, "%lld%s", "h"},
150
        {0, "%lld%s", "d"},
151
    };
152

153
    const auto* curr_interval = intervals;
545✔
154
    auto usecs = std::chrono::duration_cast<std::chrono::microseconds>(
545✔
155
                     std::chrono::seconds(this->d_timeval.tv_sec))
545✔
156
        + std::chrono::microseconds(this->d_timeval.tv_usec);
1,090✔
157
    auto millis = std::chrono::duration_cast<std::chrono::milliseconds>(usecs);
545✔
158
    std::string retval;
545✔
159
    bool neg = false;
545✔
160

161
    if (millis < 0s) {
545✔
162
        neg = true;
2✔
163
        millis = -millis;
2✔
164
    }
165

166
    uint64_t remaining = millis.count();
545✔
167
    uint64_t scale = 1;
545✔
168
    if (this->d_msecs_resolution > 0) {
545✔
169
        remaining = roundup(remaining, this->d_msecs_resolution);
545✔
170
    }
171
    if (millis >= 10min) {
545✔
172
        remaining /= curr_interval->length;
211✔
173
        scale *= curr_interval->length;
211✔
174
        curr_interval += 1;
211✔
175
    }
176

177
    for (; curr_interval != std::end(intervals); curr_interval++) {
2,944✔
178
        uint64_t amount;
179
        char segment[32];
180
        auto skip = scale < this->d_msecs_resolution;
1,470✔
181

182
        if (curr_interval->length) {
1,470✔
183
            amount = remaining % curr_interval->length;
1,264✔
184
            remaining = remaining / curr_interval->length;
1,264✔
185
        } else {
186
            amount = remaining;
206✔
187
            remaining = 0;
206✔
188
        }
189
        scale *= curr_interval->length;
1,470✔
190

191
        if (amount == 0 && remaining == 0) {
1,470✔
192
            break;
543✔
193
        }
194

195
        if (skip) {
927✔
196
            continue;
24✔
197
        }
198

199
        snprintf(segment,
903✔
200
                 sizeof(segment),
201
                 curr_interval->format,
903✔
202
                 amount,
203
                 curr_interval->symbol);
903✔
204
        retval.insert(0, segment);
903✔
205
        if (remaining > 0 && amount < 10 && curr_interval->symbol[0]) {
903✔
206
            retval.insert(0, "0");
38✔
207
        }
208
    }
209

210
    if (neg) {
545✔
211
        retval.insert(0, "-");
2✔
212
    }
213

214
    return retval;
1,090✔
UNCOV
215
}
×
216

217
}  // namespace time
218
}  // namespace humanize
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc