使用 std::chrono 在 C++ 中输出日期和时间

Posted

技术标签:

【中文标题】使用 std::chrono 在 C++ 中输出日期和时间【英文标题】:Outputting Date and Time in C++ using std::chrono 【发布时间】:2021-12-08 19:20:46 【问题描述】:

我一直在升级一些旧代码,并在可能的情况下尝试更新到 c++11。以下代码是我用来在程序中显示时间和日期的方式

#include <iostream>
#include <string>
#include <stdio.h>
#include <time.h>

const std::string return_current_time_and_date() const

    time_t now = time(0);
    struct tm tstruct;
    char buf[80];
    tstruct = *localtime(&now);
    strftime(buf, sizeof(buf), "%Y-%m-%d %X", &tstruct);
    return buf;

我想使用 std::chrono(或类似的)以类似的格式输出当前时间和日期,但我不确定如何去做。任何帮助将不胜感激。谢谢

【问题讨论】:

如果已经有这样的问题,请查找,如果没有,请查询。 可能重复: ***.com/questions/12346260/c-date-and-time C++11 不会让你变得更好。 chrono 更多的是关于时间(某件事花了多长时间)而不是时间类型的东西。不过,您可以查看 Boost Date Time。它具有更强大的日期和时间功能。 使用计时和输出获取日期和时间?请参阅first example。 @DyP 这看起来很有希望,但我需要从函数中返回一个字符串,并且不知道如何使用 std::put_time 进行此操作 【参考方案1】:

&lt;chrono&gt; 库只处理时间而不是日期,但 system_clock 除外,它能够将其时间点转换为 time_t。所以使用&lt;chrono&gt; 日期不会有太大改善。希望我们在不久的将来能得到类似chrono::date 的东西。

也就是说,您可以通过以下方式使用&lt;chrono&gt;

#include <chrono>  // chrono::system_clock
#include <ctime>   // localtime
#include <sstream> // stringstream
#include <iomanip> // put_time
#include <string>  // string

std::string return_current_time_and_date()

    auto now = std::chrono::system_clock::now();
    auto in_time_t = std::chrono::system_clock::to_time_t(now);

    std::stringstream ss;
    ss << std::put_time(std::localtime(&in_time_t), "%Y-%m-%d %X");
    return ss.str();

请注意,std::localtime 可能会导致数据争用。 localtime_r 或类似功能可能在您的平台上可用。

更新:

使用新版 Howard Hinnant 的 date library 你可以这样写:

#include "date.h"
#include <chrono>
#include <string>
#include <sstream>

std::string return_current_time_and_date() 
  auto now = std::chrono::system_clock::now();
  auto today = date::floor<days>(now);

  std::stringstream ss;
  ss << today << ' ' << date::make_time(now - today) << " UTC";
  return ss.str();

这将打印出类似“2015-07-24 05:15:34.043473124 UTC”的内容。


在一个不相关的注释中,返回 const 对象在 C++11 中变得不可取; const 返回值不能被移出。我还删除了尾随 const,因为尾随 const 仅对成员函数有效,并且此函数不需要成为成员。

【讨论】:

注意一些旧的编译器可能无法识别std::put_time。一旦我尝试使用 g++ 在 CentOS 7 上编译具有 std::put_time 的代码,它就失败了。 GCC 中std::put_time 支持的状态***.com/q/14136833/4694036 有没有类似std::put_time但返回字符串的C++函数? @Nick No. C 函数strftime 可以让你用 C 字符串填充缓冲区。已废弃的函数asctime 将返回一个固定时间格式的 C 字符串。 谢谢,我猜你可以用字符串流做 put_time 如果您不使用using namespace date;&lt;&lt; today 将不起作用。【参考方案2】:

一个例子:

#include <iostream>
#include <chrono>
#include <ctime>

std::string getTimeStr()
    std::time_t now = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());

    std::string s(30, '\0');
    std::strftime(&s[0], s.size(), "%Y-%m-%d %H:%M:%S", std::localtime(&now));
    return s;

int main()

    std::cout<<getTimeStr()<<std::endl;
    return 0;


输出如下:

【讨论】:

