有效地生成UTC时间戳

时间:2017-05-14 10:57:14

标签: c++

我需要经常以字符串格式生成UTC时间戳(每秒几次),而我所拥有的代码效率很低。 有没有比我使用的更快的方式?可以假设程序不会跨越日界运行。

void GenerateUTCTimestamp(std::string& out)
{
    auto now = std::chrono::system_clock::now();
    time_t tnow = std::chrono::system_clock::to_time_t(now);
    tm* utc = gmtime(&tnow);

    std::stringstream ss;

    ss << std::setfill('0');
    ss << std::setw(4) << utc->tm_year + 1900;      // Year
    ss << std::setw(2) << utc->tm_mon + 1;          // Month
    ss << std::setw(2) << utc->tm_mday;             // Day
    ss << '-';
    ss << std::setw(2) << utc->tm_hour << ':';      // Hours
    ss << std::setw(2) << utc->tm_min << ':';       // Minutes
    ss << std::setw(2) << utc->tm_sec;              // Seconds

    out = ss.str();
}

1 个答案:

答案 0 :(得分:2)

您将无法找到比此更快的代码:

#include <chrono>
#include <string>

void
stamp(char* s, int i)
{
    do
    {
        *s-- = char(i % 10) + '0';
        i /= 10;
    } while (i > 0);
}

void GenerateUTCTimestamp(std::string& out)
{
    using namespace std;
    using namespace std::chrono;
    using days = duration<int, ratio<86400>>;
    auto now = time_point_cast<seconds>(system_clock::now());
    auto today = time_point_cast<days>(now);
    auto s = now - today;

    // y-m-d
    auto z = today.time_since_epoch().count() + 719468;
    const auto era = 5;
    const auto doe = z - era * 146097;
    const auto yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365;
    const auto y = yoe + era * 400;
    const auto doy = doe - (365*yoe + yoe/4 - yoe/100);
    auto m = (5*doy + 2)/153;
    const auto d = doy - (153*m+2)/5 + 1;
    m = m + (m < 10 ? 3 : -9);

    // h:M:s
    const auto h = duration_cast<hours>(s);
    s -= h;
    const auto M = duration_cast<minutes>(s);
    s -= M;

    // format yyyymmdd-hh:MM:ss
    out = "00000000-00:00:00";
    stamp(&out[3], y);
    stamp(&out[5], m);
    stamp(&out[7], d);
    stamp(&out[10], h.count());
    stamp(&out[13], M.count());
    stamp(&out[16], s.count());
}
  • 此代码使用此处的公共域算法civil_from_days

    http://howardhinnant.github.io/date_algorithms.html#civil_from_days

    您可以在其中找到该算法的深入解释。

  • 代码中的分支数量最小化,代码大小本身也最小化。

  • 完全避免使用通用(和方便)流,而是选择不涉及本地化,特征,宽字符,自定义宽度或对齐的简单整数到字符算法甚至是负值。

  • 除第一次调用外,重复使用并直接格式化为out可完全避免内存分配。

  • 此代码 的有效范围有限:2000-03-01至2400-02-29。如果您需要使此代码对此范围之外的时间点有效,请将era的计算更改为:

    const auto era = (z >= 0 ? z : z - 146096) / 146097;

我把这段代码放在1000个调用的循环中(使用相同的string),定时,并平均所有调用的时间。

在我的机器上(macOS,clang,libc ++, - O3),原始代码大约需要3.9μs,优化代码需要大约150ns(大约快25倍)。

然后,对于笑脸,我使用Howard Hinnant's date library实现了GenerateUTCTimestamp,以了解它在时间测试中的表现。它显然赢得了易用性测试(imho):

#include "date.h"

void GenerateUTCTimestamp(std::string& out)
{
    using namespace date;
    using namespace std::chrono;
    out = format("%Y%m%d-%T", time_point_cast<seconds>(system_clock::now()));
}

它的时钟频率为2.5μs,比线程不安全的C API快50%,但速度比优化的代码慢得多。通用工具的灵活性会降低性能。

日期库使用与优化工具相同的日历算法(使用广义era除外),但格式化为原始代码的stringstream。它当然也必须解析格式化字符串。