date
, datetime
和 time
提供了对 MySQL 原生日期和时间类型的支持。本节将详细介绍如何使用它们。
date
表示 C++ 中的 MySQL DATE
。date
包含日期的年、月和日组成部分,不包含任何时区信息。它是一种更接近协议的类型,而不是词汇表类型。使用 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; }
![]() |
注意 |
---|---|
在 MSVC 下使用 |
如果您的编译器不支持本地时间,您可以使用 date::get_time_point
或 date::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
表示 C++ 中的 MySQL DATETIME
和 TIMESTAMP
。datetime
表示分解的时间点,具有年、月、日、小时、分钟、秒和微秒。
datetime
对象不携带任何时区信息。时区语义取决于实际的 MySQL 类型
MySQL 也接受无效的 datetime(如 2020-00-10 10:20:59.000000
)。
与 date
一样,您可以使用 datetime::as_local_time_point
, get_local_time_point
和 valid
(或 as_time_point
和 get_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
时,我们建议将 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
类型表示,它是以微秒为周期的 std::chrono::duration
专用的别名。