不是线程安全的请参阅Data races 投反对票:当前答案返回一个 30 字符长的字符串,末尾有很多空字符。如果您尝试将某些内容连接到它,它将不起作用。我确实建议进行编辑以使其正常工作,但被 Django 程序员拒绝....【参考方案3】:

这是一个 C++20 解决方案:

#include <chrono>
#include <format>

std::string get_current_time_and_date()

    auto const time = std::chrono::current_zone()
        ->to_local(std::chrono::system_clock::now());
    return std::format(":%Y-%m-%d %X", time);

std::chrono::time_zone::to_local 将系统时钟时间点 (std::chrono::time_point&lt;std::chrono::system_clock, TDuration&gt;) 转换为本地时间点 (std::chrono::local_time&lt;TDuration&gt;)。然后可以使用std::format 格式化这个本地时间点,格式化选项类似于strftime

目前,只有 MSVC 实现了std::format。 chrono 中添加的日历和时区 目前由 Clang 和 GCC “部分”实现,但请在此处查看更新状态:https://en.cppreference.com/w/cpp/compiler_support。有关chrono 库的更多信息,请在此处阅读:https://en.cppreference.com/w/cpp/chrono。

【讨论】:

clang 或 gcc 是否实现了 &lt;chrono&gt; 库的时区部分?即使我们从示例中删除std::format,上面的代码是否可以在 Godbolt 或 Wandbox 中进行测试? 最简单的找出方法是……在 Godbolt 或 Wandbox 中测试它。 实际上,我想我可以将该信息添加到答案中以使其更完整。谢谢【参考方案4】:

为了获得毫秒,我使用了 chrono 和 C 函数 localtime_r,它是线程安全的(与 std::localtime 相反)。

#include <iostream>
#include <chrono>
#include <ctime>
#include <time.h>
#include <iomanip>


int main() 
  std::chrono::system_clock::time_point now = std::chrono::system_clock::now();
  std::time_t currentTime = std::chrono::system_clock::to_time_t(now);
  std::chrono::milliseconds now2 = std::chrono::duration_cast<std::chrono::milliseconds>(now.time_since_epoch());
  struct tm currentLocalTime;
  localtime_r(&currentTime, &currentLocalTime);
  char timeBuffer[80];
  std::size_t charCount  std::strftime( timeBuffer, 80,
                                         "%D %T",
                                          &currentLocalTime)
                         ;

  if (charCount == 0) return -1;

  std::cout << timeBuffer << "." << std::setfill('0') << std::setw(3) << now2.count() % 1000 << std::endl;
  return 0;

对于格式:http://www.cplusplus.com/reference/ctime/strftime/

【讨论】:

由于您已经在导入 iomanip,您可以跳过使用 std::put_time 而不是 std::strftime 创建自己的缓冲区。 您可能会出现半秒,因为to_time_t 可能会截断,但也可能会舍入。 您不必应用时钟类型模板参数吗? std::chrono::system_clock::time_point&lt;system_clock&gt; now = std::chrono::system_clock::now();【参考方案5】:

bames53 解决方案不错,但是在我的 VS2017 上编译不了。使用 ctime 的解决方案无法编译,因为 localtime 非常不推荐使用。带有 date.h 的那个不与当前 date.h 一起编译,即使文档说应该这样做,我也刚刚离开了 github,因为今天不能按原样流式传输。我省略了包含,但这里是有效的代码:

void TimeTest()

    auto n = std::chrono::system_clock::now();
    auto in_time_t = std::chrono::system_clock::to_time_t(n);
    std::tm buf;
    localtime_s(&buf, &in_time_t);
    std::cout << std::put_time(&buf, "%Y-%m-%d %X") << std::endl;



// I just added date.h from this link's guthub to the project.
// https://howardhinnant.github.io/date/date.html
void TimeTest1() 
    auto now = std::chrono::system_clock::now();
    auto today =  floor<date::days>(std::chrono::system_clock::now());
    std::cout << date::year_month_day today  << ' ' << date::make_time(now - today) << std::endl;


// output is 
// 2018-04-08 21:19:49
// 2018-04-08 18:19:49.8408289

