Stroika Library 3.0d16
 
Loading...
Searching...
No Matches
TimeOfDay.h
Go to the documentation of this file.
1/*
2 * Copyright(c) Sophist Solutions, Inc. 1990-2025. All rights reserved
3 */
4#ifndef _Stroika_Foundation_Time_TimeOfDay_h_
5#define _Stroika_Foundation_Time_TimeOfDay_h_ 1
6
7#include "Stroika/Foundation/StroikaPreComp.h"
8
9#include <climits>
10#include <compare>
11#include <locale>
12#include <string>
13
15#include "Stroika/Foundation/Common/Common.h"
17#include "Stroika/Foundation/DataExchange/ValidationStrategy.h"
18#include "Stroika/Foundation/Execution/Exceptions.h"
20
21/**
22 * \file
23 *
24 * \note Code-Status: <a href="Code-Status.md#Release">Release</a>
25 *
26 * TODO:
27 * @todo Need DefaultNames<> for enums in TimeOfDay module
28 *
29 * @todo Consider having some way to support double as TimeOfDay (or maybe float). Don't want the
30 * complexity of the chrono code, but some of the power ;-). Not sure how to compromise.
31 *
32 * @todo I'm not sure eCurrentLocale_WithZerosStripped is a good idea. Not sure if better
33 * to use separate format print arg or???
34 *
35 * @todo Should we PIN or throw OVERFLOW exception on values/requests which are out of range?
36 *
37 * @todo (medium) Consider using strftime and strptime with %FT%T%z.
38 * Same format
39 * That doesn't use std::locale()
40 * En.cppreference.com/w/cpp/io/manip/get_time
41 * istringstream xxx ('2011-feb')
42 * ss.imbue(std::locale() ('de-DE'));
43 * ss >> std::get_time(&t, '%FT%T%z')
44 */
45
46namespace Stroika::Foundation::Time {
47
48 using Characters::String;
49
50 /**
51 * Description:
52 * A time value - which is assumed to be within a given day - e.g 2:30 pm.
53 *
54 * TimeOfDay precision is up to the second (use TimePointSeconds for more precision).
55 *
56 * \note this implies NO NOTION of timezone. Its a time relative to midnight of a given day.
57 *
58 * \note \em Thread-Safety <a href="Thread-Safety.md#C++-Standard-Thread-Safety">C++-Standard-Thread-Safety</a>
59 *
60 * \note <a href="Design-Overview.md#Comparisons">Comparisons</a>:
61 * static_assert (totally_ordered<TimeOfDay>);
62 */
63 class [[nodiscard]] TimeOfDay {
64 public:
65 /**
66 * NB: The maximum value in a TimeOfDay struct is one less than kMaxSecondsPerDay
67 */
68 static constexpr uint32_t kMaxSecondsPerDay = 60 * 60 * 24u; // nb: 86400: wont fit in uint16_t
69
70 public:
71 /**
72 * If value out of range - pinned to kMax.
73 * We normalize to be within a given day (seconds since midnight)
74 *
75 * For the TimeOfDay, we allow out of range values and pin/accumulate. But you can still never have a time of day >= kMaxSecondsPerDay.
76 * And the first hour (1pm) is hour 0, so TimeOfDay{2, 0, 0} is 3am.
77 *
78 * if DataExchange::ValidationStrategy is NOT specified, or == DataExchange::ValidationStrategy::eAssertion, then
79 * \pre argument time-of-day (in seconds or hours/minutes/seconds) is in valid range for one day
80 * \pre t < kMaxSecondsPerDay
81 * \pre hour < 24
82 * \pre minute < 60
83 * \pre seconds <= 60 (note <= not < due to leap seconds)
84 * else if validationStrategy == eThrow, then check and throw if out of range.
85 */
86 constexpr TimeOfDay (TimeOfDay&& src) noexcept = default;
87 constexpr TimeOfDay (const TimeOfDay&) = default;
88 constexpr explicit TimeOfDay (uint32_t t);
89 explicit TimeOfDay (uint32_t t, DataExchange::ValidationStrategy validationStrategy);
90 explicit constexpr TimeOfDay (unsigned int hour, unsigned int minute, unsigned int seconds = 0);
91 TimeOfDay (unsigned int hour, unsigned int minute, unsigned int seconds, DataExchange::ValidationStrategy validationStrategy);
92
93 public:
94 /**
95 */
96 nonvirtual TimeOfDay& operator= (TimeOfDay&&) noexcept = default;
97 nonvirtual TimeOfDay& operator= (const TimeOfDay&) = default;
98
99 public:
100 /**
101 * \note https://en.cppreference.com/w/cpp/locale/time_get/get and https://en.cppreference.com/w/cpp/locale/time_put/put
102 * equivalent to "%H:%M:%S"
103 *
104 * \note leading zeros in hours, minutes, seconds, required, not optional
105 * \note this is locale-independent
106 */
107 static constexpr string_view kISO8601Format = "%T"sv;
108
109 public:
110 /**
111 * \note https://en.cppreference.com/w/cpp/locale/time_get/get and https://en.cppreference.com/w/cpp/locale/time_put/put
112 */
113 static constexpr string_view kLocaleStandardFormat = "%X"sv;
114
115 public:
116 /**
117 * \note https://en.cppreference.com/w/cpp/locale/time_get/get and https://en.cppreference.com/w/cpp/locale/time_put/put
118 */
119 static constexpr string_view kLocaleStandardAlternateFormat = "%EX"sv;
120
121 public:
122 /**
123 * Default formats used by TimeOfDay::Parse () to parse time strings. The first of these - %X, is
124 * the locale-specific time format.
125 *
126 * \note https://en.cppreference.com/w/cpp/locale/time_get/get and https://en.cppreference.com/w/cpp/locale/time_put/put
127 */
128 static const Traversal::Iterable<String> kDefaultParseFormats;
129
130 public:
131 /**
132 * Always produces a valid legal TimeOfDay, or throws an exception.
133 *
134 * \note an empty string produces FormatException exception (whereas before 2.1d11 it produced an empty TimeOfDay object (TimeOfDay {}).
135 *
136 * \note the 2 argument locale overload uses each of kDefaultParseFormats formats to try to
137 * parse the time string, but the default is locale specific standard time format.
138 *
139 * \note overloads with the locale missing, default to locale{} - the default locale.
140 *
141 * \note format strings defined by https://en.cppreference.com/w/cpp/locale/time_get/get and https://en.cppreference.com/w/cpp/locale/time_put/put
142 *
143 * \see https://en.cppreference.com/w/cpp/locale/time_get/get for allowed formatPatterns
144 *
145 * The overload which takes a locale but no explicit format strings, defaults to trying
146 * each of kDefaultParseFormats strings in order, and returns the first match.
147 *
148 * The overload taking an iterable of formats, tries each, and returns the timeofday for the first that succeeds, or throws
149 * FormatException if none succeed.
150 */
151 static TimeOfDay Parse (const String& rep, const locale& l = locale{});
152 static TimeOfDay Parse (const String& rep, const String& formatPattern);
153 static TimeOfDay Parse (const String& rep, const locale& l, const String& formatPattern);
154 static TimeOfDay Parse (const String& rep, const locale& l, const Traversal::Iterable<String>& formatPatterns);
155
156 public:
157 /**
158 * \brief like Parse(), but returns nullopt on parse error, not throwing exception.
159 * if locale is missing, and formatPattern is not locale independent, the current locale (locale{}) is used.
160 * if rep is empty, this will return nullopt
161 */
162 static optional<TimeOfDay> ParseQuietly (const String& rep, const String& formatPattern);
163 static optional<TimeOfDay> ParseQuietly (const String& rep, const locale& l, const String& formatPattern);
164
165 private:
166 // this requires rep!= ""
167 static optional<TimeOfDay> ParseQuietly_ (const wstring& rep, const String& formatPattern);
168 static optional<TimeOfDay> ParseQuietly_ (const wstring& rep, const time_get<wchar_t>& tmget, const String& formatPattern);
169
170 public:
171 /**
172 * kMin is the first date this TimeOfDay class supports representing.
173 */
174 static const TimeOfDay kMin; // defined constexpr
175
176 public:
177 /**
178 * kMax is the last date this TimeOfDay class supports representing. This is a legal TimeOfDay, and
179 * not like 'end' - one past the last legal value.
180 */
181 static const TimeOfDay kMax; // defined constexpr
182
183 public:
184 class FormatException;
185
186 public:
187 /**
188 * \post {return} < kMaxSecondsPerDay
189 */
190 nonvirtual constexpr uint32_t GetAsSecondsCount () const; // seconds since StartOfDay (midnight)
191
192 public:
193 /**
194 */
195 nonvirtual void ClearSecondsField ();
196
197 public:
198 /**
199 * returns 0..23
200 */
201 nonvirtual constexpr uint8_t GetHours () const;
202
203 public:
204 /**
205 * returns 0..59
206 */
207 nonvirtual constexpr uint8_t GetMinutes () const;
208
209 public:
210 /**
211 * returns 0..59
212 */
213 nonvirtual constexpr uint8_t GetSeconds () const;
214
215 public:
216 /**
217 * \brief NonStandardPrintFormat is a representation which a TimeOfDay can be transformed into
218 *
219 * eCurrentLocale_WithZerosStripped
220 * eCurrentLocale_WithZerosStripped is eCurrentLocale, but with many cases of trailing zero's,
221 * and sometimes leading zeros, stripped, so for example, 01:03:05 PM will become 1:03:05 PM,
222 * and 04:06:00 PM will become 4:06 PM.
223 */
224 enum class NonStandardPrintFormat : uint8_t {
225 eCurrentLocale_WithZerosStripped,
226
227 eDEFAULT = eCurrentLocale_WithZerosStripped,
228
229 Stroika_Define_Enum_Bounds (eCurrentLocale_WithZerosStripped, eCurrentLocale_WithZerosStripped)
230 };
231
232 public:
233 using NonStandardPrintFormat::eCurrentLocale_WithZerosStripped;
234
235 public:
236 /**
237 * For formatPattern, see http://en.cppreference.com/w/cpp/locale/time_put/put
238 * If only formatPattern specified, and no locale, use default (global) locale.
239 *
240 * \note if locale is missing (not specified as argument) the default locale (locale{}) is used.
241 */
242 nonvirtual String Format (NonStandardPrintFormat pf = NonStandardPrintFormat::eDEFAULT) const;
243 nonvirtual String Format (const locale& l) const;
244 nonvirtual String Format (const locale& l, const String& formatPattern) const;
245 nonvirtual String Format (const String& formatPattern) const;
246
247 public:
248 /**
249 */
250 constexpr strong_ordering operator<=> (const TimeOfDay& rhs) const = default;
251
252 public:
253 /**
254 * @see Characters::ToString ()
255 */
256 nonvirtual String ToString () const;
257
258 private:
259 uint32_t fTime_;
260 };
261
262 class TimeOfDay::FormatException : public Execution::RuntimeErrorException<> {
263 public:
264 FormatException ();
265
266 public:
267 /**
268 */
269 static const FormatException kThe;
270 };
271 static_assert (totally_ordered<TimeOfDay>);
272
273 inline const TimeOfDay::FormatException TimeOfDay::FormatException::kThe;
274
275 //%t Any white space.
276 //%T The time as %H : %M : %S. (iso8601 format)
277 //%r is the time as %I:%M:%S %p
278 //%M The minute [00,59]; leading zeros are permitted but not required.
279 //%p Either 'AM' or 'PM' according to the given time value, or the corresponding strings for the current locale. Noon is treated as 'pm' and midnight as 'am'.
280 //%P Like %p but in lowercase: 'am' or 'pm' or a corresponding string for the current locale. (GNU)
281 //%S The seconds [00,60]; leading zeros are permitted but not required.
283 kLocaleStandardFormat,
284 kLocaleStandardAlternateFormat,
285 kISO8601Format,
286 "%r"sv,
287 "%H:%M"sv,
288 "%I%p"sv,
289 "%I%P"sv,
290 "%I%t%p"sv,
291 "%I%t%P"sv,
292 "%I:%M%t%p"sv,
293 "%I:%M%t%P"sv,
294 "%I:%M:%S%t%p"sv,
295 "%I:%M:%S%t%P"sv,
296 "%I:%M:%S"sv,
297 "%I:%M"sv,
298 };
299
300}
301
302/*
303 ********************************************************************************
304 ***************************** Implementation Details ***************************
305 ********************************************************************************
306 */
307#include "TimeOfDay.inl"
308
309#endif /*_Stroika_Foundation_Time_TimeOfDay_h_*/
#define Stroika_Define_Enum_Bounds(FIRST_ITEM, LAST_ITEM)
String is like std::u32string, except it is much easier to use, often much more space efficient,...
Definition String.h:201
static const Traversal::Iterable< String > kDefaultParseFormats
Definition TimeOfDay.h:128
static const TimeOfDay kMin
Definition TimeOfDay.h:174
static const TimeOfDay kMax
Definition TimeOfDay.h:181
constexpr TimeOfDay(TimeOfDay &&src) noexcept=default
NonStandardPrintFormat
NonStandardPrintFormat is a representation which a TimeOfDay can be transformed into.
Definition TimeOfDay.h:224
Iterable<T> is a base class for containers which easily produce an Iterator<T> to traverse them.
Definition Iterable.h:237