userver: userver/storages/postgres/io/chrono.hpp Source File
Loading...
Searching...
No Matches
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