userver
C++ Async Framework
Toggle main menu visibility
Loading...
Searching...
No Matches
datetime_utils.hpp
Go to the documentation of this file.
1
#
pragma
once
2
3
/// @file
4
/// @brief Utilities for @c google::protobuf::Timestamp, @c google::type::Date, and @c google::protobuf::Duration types.
5
6
#
include
<
chrono
>
7
#
include
<
stdexcept
>
8
#
include
<
version
>
9
10
#
include
<
google
/
protobuf
/
duration
.
pb
.
h
>
11
#
include
<
google
/
protobuf
/
timestamp
.
pb
.
h
>
12
#
include
<
google
/
type
/
date
.
pb
.
h
>
13
14
#
include
<
userver
/
formats
/
json_fwd
.
hpp
>
15
#
include
<
userver
/
formats
/
parse
/
to
.
hpp
>
16
#
include
<
userver
/
formats
/
serialize
/
to
.
hpp
>
17
#
include
<
userver
/
utils
/
assert
.
hpp
>
18
#
include
<
userver
/
utils
/
datetime
/
cpp_20_calendar
.
hpp
>
19
#
include
<
userver
/
utils
/
datetime
/
date
.
hpp
>
20
21
USERVER_NAMESPACE_BEGIN
22
23
namespace
ugrpc {
24
25
/// @brief Checks if @c google::protobuf::Timestamp contains a valid value according to protobuf documentation.
26
///
27
/// The timestamp must be from 0001-01-01T00:00:00Z to 9999-12-31T23:59:59Z inclusive
28
/// and 'nanos' must be from 0 to 999,999,999 inclusive.
29
bool
IsValid
(
const
google::protobuf::Timestamp& grpc_ts);
30
31
/// @brief Exception thrown when timestamp conversion functions receive/produce invalid @c google::protobuf::Timestamp
32
/// according to @c IsValid or otherwise lead to undefined behavior due to integer overflow.
33
class
TimestampConversionError
:
public
std::overflow_error {
34
using
std::overflow_error::overflow_error;
35
};
36
37
/// @brief Creates @c google::protobuf::Timestamp from @c std::chrono::time_point.
38
///
39
/// @throws TimestampConversionError if the value is not valid according to @c IsValid.
40
template
<
class
Duration>
41
google::protobuf::Timestamp
ToProtoTimestamp
(
const
std::chrono::time_point<std::chrono::system_clock, Duration>&
42
system_tp) {
43
const
auto
seconds = std::chrono::duration_cast<std::chrono::seconds>(system_tp.time_since_epoch());
44
const
auto
nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(system_tp.time_since_epoch() - seconds);
45
46
google::protobuf::Timestamp timestamp;
47
timestamp.set_seconds(seconds.count());
48
timestamp.set_nanos(nanos.count());
49
50
if
(!
IsValid
(
timestamp
)
) {
51
throw
TimestampConversionError
(
"system_tp is invalid"
);
52
}
53
return
timestamp;
54
}
55
56
/// @brief Creates @c std::chrono::system_clock::time_point from @c google::protobuf::Timestamp.
57
///
58
/// @throws TimestampConversionError if the value is not valid according to @c IsValid or does not fit the time_point.
59
template
<
class
Duration = std::chrono::system_clock::duration>
60
std::chrono::time_point<std::chrono::system_clock, Duration>
ToTimePoint
(
const
google::protobuf::Timestamp& grpc_ts) {
61
if
(!
IsValid
(
grpc_ts
)
) {
62
throw
TimestampConversionError
(
"grpc_ts is invalid"
);
63
}
64
65
const
std::chrono::seconds seconds(grpc_ts.seconds());
66
// Equality is important here. Otherwise, grpc_duration.seconds() == std::chrono::seconds::max()
67
// will lead to overflow if grpc_duration.nanos() != 0.
68
if
(seconds >= std::chrono::duration_cast<std::chrono::seconds>(Duration::max())) {
69
throw
TimestampConversionError
(
"grpc_ts does not fit the output type"
);
70
}
71
72
return
std::chrono::time_point<
73
std::chrono::system_clock,
74
Duration>(std::chrono::duration_cast<
75
Duration>(seconds + std::chrono::duration_cast<Duration>(std::chrono::nanoseconds(grpc_ts.nanos()))));
76
}
77
78
/// @brief Returns current (possibly, mocked) timestamp as a @c google::protobuf::Timestamp.
79
google::protobuf::Timestamp
NowTimestamp
();
80
81
/// @brief Checks if @c google::type::Date contains a valid value according to protobuf documentation.
82
///
83
/// Year must be from 1 to 9999.
84
/// Month must be from 1 to 12.
85
/// Day must be from 1 to 31 and valid for the year and month.
86
bool
IsValid
(
const
google::type::Date& grpc_date);
87
88
/// @brief Exception thrown when date conversion functions receive/produce invalid @c google::type::Date
89
/// according to @c IsValid or otherwise lead to undefined behavior due to integer overflow.
90
class
DateConversionError
:
public
std::overflow_error {
91
using
std::overflow_error::overflow_error;
92
};
93
94
#
if
__cpp_lib_chrono
>=
201907L
95
96
/// @brief Creates @c google::type::Date from @c std::chrono::year_month_day.
97
///
98
/// @throws DateConversionError if the value is not valid according to @c IsValid.
99
google::type::Date ToProtoDate(
const
std::chrono::year_month_day& system_date);
100
101
/// @brief Creates @c std::chrono::year_month_day from @c google::type::Date.
102
///
103
/// @throws DateConversionError if the value is not valid according to @c IsValid.
104
std::chrono::year_month_day ToYearMonthDay(
const
google::type::Date& grpc_date);
105
106
#
endif
107
108
/// @brief Creates @c google::type::Date from @c utils::datetime::Date.
109
///
110
/// @throws DateConversionError if the value is not valid according to @c IsValid.
111
google::type::Date
ToProtoDate
(
const
utils
::
datetime
::Date& utils_date);
112
113
/// @brief Creates @c utils::datetime::Date from @c google::type::Date.
114
///
115
/// @throws DateConversionError if the value is not valid according to @c IsValid.
116
utils
::
datetime
::Date
ToUtilsDate
(
const
google::type::Date& grpc_date);
117
118
/// @brief Creates @c google::type::Date from @c std::chrono::time_point.
119
///
120
/// @throws DateConversionError if the value is not valid according to @c IsValid.
121
template
<
class
Duration>
122
google::type::Date
ToProtoDate
(
const
std::chrono::time_point<std::chrono::system_clock, Duration>& system_tp) {
123
return
ToProtoDate
(
utils
::
datetime
::Date(std::chrono::floor<
utils
::
datetime
::Date::Days>(system_tp))
)
;
124
}
125
126
/// @brief Creates @c std::chrono::system_clock::time_point from @c google::type::Date.
127
///
128
/// @throws DateConversionError if the value is not valid according to @c IsValid.
129
std::chrono::time_point<std::chrono::system_clock,
utils
::
datetime
::Days>
ToTimePoint
(
130
const
google::type::Date& grpc_date
131
);
132
133
/// @brief Returns current (possibly, mocked) timestamp as a @c google::type::Date.
134
google::type::Date
NowDate
();
135
136
/// @brief Checks if @c google::protobuf::Duration contains a valid value according to protobuf documentation.
137
///
138
/// Seconds must be from -315,576,000,000 to +315,576,000,000 inclusive.
139
/// Note: these bounds are computed from: 60 sec/min * 60 min/hr * 24 hr/day * 365.25 days/year * 10000 years
140
/// For durations of one second or more, a non-zero value for the `nanos` field must be of the same sign as `seconds`.
141
/// Nanos must also be from -999,999,999 to +999,999,999 inclusive.
142
bool
IsValid
(
const
google::protobuf::Duration& grpc_duration);
143
144
/// @brief Exception thrown when duration conversion functions receive/produce invalid @c google::protobuf::Duration
145
/// according to @c IsValid or otherwise lead to undefined behavior due to integer overflow.
146
class
DurationConversionError
:
public
std::overflow_error {
147
using
std::overflow_error::overflow_error;
148
};
149
150
/// @brief Creates @c std::chrono::duration from @c google::protobuf::Duration.
151
///
152
/// @throws DurationConversionError if the value is not valid according to @c IsValid or does not fit the Duration.
153
template
<
class
Duration = std::chrono::microseconds>
154
Duration
ToDuration
(
const
google::protobuf::Duration& grpc_duration) {
155
if
(!
IsValid
(
grpc_duration
)
) {
156
throw
DurationConversionError
(
"grpc_duration is invalid"
);
157
}
158
159
const
std::chrono::seconds seconds(grpc_duration.seconds());
160
// Equality is important here. Otherwise, grpc_duration.seconds() == std::chrono::seconds::max()
161
// will lead to overflow if grpc_duration.nanos() != 0.
162
if
(seconds >= std::chrono::duration_cast<std::chrono::seconds>(Duration::max())) {
163
throw
DurationConversionError
(
"grpc_duration does not fit the output type"
);
164
}
165
return
std::chrono::duration_cast<
166
Duration>(seconds + std::chrono::duration_cast<Duration>(std::chrono::nanoseconds(grpc_duration.nanos())));
167
}
168
169
/// @brief Creates @c google::protobuf::Duration from @c std::chrono::duration.
170
///
171
/// @throws DurationConversionError if the value is not valid according to @c IsValid.
172
template
<
class
Rep,
class
Period>
173
google::protobuf::Duration
ToProtoDuration
(
const
std::chrono::duration<Rep, Period>& duration) {
174
const
auto
seconds = std::chrono::duration_cast<std::chrono::seconds>(duration);
175
const
auto
nanos = std::chrono::duration_cast<std::chrono::nanoseconds>(duration - seconds);
176
177
google::protobuf::Duration result;
178
result.set_seconds(seconds.count());
179
result.set_nanos(nanos.count());
180
if
(!
IsValid
(
result
)
) {
181
throw
DurationConversionError
(
"duration is invalid"
);
182
}
183
return
result;
184
}
185
186
}
// namespace ugrpc
187
188
namespace
formats::
parse
{
189
190
google::protobuf::Timestamp Parse(
const
formats::
json
::
Value
& json, formats::
parse
::
To
<google::protobuf::Timestamp>);
191
192
google::type::Date Parse(
const
formats::
json
::
Value
& json, formats::
parse
::
To
<google::type::Date>);
193
194
}
// namespace formats::parse
195
196
namespace
formats::
serialize
{
197
198
formats::
json
::
Value
Serialize(
const
google::protobuf::Timestamp& value, formats::
serialize
::
To
<formats::
json
::
Value
>);
199
200
formats::
json
::
Value
Serialize(
const
google::type::Date& value, formats::
serialize
::
To
<formats::
json
::
Value
>);
201
202
}
// namespace formats::serialize
203
204
USERVER_NAMESPACE_END
userver
ugrpc
datetime_utils.hpp
Generated on
for userver by
Doxygen
1.17.0