gpt4 book ai didi

c++ - 使用C++将时间戳转换为格式化的日期时间

转载 作者:行者123 更新时间:2023-11-30 04:42:46 28 4
gpt4 key购买 nike

我只能使用C++标准库(C++ 14)将时间戳转换为给定的格式date-time。我是C++的新手,我知道C++通过Java之类的库对我们的支持并不多。
在中欧时区(CET)的给定日期和时间 2011-03-10 11:23:56,以下
将产生标准格式的输出:“ 2011-03-10T11:23:56.123 + 0100 ”。

std::string format = "yyyy-MM-dd'T'HH:mm:ss'.'SSSZ"; //default format
auto duration = std::chrono::system_clock::now().time_since_epoch();
auto timestamp = std::chrono::duration_cast<std::chrono::milliseconds>(duration).count();

我的格式字符串的语法是
G : Era 
yy,yyyy : year (two digits/four digits)
M,MM : month in number (without zero/ with zero) - (e.g.,1/01)
MMM,MMMM : month in text (shortname/fullname)- (e.g.,jan/january)
d,dd : day in month (without zero/with zero)- (e.g.,1/01)
D : day in year
F : day of week of month
E, EEEE : day of week
h,hh : hours(1-12) (without zero/with zero)- (e.g.,1/01)
H,HH : hours(0-23) (without zero/with zero)- (e.g.,1/01)
m,mm : minutes (without zero/with zero)- (e.g.,1/01)
s,ss : seconds (without zero/with zero)- (e.g.,1/01)
S,SS,SSS : milliseconds
w,W : Week in year (without zero/with zero)- (e.g.,1/01)
a : AM/PM
z,zzzz : timezone name

最佳答案