随时修复 bames53 解决方案并删除我的解决方案。我的文字不适合评论。我相信它可以使许多人免于悲痛。

【讨论】:

如果您添加using date::operator&lt;&lt;;,您可以流式传输today,而无需将其转换为year_month_day 霍华德,非常感谢您编写和分享这个库。就这一点而言,我宁愿永远不要使用 using,因为在我的日子里,我已经饱受命名空间冲突的错误和歧义之苦。另外,如果我们要准确的话,我想指出第一个示例输出本地时间,而第二个示例输出通用时间。 很高兴能帮上忙。 usings 在函数范围内是一个很好的折衷方案,因为冲突发生时更容易控制。我意识到这并不完美,但对于 std::types 的流插入运算符来说,这是最好的,由 3rd 方库编写。但就在上个月,我说服 C++ 委员会将此流操作符放在 C++20 的 namespace std::chrono 中,以便 ADL 找到它。然后它将不再需要using 关于输出为 UTC 的良好观察。还提供了一个更高级别的库,它提供本地时间的操作。这可以是您计算机当前的本地时区,也可以是专门指定的时区(例如“America/New_York”)。这是documentation for the time zone library。 它在 C++20 中:eel.is/c++draft/time.clock.system.nonmembers#4 供应商正在实施它。【参考方案6】:

fmt 库能够格式化tm 结构:它与 strftime 具有相同的规范。

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

std::string current_datetime(void)

  std::time_t tt = std::time(nullptr);
  std::tm *tm = std::localtime(&tt);
  return fmt::format(":%Y%m%d", *tm);

【讨论】:

【参考方案7】:

您可以通过使用 Boost lexical_cast 而不是字符串流操作来改进来自 @bames53 的答案。

这是我的工作:

#include <boost/lexical_cast.hpp>
#include <ctime>

std::string return_current_time_and_date() 
    auto current_time = std::time(0);
    return boost::lexical_cast<std::string>(std::put_time(std::gmtime(& current_time), "%Y-%m-%d %X"));

【讨论】:

还需要iomanip en.cppreference.com/w/cpp/io/manip/put_time【参考方案8】:

虽然已经给出了正确的答案,但我决定再实施一种解决方案,该解决方案也输出秒的小数部分。

您可能会注意到,在我的代码中,有时我会从 time_t 值中减去一秒,- std::chrono::seconds(1),这是因为根据 documentation to_time_t() 可能会舍入值而不是截断(根据 doc “如果 std: :time_t 具有较低的精度,它是实现定义的值是舍入还是截断”),因此我必须减去 1 秒才能使其截断时间。

Try it online!

#include <chrono>
#include <string>
#include <sstream>
#include <iomanip>

std::string FormatTime(std::chrono::system_clock::time_point tp) 
    std::stringstream ss;
    auto t = std::chrono::system_clock::to_time_t(tp);
    auto tp2 = std::chrono::system_clock::from_time_t(t);
    if (tp2 > tp)
        t = std::chrono::system_clock::to_time_t(tp - std::chrono::seconds(1));
    ss  << std::put_time(std::localtime(&t), "%Y-%m-%d %T")
        << "." << std::setfill('0') << std::setw(3)
        << (std::chrono::duration_cast<std::chrono::milliseconds>(
           tp.time_since_epoch()).count() % 1000);
    return ss.str();


std::string CurrentTimeStr() 
    return FormatTime(std::chrono::system_clock::now());


#include <iostream>

int main() 
    std::cout << CurrentTimeStr() << std::endl;

示例输出:

2021-12-02 04:10:51.876

【讨论】:

以上是关于使用 std::chrono 在 C++ 中输出日期和时间的主要内容,如果未能解决你的问题,请参考以下文章

从 C++ 中的 std::chrono::time_point 中提取年/月/日等

如何从 C++ 中的 std::chrono::year_month_day 获取工作日编号

C++ 中的时序逻辑错误(使用 std::chrono)

std c++ 获取运行时间封装

C++ std::chrono::seconds()函数(持续时间(秒))

C++ 11 时间编程 std::chrono::steady_clock使用--计算程序执行时间