3

This code

#include <chrono>
#include <fmt/format.h>
#include <fmt/chrono.h>
...
auto now = std::chrono::system_clock::now();
fmt::print("The time is: {:%Y-%m-%d %H:%M:%S}\n", now);

Prints this:

The time is: 2023-01-02 15:51:23

How do I get it to print sub-second precision, for example, milliseconds? Something like:

The time is: 2023-01-02 15:51:23.753

Rob N
  • 15,024
  • 17
  • 92
  • 165

2 Answers2

3

I think you have to do this in two steps -- YMD and HM followed by the high-res seconds. Here is a worked example, using fmt version 9.0 on Ubuntu:

#include <chrono>
#include <fmt/format.h>
#include <fmt/chrono.h>

int main() {
    auto now = std::chrono::system_clock::now();
    auto sse = now.time_since_epoch();
    fmt::print("{:%FT%H:%M:}{:%S}\n", now, sse);
    exit(0);
}

for which I get

$ g++ -o answer answer.cpp -lfmt; ./answer 
2023-01-02T16:26:17.009359309
$ 

(Initial, earlier, slightly off attempt to reason based on spdlog follows. But spdlog wraps fmt and overlays its format.)

It is on the format page, you want something like %Y-%m-%d %H:%M:%S.%e. Note that %e, %f and %F give you milli, micro and nanoseconds. Edit: That was the spdlog format though.

Here is a quick example, for simplicity from my R package RcppSpdlog accessing fmt via spdlog:

> library(RcppSpdlog)
> RcppSpdlog::log_set_pattern("[%Y-%m-%d %H:%M:%S.%f] [%L] %v")
> RcppSpdlog::log_warn("Hello")
[2023-01-02 15:00:49.746031] [W] Hello
> RcppSpdlog::log_set_pattern("[%Y-%m-%d %H:%M:%S.%e] [%L] %v")
> RcppSpdlog::log_warn("World")
[2023-01-02 15:01:02.137] [W] World
> 

Note the display of micro- and milliseconds as stated.

Edit: There is something else going on with your example. Here is a simplified answer, printing only seconds and it comes by default fractionally down to nanoseconds (for me on Linux):

$ cat answer.cpp
#include <chrono>
#include <fmt/format.h>
#include <fmt/chrono.h>

int main() {
    fmt::print("{:%S}\n", std::chrono::system_clock::now().time_since_epoch());
    exit(0);
}
$ g++ -o answer answer.cpp -lfmt
$ ./answer 
02.461241690
$
Dirk Eddelbuettel
  • 360,940
  • 56
  • 644
  • 725
  • 1
    Hmm. My question is about the `fmt` library, not `spdlog`. And adding a `%f` doesn't compile for me. `%e` doesn't work - it keeps printing "2". I'm guessing it's the day of the week. – Rob N Jan 02 '23 at 21:10
  • I'm looking into how to make vcpkg update. I think it's using 9.0.0 of fmt, but I see a 9.1.0 on their website. – Rob N Jan 02 '23 at 21:14
  • I am using 9.1 via spdlog. I would be surprised if not at least `%e` worked with 9.0 and before. – Dirk Eddelbuettel Jan 02 '23 at 21:19
  • Hm, looking at the release note for 9.1 it does seem that `chrono` formatting got enhanced. Well you can try to upgrade to 9.1, or try another formatter / chrono wrapper like Howard Hinnant's `date`. – Dirk Eddelbuettel Jan 02 '23 at 21:28
  • I can replicate the 'always print 2' under `%2` using the 9.0 system library under Ubuntu. – Dirk Eddelbuettel Jan 02 '23 at 21:29
  • You're latest update there, for 9.0, seems to be working for me on macOS 13.1. – Rob N Jan 03 '23 at 01:31
  • Yeah, sorry about the iterations: "today I learned" that `spdlog` has its own format strings. Makes sense, come to think about it, as `chrono` object have similar formating in `CCTZ`, `Date`, and other libraries wrapped around it. – Dirk Eddelbuettel Jan 03 '23 at 01:33
  • If you could remove the `spdlog` part, or put it as an aside at the end, I'll accept this answer. I just upgraded to `fmt` 9.1 and still none of the %f, %e, %F works. Your second suggestion to use the `time_since_epoch` works. – Rob N Jan 04 '23 at 16:21
  • Done. I think the 'two step approach' is also needed with other time libraries wrapping `chrono`. – Dirk Eddelbuettel Jan 04 '23 at 16:49
  • This chrono-wrapping library only needs one step: https://github.com/HowardHinnant/date: `cout << format("%Y-%m-%d %T\n", now);` – Howard Hinnant Jan 04 '23 at 20:05
  • 1
    Nice, I am fairly sure I needed two steps with CCTZ as well, but spdlog also (like your Date) gets by with one higher-level. (I pointed OP at Date earlier here too...) – Dirk Eddelbuettel Jan 04 '23 at 20:15
1

Just share my way to output fractional seconds in logging:

std::string Timestamp() {
    namespace c = std::chrono;
    auto tp = c::time_point_cast<c::microseconds>(c::system_clock::now());
    auto us = tp.time_since_epoch().count() % std::micro::den;
    return fmt::format("{:%Y-%m-%d_%H:%M:%S}.{:06d}", tp, us);
}

There is a pull request to output fractional seconds for formatting time_point, (but closed, the author did not continue the work):

https://github.com/fmtlib/fmt/pull/2292

My concern is that change %S to include fractional part is some-what breaking change (strftime defines it as seconds from 00 to 60). I hope fmtlib would consider some other way to tackle this requirement.

zerox
  • 307
  • 3
  • 11