【问题标题】:Outputting Date and Time in C++ using std::chrono使用 std::chrono 在 C++ 中输出日期和时间
【发布时间】: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(或类似的)以类似的格式输出当前时间和日期,但我不确定如何去做。任何帮助将不胜感激。谢谢

【问题讨论】:

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

标签: c++ date c++11 time chrono


【解决方案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 支持的状态stackoverflow.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"));
    }
    

    【讨论】:

    【解决方案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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-03-20
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-07-25
      • 1970-01-01
      相关资源
      最近更新 更多