userver: userver/utils/datetime_light.hpp Source File
Loading...
Searching...
No Matches
datetime_light.hpp
Go to the documentation of this file.
1#pragma once
2
3/// @file userver/utils/datetime_light.hpp
4/// @brief Date and Time related converters
5/// @ingroup userver_universal
6
7#include <chrono>
8#include <cstdint>
9#include <optional>
10#include <ratio>
11#include <stdexcept>
12#include <string>
13
14#include <cctz/civil_time.h>
15#include <cctz/time_zone.h>
16
17#include <userver/utils/datetime/wall_coarse_clock.hpp>
18
19USERVER_NAMESPACE_BEGIN
20
21namespace utils::datetime {
22/// @snippet utils/datetime/from_string_saturating_test.cpp kRfc3339Format
23inline const std::string kRfc3339Format = "%Y-%m-%dT%H:%M:%E*S%Ez";
24/// @snippet utils/datetime/from_string_saturating_test.cpp kTaximeterFormat
25inline const std::string kTaximeterFormat = "%Y-%m-%dT%H:%M:%E6SZ";
26/// @snippet utils/datetime/from_string_saturating_test.cpp kAbsoluteDeadlineFormat
27inline const std::string kAbsoluteDeadlineFormat = "%Y-%m-%dT%H:%M:%E6SZ";
28inline constexpr std::time_t kStartOfTheEpoch = 0;
29/// @snippet utils/datetime_test.cpp kDefaultDriverTimezone
30inline const std::string kDefaultDriverTimezone = "Europe/Moscow";
31/// @snippet utils/datetime_test.cpp kDefaultTimezone
32inline const std::string kDefaultTimezone = "UTC";
33/// @snippet utils/datetime/from_string_saturating_test.cpp kDefaultFormat
34inline const std::string kDefaultFormat = "%Y-%m-%dT%H:%M:%E*S%z";
35/// @snippet utils/datetime/from_string_saturating_test.cpp kIsoFormat
36inline const std::string kIsoFormat = "%Y-%m-%dT%H:%M:%SZ";
37inline const std::string kFractionFormat = "%Y-%m-%dT%H:%M:%S.%E*f%z";
38
39using timepair_t = std::pair<std::uint8_t, std::uint8_t>;
40
41/// Date/time parsing error
42class DateParseError : public std::runtime_error {
43public:
44 DateParseError(const std::string& timestring);
45};
46
47/// Timezone information lookup error
48class TimezoneLookupError : public std::runtime_error {
49public:
50 TimezoneLookupError(const std::string& tzname);
51};
52
53/// @brief std::chrono::system_clock::now() that could be mocked
54///
55/// Returns last time point passed to utils::datetime::MockNowSet(), or
56/// std::chrono::system_clock::now() if the timepoint is not mocked.
57std::chrono::system_clock::time_point Now() noexcept;
58
59/// @brief Returns std::chrono::system_clock::time_point from the start of the
60/// epoch
61std::chrono::system_clock::time_point Epoch() noexcept;
62
63/// @brief std::chrono::steady_clock::now() that could be mocked
64///
65/// Returns last time point passed to utils::datetime::MockNowSet(), or
66/// std::chrono::steady_clock::now() if the timepoint is not mocked.
67///
68/// It is only intended for period-based structures/algorithms testing.
69///
70/// @warning You MUST NOT pass time points received from this function outside
71/// of your own code. Otherwise this will break your service in production.
72std::chrono::steady_clock::time_point SteadyNow() noexcept;
73
74/// @brief utils::datetime::WallCoarseClock::now() that could be mocked
75///
76/// Returns last time point passed to utils::datetime::MockNowSet(), or utils::datetime::WallCoarseClock::now() if
77/// the timepoint is not mocked.
78WallCoarseClock::time_point WallCoarseNow() noexcept;
79
80// See the comment to SteadyNow()
81class SteadyClock : public std::chrono::steady_clock {
82public:
83 using time_point = std::chrono::steady_clock::time_point;
84
85 static time_point now() { return SteadyNow(); }
86};
87
88/// @brief Returns true if the time is in range; works over midnight too
90 int hour,
91 int min,
92 int hour_from,
93 int min_from,
94 int hour_to,
95 int min_to,
96 bool include_time_to = false
97) noexcept;
98
99/// @brief Extracts time point from a string, guessing the format
100/// @note Use GuessStringtime instead
101/// @throws utils::datetime::DateParseError
102std::chrono::system_clock::time_point DoGuessStringtime(const std::string& timestring, const cctz::time_zone& timezone);
103
104/// @brief Returns time in a string of specified format in local timezone
105/// @see kRfc3339Format, kTaximeterFormat, kStartOfTheEpoch,
106/// kDefaultDriverTimezone, kDefaultTimezone, kDefaultFormat, kIsoFormat
107std::string LocalTimezoneTimestring(std::time_t timestamp, const std::string& format = kDefaultFormat);
108
109/// @brief Returns time in a string of specified format in UTC timezone
110/// @see kRfc3339Format, kTaximeterFormat, kStartOfTheEpoch,
111/// kDefaultDriverTimezone, kDefaultTimezone, kDefaultFormat, kIsoFormat
112std::string UtcTimestring(std::time_t timestamp, const std::string& format = kDefaultFormat);
113
114std::string Timestring(std::chrono::system_clock::time_point tp);
115
116/// @brief Returns time in a string of specified format in local timezone
117/// @see kRfc3339Format, kTaximeterFormat, kStartOfTheEpoch,
118/// kDefaultDriverTimezone, kDefaultTimezone, kDefaultFormat, kIsoFormat
120 std::chrono::system_clock::time_point tp,
121 const std::string& format = kDefaultFormat
122);
123
124/// @brief Returns time in a string of specified format in UTC timezone
125/// @see kRfc3339Format, kTaximeterFormat, kStartOfTheEpoch,
126/// kDefaultDriverTimezone, kDefaultTimezone, kDefaultFormat, kIsoFormat
127/// @throws std::runtime_error if tp does not fit into @c std::chrono::hours
128template <class Duration>
129std::string UtcTimestring(
130 std::chrono::time_point<std::chrono::system_clock, Duration> tp,
131 const std::string& format = kDefaultFormat
132) {
133 using quotient = std::ratio_divide<typename Duration::period, std::chrono::hours::period>;
134 if constexpr (quotient::num <= quotient::den) {
135 return cctz::format(format, tp, cctz::utc_time_zone());
136 } else {
137 // cctz cannot handle time_points with duration more than hours
138 const auto days = tp.time_since_epoch();
139 if (days > std::chrono::duration_cast<Duration>(std::chrono::hours::max())) {
140 throw std::runtime_error("tp does not find std::chrono::hours");
141 }
142 const std::chrono::time_point<std::chrono::system_clock, std::chrono::hours>
143 tp_hours(std::chrono::duration_cast<std::chrono::hours>(days));
144 return cctz::format(format, tp_hours, cctz::utc_time_zone());
145 }
146}
147
148/// @brief Extracts time point from a string of a specified format in local timezone
149/// @throws utils::datetime::DateParseError
150/// @see kRfc3339Format, kTaximeterFormat, kStartOfTheEpoch,
151/// kDefaultDriverTimezone, kDefaultTimezone, kDefaultFormat, kIsoFormat
152std::chrono::system_clock::time_point LocalTimezoneStringtime(
153 const std::string& timestring,
154 const std::string& format = kDefaultFormat
155);
156
157/// @brief Extracts time point from a string of a specified format in UTC timezone
158/// @throws utils::datetime::DateParseError
159/// @see kRfc3339Format, kTaximeterFormat, kStartOfTheEpoch,
160/// kDefaultDriverTimezone, kDefaultTimezone, kDefaultFormat, kIsoFormat
161std::chrono::system_clock::time_point UtcStringtime(
162 const std::string& timestring,
163 const std::string& format = kDefaultFormat
164);
165
166/// @brief Extracts time point from a string of a kDefaultFormat format in UTC timezone
167/// @throws utils::datetime::DateParseError
168std::chrono::system_clock::time_point Stringtime(const std::string& timestring);
169
170/// @brief Extracts time point from a string of a specified format
171/// @throws utils::datetime::DateParseError
172/// @see kRfc3339Format, kTaximeterFormat, kStartOfTheEpoch,
173/// kDefaultDriverTimezone, kDefaultTimezone, kDefaultFormat, kIsoFormat
174std::optional<std::chrono::system_clock::time_point> OptionalStringtime(
175 const std::string& timestring,
176 const cctz::time_zone& timezone,
177 const std::string& format
178);
179
180/// @brief Extracts time point from a string of a kDefaultFormat format in UTC timezone
181/// @throws utils::datetime::DateParseError
182std::optional<std::chrono::system_clock::time_point> OptionalStringtime(const std::string& timestring);
183
184/// @brief Extracts time point from a string, guessing the format
185/// @throws utils::datetime::DateParseError
186std::chrono::system_clock::time_point GuessLocalTimezoneStringtime(const std::string& timestamp);
187
188/// @brief Converts time point to std::time_t
189///
190/// Example:
191///
192/// @snippet utils/datetime_test.cpp UtcTimestring C time example
193std::time_t Timestamp(std::chrono::system_clock::time_point tp) noexcept;
194
195/// @brief Returned current time as std::time_t; could be mocked
196std::time_t Timestamp() noexcept;
197
198/// @brief Parse day time in hh:mm[:ss] format
199/// @param str day time in format hh:mm[:ss]
200/// @return number of second since start of day
201std::uint32_t ParseDayTime(const std::string& str);
202
203/// @brief Converts absolute time in std::chrono::system_clock::time_point to
204/// a civil time of a local timezone.
205cctz::civil_second LocalTimezoneLocalize(const std::chrono::system_clock::time_point& tp);
206
207/// @brief Converts a civil time in a local timezone into an absolute time.
208std::time_t LocalTimezoneUnlocalize(const cctz::civil_second& local_tp);
209
210/// @brief Returns string with time in ISO8601 format "YYYY-MM-DDTHH:MM:SS+0000"
211/// @param timestamp unix timestamp
212std::string TimestampToString(std::time_t timestamp);
213
214/// @brief Convert time_point to DotNet ticks
215/// @param tp time point
216/// @return number of 100-nanosecond intervals between current date and 01/01/0001
217///
218/// Example:
219///
220/// @snippet utils/datetime_test.cpp TimePointToTicks example
221std::int64_t TimePointToTicks(const std::chrono::system_clock::time_point& tp) noexcept;
222
223/// @brief Convert DotNet ticks to a time point
224std::chrono::system_clock::time_point TicksToTimePoint(std::int64_t ticks) noexcept;
225
226/// @brief Compute (a - b) with a specified duration
227template <class Duration, class Clock>
228double CalcTimeDiff(const std::chrono::time_point<Clock>& a, const std::chrono::time_point<Clock>& b) {
229 const auto duration_a = a.time_since_epoch();
230 const auto duration_b = b.time_since_epoch();
231 return std::chrono::duration_cast<Duration>(duration_a - duration_b).count();
232}
233
234} // namespace utils::datetime
235
236USERVER_NAMESPACE_END