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