Boost C++ 库

...世界上最受推崇和设计最精良的 C++ 库项目之一。 Herb SutterAndrei Alexandrescu, C++ 编码标准

PrevUpHomeNext

时间类型:date、datetime 和 time

date, datetimetime 提供了对 MySQL 原生日期和时间类型的支持。本节将详细介绍如何使用它们。

date 类型

date 表示 C++ 中的 MySQL DATEdate 包含日期的年、月和日组成部分,不包含任何时区信息。它是一种更接近协议的类型,而不是词汇表类型。使用 date 而不是 std::chrono::time_point 类型的主要原因是,在某些配置下,MySQL 允许存储无效日期,例如 2020-00-01。这些不能表示为 std::chrono::time_point。除非处理这些特殊值,否则我们建议在使用 date 之前将其转换为 time_point

由于 date 表示本地时间点,std::chrono::local_time 是对其最准确的表示。如果您的编译器支持 C++20 日历类型(按照 __cpp_lib_chrono >= 201907L),您可以使用 date::as_local_time_point 来执行转换

date d(2020, 2, 19);                                   // d holds "2020-02-19"
std::chrono::local_days tp = d.as_local_time_point();  // now use tp normally

如果日期无效,as_time_point 将抛出异常。

您可以使用 date::valid 查询 date 是否包含有效日期

date d1(2020, 2, 19);  // regular date
bool v1 = d1.valid();  // true
date d2(2020, 0, 19);  // invalid date
bool v2 = d2.valid();  // false

您可以将其与 date::get_local_time_point 结合使用,后者执行未经检查的转换

date d = /* obtain a date somehow */ date(2020, 2, 29);
if (d.valid())
{
    // Same as as_time_point, but doesn't check for validity
    // Caution: be sure to check for validity.
    // If d is not valid, get_time_point results in undefined behavior
    std::chrono::local_days tp = d.get_local_time_point();

    // Use tp as required
    std::cout << tp.time_since_epoch().count() << std::endl;
}
else
{
    // the date is invalid
    std::cout << "Invalid date" << std::endl;
}
[Note] 注意

在 MSVC 下使用 std::chrono 时区功能可能会导致您的工具报告内存泄漏。这是 MSVC 标准库中的一个问题。有关可能的解决方法,请参阅 此建议

如果您的编译器不支持本地时间,您可以使用 date::get_time_pointdate::as_time_point 代替。这些返回 date::time_point 对象,这些对象是使用系统时钟的 time_points。这些时间点应解释为本地时间,而不是 UTC

date d(2020, 2, 19);  // d holds "2020-02-19"

// date::time_point is a std::chrono::time_point that uses std::chrono::system_clock
// tp is a local time, rather than UTC
// tp holds the same time_since_epoch() than d.as_local_time_point()
date::time_point tp = d.as_time_point();

datetime 类型

datetime 表示 C++ 中的 MySQL DATETIMETIMESTAMPdatetime 表示分解的时间点,具有年、月、日、小时、分钟、秒和微秒。

datetime 对象不携带任何时区信息。时区语义取决于实际的 MySQL 类型

MySQL 也接受无效的 datetime(如 2020-00-10 10:20:59.000000)。

date 一样,您可以使用 datetime::as_local_time_point, get_local_time_pointvalid (或 as_time_pointget_time_point,如果您的编译器不支持 C++20 日历类型)

datetime dt1(2020, 10, 11, 10, 20, 59, 123456);  // regular datetime 2020-10-11 10:20:59.123456
bool v1 = dt1.valid();                           // true
datetime dt2(2020, 0, 11, 10, 20, 59);           // invalid datetime 2020-00-10 10:20:59.000000
bool v2 = dt2.valid();                           // false


// local_time_point is a std::chrono::local_time with microsecond resolution
// Only available if your compiler supports C++20 calendar types
datetime::local_time_point tp = dt1.as_local_time_point();

// If you're using an older compiler, use as_time_point.
// tp2 uses std::chrono::system_clock and microsecond resolution.
// tp2 should be interpreted as a local time, rather than UTC
datetime::time_point tp2 = dt1.as_time_point();

TIMESTAMP 注意事项

当使用 TIMESTAMP 时,我们建议将 time_zone 会话变量设置为已知值。为了说明这一点,请考虑具有以下表定义的事件日志系统

results result;
conn.execute(
    R"%(
        CREATE TEMPORARY TABLE events (
            id INT PRIMARY KEY AUTO_INCREMENT,
            t TIMESTAMP,
            contents VARCHAR(256)
        )
    )%",
    result
);

我们将插入带有显式时间戳的事件。我们可能还想检索带有时间戳过滤器的事件。这是我们的预处理语句的样子

auto insert_stmt = conn.prepare_statement("INSERT INTO events (t, contents) VALUES (?, ?)");
auto select_stmt = conn.prepare_statement("SELECT id, t, contents FROM events WHERE t > ?");

这些语句可以从我们代码的不同部分运行,甚至可以从不同的应用程序运行。为了获得一致的结果,我们必须确保插入和检索期间使用的时区相同。默认情况下,time_zone 设置为 SYSTEM,这将使用服务器的时区设置。这不是我们想要的,所以让我们更改它

// This change has session scope. All operations after this query
// will now use UTC for TIMESTAMPs. Other sessions will not see the change.
// If you need to reconnect the connection, you need to run this again.
// If your MySQL server supports named time zones, you can also use
// "SET time_zone = 'UTC'"
conn.execute("SET time_zone = '+00:00'", result);

这样,插入代码可以如下所示

// Get the timestamp of the event. This may have been provided by an external system
// For the sake of example, we will use the current timestamp
datetime event_timestamp = datetime::now();

// event_timestamp will be interpreted as UTC if you have run SET time_zone
conn.execute(insert_stmt.bind(event_timestamp, "Something happened"), result);

查询代码将是

// Get the timestamp threshold from the user. We will use a constant for the sake of example
datetime threshold = datetime(2022, 1, 1);  // get events that happened after 2022-01-01 UTC

// threshold will be interpreted as UTC. The retrieved events will have their
// `t` column in UTC
conn.execute(select_stmt.bind(threshold), result);

如果您不设置 time_zone,如果您从不设置 time_zone 的客户端运行插入和查询,并且服务器不更改其配置,您可能会明显获得正确的结果。但是,依赖这一点会使您的应用程序变得脆弱,因此我们不建议这样做。

TIME 类型

TIME 类型是带符号的持续时间,分辨率为一微秒。它使用 time 类型表示,它是以微秒为周期的 std::chrono::duration 专用的别名。


PrevUpHomeNext