c++ 如何用STL表示24小时制?

bqucvtff  于 2022-11-27  发布在  其他
关注(0)|答案(3)|浏览(196)

我正在从STL中寻找一个解决方案来处理“一天中的时间”。我正在做一个简单的单元测试练习,行为取决于当前时间是早上、晚上还是晚上。
在第一次迭代中,我使用了一个不起眼的整数来代替某个“时间对象”:

using TimeOfDay = int;
constexpr bool isBetween(TimeOfDay in, TimeOfDay min, TimeOfDay max) noexcept {
  return in >= min && in <= max; 
}
constexpr bool isMorning(TimeOfDay in) noexcept { 
  return isBetween(in, 6, 12); }
constexpr bool isEvening(TimeOfDay in) noexcept {
  return isBetween(in, 18, 22);
}
constexpr bool isNight(TimeOfDay in) noexcept {
  return isBetween(in, 22, 24) || isBetween(in, 0, 6);
}

constexpr string_view getGreetingFor(TimeOfDay time) noexcept {
  if (isMorning(time)) {
    return "Good morning "sv;
  }
  if (isEvening(time)) {
    return "Good evening "sv;
  }
  if (isNight(time)) {
    return "Good night "sv;
  }
  return "Hello "sv;
}

这是可行的,但有几个气味:

  • int不是表示24小时时钟的正确类型
  • isNight()需要进行不必要的复杂比较,这是由于换行(22-06)
  • 理想情况下,我希望能够实际使用系统时钟进行一些测试。
  • std::chrono::system_clock::now()返回一个std::chrono::time_point,所以我的理想类型应该是可以与time_point相比的类型,或者是可以从time_point轻松构造的类型。

任何指点都将不胜感激!
(我正在Visual Studio中使用C最新版本(预览C工作草案,因此大致为C++23))

xpszyzbs

xpszyzbs1#

首先假设您要查找的是 * 本地 * 时间。chrono::system_clock代表UTC。因此,我推荐一个helper函数,该函数接受std::chrono::system_clock::time_point并返回一个std::chrono::system_clock::duration,它代表从最近的本地午夜开始经过的时间:

using TimeOfDay = std::chrono::system_clock::duration;

TimeOfDay
get_local_time_of_day(std::chrono::system_clock::time_point tp) noexcept
{
  using namespace std::chrono;
  auto time = current_zone()->to_local(tp);
  return time - floor<days>(time);
}

chrono::current_zone()返回一个指向您的计算机当前设置的chrono::time_zone的指针。它用于将system_clock::time_point转换为 localtime_point。此本地time_point是一个chrono::time_point,但具有表示“本地时间”的不同时钟。
表达式floor<days>(time)time_point截断为精度days,这实际上为您提供了一个指向最近经过的当地午夜的指针。从time中减去该值将得到自当地午夜以来的chrono::duration。此duration将与tp具有相同的精度。
此函数不能被设置为constexpr,因为将UTC转换为本地时间的规则掌握在您的政治家手中,并且可能会发生变化(在许多国家/地区,一年两次!)。
现在可以这样写isMorning(et al):

bool isMorning(std::chrono::system_clock::time_point in) noexcept { 
  using namespace std::literals;
  return isBetween(get_local_time_of_day(in), 6h, 12h); }

并这样称呼它:

if (isMorning(system_clock::now())) ...

在我看来,isNight的逻辑没有问题,isBetween的代码也可以保持原样(采用TimeOfDay的新定义)。

isr3a4wc

isr3a4wc2#

如果你说的是一天中的时间,与日期分离,使用std::chrono::duration。对于整数小时,有别名std::chrono::hours
另请参阅std::chrono::hh_mm_ss
大概是这样的:

#include <iostream>
#include <chrono>

using namespace std::chrono;

int main()
{
    auto now = system_clock::now().time_since_epoch();
    auto today = duration_cast<days>(now);
    hh_mm_ss time { now - today };
    std::cout << time.hours() << time.minutes() << time.seconds();
}

See it on coliru

monwx1rj

monwx1rj3#

“时间”应该存储在std::chrono::duration中。要只分离任何duration的h/m/s信息,可以执行以下操作:

auto time_info = my_duration - std::chrono::round<std::chrono::days>(my_duration);

同样,您可以分离具有相同接口的time_point的h/m/s信息:

auto now = std::chrono::system_clock::now();
auto time_info = now - std::chrono::round<std::chrono::days>(now);

然后可以使用helper类hh_mm_ss来检查信息:

auto hms = std::chrono::hh_mm_ss{ time_info };
std::cout << hms.hours() << hms.minutes() << hms.seconds();

要检查一个duration是否在另外两个时间之间,你可以这样做:

using TimeOfDay = std::chrono::seconds;

constexpr bool isBetween(TimeOfDay in, TimeOfDay min, TimeOfDay max) noexcept {
    return in >= min && in <= max; 
}

constexpr bool isMorning(TimeOfDay in) noexcept { 
    return isBetween(in, std::chrono::hours{6}, std::chrono::hours{12}); 
}

您还可以通过using namespace std::chrono_literalsusing namespace std::literals启用文字:

constexpr bool isMorning(TimeOfDay in) noexcept { 
    return isBetween(in, 6h, 12h); 
}

对于isEvening中的 Package ,您可以执行类似以下的操作:isBetween(in + 12h - days{1}, 10h, 18h),但我认为你现在所拥有的有一个更干净,更容易遵循的逻辑。
如果需要,这可以很容易地转换回time_point

auto today = std::chrono::round<std::chrono::days>(std::chrono::system_clock::now());
auto now = today + time_info;

相关问题