【问题标题】:Converting ddhhmm to YYYY-MM-DD hh:mm format in C++在 C++ 中将 ddhhmm 转换为 YYYY-MM-DD hh:mm 格式
【发布时间】:2020-06-22 08:12:17
【问题描述】:

我有一个字符串,它的 UTC 时间为 ddhhmm,我需要将其转换为 YYYY-MM-dd hh:mm:ss 格式。

Example Input: 220805
Output: 2020-06-22 08:05:00

据我所知,<chrono> 库只能处理时间而不是日期。 进行转换时,我不知道如何在本地时间和 UTC 之间进行转换。是否有任何 C++ 特定的库可以做到这一点?

提前致谢。

【问题讨论】:

  • 我假设您想在当前 UTC 年份和月份前加上前缀? “-dd hh:mm:00”部分只是字符串操作,不是真正的日期操作。
  • 实际上时间是在未来(未来最多 30 分钟)。我得到 220805 的时间是将来。我需要按时间处理。
  • std::time_get::get_date 将允许您生成和解析日期组件。 (参见页面上的示例)

标签: c++ c++11


【解决方案1】:

std::gmtime 获取当前 UTC 时间; std::put_time(time, "%Y-%m-") 格式化它。

【讨论】:

  • std::time_get::get_date 路由好多了。
  • 如果时间在未来,我该如何处理。时间最长为未来 30 分钟。例如,如果当前 UTC 是 220807 (ddhhmm),我得到的输入将是 220830。在这种情况下,我该如何处理月份和年份。
  • @NJMR: std::gmtime 接受std::time_t。将当前时间加上 30 分钟,表示为 time_tnot entirely trivial 但这是可行的。
【解决方案2】:

来自cmets:

实际上时间是未来(未来最多 30 分钟)。

未指定如果输入不在[now, now+30min] 范围内该怎么办。但是,假设输入将在此范围内,则可以消除所有歧义。

C 时间 API 使用起来有些笨拙,因为它在串行 (time_t) 和字段 (tm) 表示之间的转换中插入本地时区(并非总是如此,但通常足以容易出错)。并且问题陈述要么说要么暗示输入和输出都是 UTC(表示输入,隐含输出)。

考虑到这一切,C++20 <chrono> 可以巧妙地解决这个问题。尽管 C++20 <chrono> 尚未发布(据我所知),但此 C++ <chrono> preview library 可以与 C++11/14/17 一起使用来解决问题。

#include "date/date.h"

#include <iostream>
#include <stdexcept>
#include <sstream>
#include <string>

// Convert ddhhmm UTC to YYYY-MM-dd hh:mm:ss UTC
// Precondition: ddhhmm is in the range [now, now+30min]
// Throws std::out_of_range if the precondition is not met
std::string
convert(std::string in)
{
    using namespace date;
    using namespace std;
    using namespace std::chrono;

    auto now = floor<minutes>(system_clock::now());
    auto today = year_month_day{floor<days>(now)};
    auto ym = today.year()/today.month();
    day d;
    hours h;
    minutes m;
    istringstream io{in};
    io.exceptions(ios::failbit);
    io >> parse("%d", d) >> parse("%H", h) >> parse("%M", m);
    auto tp = sys_days{ym/d} + h + m;
    if (tp < now)
    {
        ym += months{1};
        tp = sys_days{ym/d} + h + m;
    }
    if (tp > now + 30min)
    {
        auto min_str = format("%d%H%M", now);
        auto max_str = format("%d%H%M", now + 30min);
        throw std::out_of_range(in + " is out of range: ["
                                + min_str + ", " + max_str + "].");
    }
    return format("%F %T", tp);
}
  • 第一步是使用system_clock::now() 建立当前的UTC 日期/时间。由于我们只想以分钟精度进行流量,因此当前时间会立即截断为分钟。

  • 接下来,年、月、日字段 (UTC) 将从当前时间中提取并存储在 today 中。

  • 现在的问题是:需要将什么monthyear 应用于输入day 才能获得所需的time_point?为了回答这个问题,“试用”yearmonth 从当前日期取出并存储在 ym 中。

  • 接下来从输入流中提取输入dayhoursminutes 字段。如果输入流没有预期的输入,则抛出异常。

  • 试用time_point 是根据ym 计算得出的,输入日期、输入小时数和输入分钟数。此试用时间小于当前时间,或者是所需的time_point。如果试用版 time_point 已过去,则向其中添加 1 个 month 应该会将其变为所需的 time_point1,2

  • 如果调整后,time_point 不在所需范围内,则会引发异常,并带有详细说明问题所在的信息字符串。

  • 否则time_point 会以所需的方式格式化。

可以这样练习:

std::cout << convert("220805") << '\n';

这只是为我输出:

terminating with uncaught exception of type std::out_of_range: 220805 is out of range: [221443, 221513].

还有这个:

std::cout << convert("221510") << '\n';

只为我输出:

2020-06-22 15:10:00

要将此程序移植到 C++20,请删除 #include "date/date.h"using namespace date;

"date/date.h" 是一个只有标头的开源库。


1ddhhmm 输入可以被视为一个“时间点”,其纪元为当月第一天的午夜 UTC。并且它在当前月份的最后一刻之后的最大值为 30 分钟(由于前提条件)。假设在 [yy-mm-01 00:00:00, yy-mm-last 24:30:00] 范围内随机均匀分布ddhhmm,大部分时间 yy-mm 是当前年份和月份。但偶尔(当ddhhmm 超过 yy-mm-last 24:00:00 时)yy-mm 将是下个月。

2 变量ym 的类型为year_month。当起始monthDecember 时,添加months{1} 将增加year 字段。

【讨论】:

  • 哇,我刚刚浏览了我们的视频...谢谢先生...
  • 您好先生,我不明白您的观点“然后将其添加 1 个月应该会使其达到所需的时间点。”。能否请您详细说明这一点或提供一些参考。谢谢。
  • 我在那个要点上添加了几个脚注,希望能澄清一下。
【解决方案3】:

做你想做的事的例子:

#include <iostream>
#include <iomanip>
#include <boost/date_time/gregorian/gregorian.hpp>
int main()
{
    const boost::gregorian::date today = boost::gregorian::day_clock::local_day();
    const std::string input = "220805";
    std::ostringstream oss;
    oss << today.year() << '-'
        << std::setw(2) << std::setfill('0') << today.month().as_number() << '-'
        << input.substr(0,2) << ' '
        << input.substr(2,2) << ':'
        << input.substr(4,2) << ":00";
    const std::string output = oss.str();
    std::cout << "input = " << input << std::endl;
    std::cout << "output = " << output << std::endl;
    return 0;
}

【讨论】:

  • 如果时间在未来,我该如何处理。时间最长为未来 30 分钟。例如,如果当前 UTC 是 220807 (ddhhmm),我得到的输入将是 220830。在这种情况下,我该如何处理月份和年份。
  • 如果 hhmm 大于 0030,则月份和年份与当前时间相同。如果 hhmm 低于 0030,您可以使用 boost 中的 date_time 对象检查当前时间,以检查当前时间是在 00:00 之前还是之后。
【解决方案4】:

如果你愿意,你仍然可以使用库来读取时钟,但你仍然必须使用C风格的日期和时间库来打印(至少使用

这个例子还展示了如何转换为本地时间:

auto timenow = std::chrono::system_clock::now();
std::time_t time_t_now = std::chrono::system_clock::to_time_t(timenow);

std::cout << "The time is "
          << std::put_time(std::localtime(&t_c), "%F %T") << '\n';

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2018-03-25
    • 2017-04-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2017-04-11
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多