来自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 中。
-
现在的问题是:需要将什么month 和year 应用于输入day 才能获得所需的time_point?为了回答这个问题,“试用”year 和 month 从当前日期取出并存储在 ym 中。
-
接下来从输入流中提取输入day、hours 和minutes 字段。如果输入流没有预期的输入,则抛出异常。
-
试用time_point 是根据ym 计算得出的,输入日期、输入小时数和输入分钟数。此试用时间小于当前时间,或者是所需的time_point。如果试用版 time_point 已过去,则向其中添加 1 个 month 应该会将其变为所需的 time_point。1,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。当起始month 为December 时,添加months{1} 将增加year 字段。