这是一个棘手的问题,因为:

  • 没有明确说明输入是什么。但是从示例代码中,我将假定为std::chrono::system_clock::time_point
  • 重要的是要认识到中欧时区(CET)被定义为time zone with a fixed UTC offset of 1h。有些地理区域全年遵循该时区规则,有些则没有。而且没有人一直跟随它。无论如何,问题的这一部分使我们能够对所涉及的UTC偏移进行硬编码:1h。没有夏令时调整。

  • 在C++ 14中,有两种方法可以在不涉及受版权保护(甚至是开源)的第三方软件的情况下进行操作:
  • 使用C API。
  • 自己动手。

  • 1的问题是它容易出错。它不能直接处理毫秒精度。它不直接处理特定时区,例如CET。 C API仅知道UTC和计算机的本地设置时区。但是这些问题是可以克服的。

    2的问题在于,它涉及从 std::chrono::system_clock::time_point提取年,月和日字段的非直观算法。

    尽管存在2的问题,但这是我更喜欢的解决方案,我将在下面介绍。我还将展示C++ 20如何使它变得更加容易。

    在所有解决方案中,我将通过实现以下形式的函数来形式化输入和输出:
    std::string format_CET(std::chrono::system_clock::time_point tp);

    自己动手(C++ 14)

    有六个离散步骤。它将需要以下 header ,而无需其他 header :
    #include <chrono>
    #include <string>
    #include <iomanip>
    #include <iostream>
    #include <limits>
    #include <sstream>

    A.将输入移位+1小时UTC偏移量。
    // shift time_point to CET
    tp += 1h;

    本地函数using指令可以很方便地将UDL h纳入范围,以及此函数中 <chrono>所需的所有其他内容:
    using namespace std::chrono;

    B.获得 time_point tp的两种变体:一种具有毫秒精度,一种具有日精度:
    // Get time_points with both millisecond and day precision
    auto tp_ms = time_point_cast<milliseconds>(tp);
    auto tp_d = time_point_cast<days>(tp_ms);

    重要的是要了解这两个强制转换将趋近于零,并且对于负的时间点将给出错误的结果。 system_clock在其1970年1月1日00:00:00 UTC的时期之前给出负的时间点。 C++ 17引入了 floor<millliseconds>(tp)来解决此问题。

    日精度 time_point将用于提取年,月和日字段,而毫秒精度 time_point将用于提取小时,分钟,秒和毫秒字段。上面使用的 duration days直到C++ 20才被添加,但是您可以使用以下方法做到:
    using days = std::chrono::duration<int, std::ratio<86400>>;

    C.要从 tp_d获取年,月和日字段,使用 public domain algorithms for calendrical operations之一很方便。这不是第三方图书馆。它是用于编写自己的日历库的算法(这就是我正在解释的内容)。我已经定制了 civil_from_days算法以完全解决 format_CET的需求:
    // Get {y, m, d} from tp_d
    auto z = tp_d.time_since_epoch().count();
    static_assert(std::numeric_limits<unsigned>::digits >= 18,
    "This algorithm has not been ported to a 16 bit unsigned integer");
    static_assert(std::numeric_limits<int>::digits >= 20,
    "This algorithm has not been ported to a 16 bit signed integer");
    z += 719468;
    const int era = (z >= 0 ? z : z - 146096) / 146097;
    const unsigned doe = static_cast<unsigned>(z - era * 146097); // [0, 146096]
    const unsigned yoe = (doe - doe/1460 + doe/36524 - doe/146096) / 365; // [0, 399]
    int y = static_cast<int>(yoe) + era * 400;
    const unsigned doy = doe - (365*yoe + yoe/4 - yoe/100); // [0, 365]
    const unsigned mp = (5*doy + 2)/153; // [0, 11]
    const unsigned d = doy - (153*mp+2)/5 + 1; // [1, 31]
    const unsigned m = mp + (mp < 10 ? 3 : -9); // [1, 12]
    y += (m <= 2);

    对于那些想知道其工作原理的人,上面链接的站点上详尽详尽地推导了该算法。

    此时,整数变量 {y, m, d}包含年,月,日三元组。

    D.获取自当地午夜以来的持续时间。这将用于提取当地时间:
    // Get milliseconds since the local midnight
    auto ms = tp_ms - tp_d;

    E.获取小时,分钟,秒和毫秒字段:
    // Get {h, M, s, ms} from milliseconds since midnight
    auto h = duration_cast<hours>(ms);
    ms -= h;
    auto M = duration_cast<minutes>(ms);
    ms -= M;
    auto s = duration_cast<seconds>(ms);
    ms -= s;

    此时, chrono::duration变量 {h, M, s, ms}保持所需的值。

    F.现在我们准备格式化:
    // Format {y, m, d, h, M, s, ms} as yyyy-MM-dd'T'HH:mm:ss'.'SSS+0100
    std::ostringstream os;
    os.fill('0');
    os << std::setw(4) << y << '-' << std::setw(2) << m << '-' << std::setw(2)
    << d << 'T' << std::setw(2) << h.count() << ':'
    << std::setw(2) << M.count() << ':' << std::setw(2) << s.count()
    << '.' << std::setw(3) << ms.count() << "+0100";
    return os.str();

    使用操纵器 setw的组合来设置每个字段的宽度,并使用 0填充字符,可以得到所需的前导零。

    C++ 20解决方案

    在C++ 20规范中,这要容易得多:
    std::string
    format_CET(std::chrono::system_clock::time_point tp)
    {
    using namespace std::chrono;
    static auto const CET = locate_zone("Etc/GMT-1");
    return std::format("{:%FT%T%z}", zoned_time{CET, floor<milliseconds>(tp)});
    }

    “Etc / GMT-1”是IANA等效于 中欧时区(CET)。此 time_zone const*定位并存储在变量 CET中。 time_point tp被截断为毫秒精度,并使用 time_zonezoned_time配对。然后使用显示的格式字符串将此 zoned_time格式化(以毫秒为单位)。

    存在C++ 20规范的开源(MIT许可)预览版,语法差异非常小 here
    #include "date/tz.h"

    std::string
    format_CET(std::chrono::system_clock::time_point tp)
    {
    using namespace date;
    using namespace std::chrono;
    static auto const CET = locate_zone("Etc/GMT-1");
    return format("%FT%T%z", zoned_time<milliseconds>{CET, floor<milliseconds>(tp)});
    }

    Some installation is required for Windows.

    该预览版适用于C++ 14。在C++ 17和更高版本中, zoned_time<milliseconds>可以简化为 zoned_time

    自定义时区支持

    还有一种使用预览库的方法,因此不需要安装。它成为仅 header 的库。这是通过创建仅模拟CET的自定义时区,然后将其安装在 zoned_time中来完成的。自定义时区如下所示:
    #include "date/tz.h"

    class CET
    {
    public:

    template <class Duration>
    auto
    to_local(date::sys_time<Duration> tp) const
    {
    using namespace date;
    using namespace std::chrono;
    return local_time<Duration>{(tp + 1h).time_since_epoch()};
    }

    template <class Duration>
    auto
    to_sys(date::local_time<Duration> tp) const
    {
    using namespace date;
    using namespace std::chrono;
    return sys_time<Duration>{(tp - 1h).time_since_epoch()};
    }

    template <class Duration>
    date::sys_info
    get_info(date::sys_time<Duration>) const
    {
    using namespace date;
    using namespace std::chrono;
    return {ceil<seconds>(sys_time<milliseconds>::min()),
    floor<seconds>(sys_time<milliseconds>::max()),
    1h, 0min, "CET"};
    }

    const CET* operator->() const {return this;}
    };
    CET现在满足了足够的时区要求,因此可以在 zoned_time中使用并按照以前的格式进行格式化。在C++ 14中,语法很复杂,因为必须显式指定 zoned_time模板参数:
    std::string
    format_CET(std::chrono::system_clock::time_point tp)
    {
    using namespace date;
    using namespace std::chrono;
    using ZT = zoned_time<milliseconds, CET>;
    return format("%FT%T%z", ZT{CET{}, floor<milliseconds>(tp)});
    }

    此选项也在C++ 20规范中,其优点是时区缩写(在您的问题中未使用)将正确报告“CET”而不是“+01”。

    可以在 here上找到有关自定义时区的更多文档。

    使用这些解决方案中的任何一种,现在都可以像下面这样来行使功能:
    #include <iostream>

    int
    main()
    {
    std::cout << format_CET(std::chrono::system_clock::now()) << '\n';
    }

    典型的输出如下所示:
    2019-10-29T16:37:51.217+0100

    关于c++ - 使用C++将时间戳转换为格式化的日期时间,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/58603446/

    28 4 0
    Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
    广告合作:1813099741@qq.com 6ren.com