userver: userver/storages/postgres/io/chrono.hpp Source File
⚠️ This is the documentation for an old userver version. Click here to switch to the latest version.
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Friends Macros Modules Pages Concepts
chrono.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/storages/postgres/io/chrono.hpp
4/// @brief Timestamp (std::chrono::*) I/O support
5/// @ingroup userver_postgres_parse_and_format
6
7#include <chrono>
8#include <limits>
9
10#include <userver/storages/postgres/io/buffer_io.hpp>
11#include <userver/storages/postgres/io/buffer_io_base.hpp>
12#include <userver/storages/postgres/io/interval.hpp>
13#include <userver/storages/postgres/io/transform_io.hpp>
14#include <userver/storages/postgres/io/type_mapping.hpp>
15#include <userver/utils/strong_typedef.hpp>
16
17USERVER_NAMESPACE_BEGIN
18
19namespace storages::postgres {
20
21using ClockType = std::chrono::system_clock;
22
23/// @brief Corresponds to TIMESTAMP WITHOUT TIME ZONE (value in absolute time
24/// (no TZ offset)).
25using TimePoint = ClockType::time_point;
26
27/// @brief Corresponds to TIMESTAMP WITH TIME ZONE (value in absolute time (no
28/// TZ offset)). Time zones are applied automatically.
29using TimePointTz = USERVER_NAMESPACE::utils::StrongTypedef<
30 struct TimestampWithTz, TimePoint,
31 USERVER_NAMESPACE::utils::StrongTypedefOps::kCompareTransparent>;
32using IntervalType = std::chrono::microseconds;
33
34/// Postgres epoch timestamp (2000-01-01 00:00 UTC)
36
37/// Constant equivalent to PostgreSQL 'infinity'::timestamp, a time point that
38/// is later than all other time points
39/// https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-DATETIME-SPECIAL-TABLE
40inline constexpr TimePoint kTimestampPositiveInfinity = TimePoint::max();
41/// Constant equivalent to PostgreSQL '-infinity'::timestamp, a time point that
42/// is earlier than all other time points
43/// https://www.postgresql.org/docs/current/datatype-datetime.html#DATATYPE-DATETIME-SPECIAL-TABLE
44inline constexpr TimePoint kTimestampNegativeInfinity = TimePoint::min();
45
46/**
47 * @page pg_timestamp uPg timestamp support
48 *
49 * The driver provides mapping from C++ std::chrono::time_point template type to
50 * Postgres timestamp (without time zone) data type.
51 *
52 * To read/write timestamp with time zone Postgres data type a TimePointTz
53 * helper type is provided.
54 *
55 * Postgres internal timestamp resolution is microseconds.
56 *
57 * Note on time zones:
58 * std::chrono::time_point is an absolute time and does not correspond to any
59 * specific time zone. std::chrono::system_clock::now() and utils::Stringtime()
60 * return this kind of time point. It is always sent to PG as such, and you can
61 * think of it as an UTC time.
62 *
63 * Postgres allows implicit conversion between TIMESTAMP and TIMESTAMP WITH TIME
64 * ZONE and it *does not apply any offsets* when doing so.
65 *
66 * Because of this you MUST ensure that you always use the correct type:
67 * * std::chrono::time_point for TIMESTAMP;
68 * * storages::postgres::TimePointTz for TIMESTAMP FOR TIME ZONE.
69 * Otherwise, you'll get skewed times in database!
70 *
71 * @code
72 * namespace pg = storages::postgres;
73 *
74 * pg::Transaction trx = ...;
75 *
76 * auto now = std::chrono::system_clock::now();
77 * // Send as timestamp without time zone
78 * auto res = trx.Execute("select $1", now);
79 * // Send as timestamp with time zone
80 * res = trx.Execute("select $1", pg::TimePointTz{now});
81 * // Read as timestamp
82 * res[0].To(now);
83 * @endcode
84 */
85
86namespace io {
87
88/// @brief Binary formatter for std::chrono::time_point.
89template <typename Duration>
92
93 const ValueType value;
94
95 explicit BufferFormatter(ValueType val) : value{val} {}
96
97 template <typename Buffer>
98 void operator()(const UserTypes& types, Buffer& buf) const {
99 static const ValueType pg_epoch =
103 } else if (value == kTimestampNegativeInfinity) {
105 } else {
107 pg_epoch)
108 .count();
110 }
111 }
112};
113
114/// @brief Binary parser for std::chrono::time_point.
115template <typename Duration>
118 using BaseType =
120 using ValueType = typename BaseType::ValueType;
121 using BaseType::BaseType;
122
123 void operator()(const FieldBuffer& buffer) {
124 static const ValueType pg_epoch =
126 Bigint usec{0};
128 if (usec == std::numeric_limits<Bigint>::max()) {
130 } else if (usec == std::numeric_limits<Bigint>::min()) {
132 } else {
134 std::swap(tmp, this->value);
135 }
136 }
137};
138
139namespace detail {
140
141template <typename Rep, typename Period>
142struct DurationIntervalCvt {
143 using UserType = std::chrono::duration<Rep, Period>;
144 UserType operator()(const Interval& wire_val) const {
145 return std::chrono::duration_cast<UserType>(wire_val.GetDuration());
146 }
147 Interval operator()(const UserType& user_val) const {
148 return Interval{std::chrono::duration_cast<IntervalType>(user_val)};
149 }
150};
151
152} // namespace detail
153
154namespace traits {
155
156/// @brief Binary formatter for std::chrono::duration
157template <typename Rep, typename Period>
162};
163
164/// @brief Binary parser for std::chrono::duration
165template <typename Rep, typename Period>
167 using type =
170};
171
172} // namespace traits
173
174template <>
177
178template <typename Duration>
179struct CppToSystemPg<std::chrono::time_point<ClockType, Duration>>
180 : PredefinedOid<PredefinedOids::kTimestamp> {};
181
182template <typename Rep, typename Period>
183struct CppToSystemPg<std::chrono::duration<Rep, Period>>
184 : PredefinedOid<PredefinedOids::kInterval> {};
185
186} // namespace io
187} // namespace storages::postgres
188
189USERVER_NAMESPACE_END