为什么时间是个麻烦(以及C++如何提供帮助)
\n想象一下:您的服务器处理来自纽约、伦敦和东京的订单。您以UTC记录订单创建时间,然后向用户显示:“您的订单创建于03:15”。对于纽约居民来说,这是前一天的22:15,对于伦敦人来说是凌晨04:15,而东京人则会在半夜被通知吵醒。这是一个典型的问题,它把简单的日志变成了一个谜题。
\n在C++中,很长一段时间都没有标准的时区处理方式。开发者要么忍受time_t和struct tm的折磨,要么引入像Boost.DateTime这样的重型库。但随着C++20的到来,情况发生了根本性的变化。std::chrono库获得了对时区的全面支持。尽管许多编译器仍在追赶标准,但我们将讨论如何从现在开始编写干净且可移植的C++时区处理代码。
在本文中,我将向您展示如何用C++将时区的混乱变成一个有序的系统。您将学习如何使用std::chrono::tzdb,将UTC转换为本地时间,考虑夏令时,并避免常见错误。
基础:从chrono角度看C++中的时区是什么
\n在C++20中,时区不仅仅是相对于UTC的小时偏移量。它是一个完整的对象,了解该地区时间变更的历史:何时调整时钟,何时取消夏令时,以及当前生效的规则。所有这些信息都存储在时区数据库(tzdb)中,该数据库内置于标准库中。
\n\n处理时区的关键类型
\n为了避免混淆,请记住三种主要类型:
\n- \n
std::chrono::time_zone— 表示一个特定的时区(例如,"America/New_York")。 \n std::chrono::zoned_time— “时区 + 时间点”的组合。这是存储和显示考虑时区时间的最方便类型。 \n std::chrono::time_zone_link— 时区的替代名称(例如,"US/Eastern"可能是"America/New_York"的链接)。 \n
获取所有时区的列表
\n让我们从简单的事情开始:列出所有可用的时区。当您允许用户选择他们的时区时,这很有用。
\n\n#include <iostream>\n#include <chrono>\n#include <string>\n\nint main() {\n // 访问时区数据库\n const auto& tzdb = std::chrono::get_tzdb();\n \n // 输出所有时区的名称\n std::cout << "可用时区 (" << tzdb.zones.size() << "):\\n";\n for (const auto& zone : tzdb.zones) {\n std::cout << zone.name() << '\\n';\n }\n return 0;\n}\n\n请注意:get_tzdb()返回一个常量引用。数据库在第一次调用时加载一次并缓存。这意味着您可以在程序的不同部分安全地调用此函数——只有在第一次访问时才会有开销。
在C++中转换不同时区的时间
\n现在进入最重要的部分——转换。假设您有一个UTC时间,并且您想向用户显示其本地时区的时间。或者反过来:用户以其本地时间输入日期,但您需要以UTC存储。
\n\n从UTC到本地时间
\n场景:服务器收到一个UTC时间的事件(例如,2024-12-01 15:30:00 UTC)。需要为"Europe/Moscow"时区显示它。
#include <iostream>\n#include <chrono>\n#include <format> // C++20 用于格式化\n\nint main() {\n using namespace std::chrono;\n \n // 1. 创建一个UTC时间点\n // 2024-12-01 15:30:00 UTC\n auto tp = sys_days{December / 1 / 2024} + 15h + 30min;\n