MT Core (C++)
Core library for replacing C++ standard in project usage
Loading...
Searching...
No Matches
calendars/mtcore_calendars/time.hpp
Go to the documentation of this file.
1/*
2
3Copyright 2025 Matthew Tolman
4
5Licensed under the Apache License, Version 2.0 (the "License");
6you may not use this file except in compliance with the License.
7You may obtain a copy of the License at
8
9 http://www.apache.org/licenses/LICENSE-2.0
10
11Unless required by applicable law or agreed to in writing, software
12distributed under the License is distributed on an "AS IS" BASIS,
13WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14See the License for the specific language governing permissions and
15limitations under the License.
16
17*/
18
19#ifndef MTCORE_CALENDARS_TIME_HPP
20#define MTCORE_CALENDARS_TIME_HPP
21
22#include "mtcore.hpp"
23
24namespace mtcore::calendars {
33
35 constexpr i32 NANOS_PER_SECOND = 1e9;
37 constexpr i64 NANOS_PER_DAY = static_cast<i64>(NANOS_PER_SECOND) * 60 * 60 * 24;
38
40 struct NanoSeconds;
42 struct DayFraction;
43
45 struct Segments {
46 u8 hour = 0;
49 u32 nano = 0;
50
52 [[nodiscard]] constexpr Result<void, TimeValidationError> try_validate() const {
53 if (hour >= 24) {
55 }
56
57 if (minute >= 60) {
59 }
60
61 if (second >= 60) {
63 }
64
65 if (nano >= NANOS_PER_SECOND) {
67 }
68
69 return success();
70 }
71
72 [[nodiscard]] explicit constexpr operator NanoSeconds() const;
73 [[nodiscard]] explicit constexpr operator DayFraction() const;
74
75 [[nodiscard]] constexpr auto operator<=>(const Segments &o) const {
76 if (hour != o.hour) {
77 return hour <=> o.hour;
78 }
79 if (minute != o.minute) {
80 return minute <=> o.minute;
81 }
82 if (second != o.second) {
83 return second <=> o.second;
84 }
85 if (nano != o.nano) {
86 return nano <=> o.nano;
87 }
88 return std::strong_ordering::equal;
89 }
90
91 [[nodiscard]] bool operator==(const Segments &o) const { return (*this <=> o) == std::strong_ordering::equal; }
92 [[nodiscard]] bool operator!=(const Segments &o) const { return (*this <=> o) != std::strong_ordering::equal; }
93 [[nodiscard]] bool operator<(const Segments &o) const { return (*this <=> o) == std::strong_ordering::less; }
94 [[nodiscard]] bool operator>(const Segments &o) const { return (*this <=> o) == std::strong_ordering::greater; }
95 [[nodiscard]] bool operator<=(const Segments &o) const {
96 const auto cmp = (*this <=> o);
97 return cmp == std::strong_ordering::equal || cmp == std::strong_ordering::less;
98 }
99 [[nodiscard]] bool operator>=(const Segments &o) const {
100 const auto cmp = (*this <=> o);
101 return cmp == std::strong_ordering::equal || cmp == std::strong_ordering::greater;
102 }
103
106 [[nodiscard]] constexpr f64 to_fraction_unchecked() const;
107 };
108
109 struct DayFraction {
111
113 [[nodiscard]] constexpr Result<void, TimeValidationError> try_validate() const {
114 if (!std::isfinite(frac)) {
116 }
117 if (frac < 0) {
119 }
120 if (frac >= 1) {
122 }
123 return success();
124 }
125
126 [[nodiscard]] explicit constexpr operator NanoSeconds() const;
127 [[nodiscard]] explicit constexpr operator Segments() const;
128
129 [[nodiscard]] constexpr auto operator<=>(const DayFraction &o) const { return frac <=> o.frac; }
130
131 [[nodiscard]] bool operator==(const DayFraction &o) const {
132 return (*this <=> o) == std::weak_ordering::equivalent;
133 }
134 [[nodiscard]] bool operator!=(const DayFraction &o) const {
135 return (*this <=> o) != std::weak_ordering::equivalent;
136 }
137 [[nodiscard]] bool operator<(const DayFraction &o) const { return (*this <=> o) == std::weak_ordering::less; }
138 [[nodiscard]] bool operator>(const DayFraction &o) const { return (*this <=> o) == std::weak_ordering::greater; }
139 [[nodiscard]] bool operator<=(const DayFraction &o) const {
140 const auto cmp = (*this <=> o);
141 return cmp == std::weak_ordering::equivalent || cmp == std::weak_ordering::less;
142 }
143 [[nodiscard]] bool operator>=(const DayFraction &o) const {
144 const auto cmp = (*this <=> o);
145 return cmp == std::weak_ordering::equivalent || cmp == std::weak_ordering::greater;
146 }
147 };
148
149 struct NanoSeconds {
150 static constexpr auto max = NANOS_PER_DAY;
152
154 [[nodiscard]] constexpr Result<void, TimeValidationError> try_validate() const {
155 if (nano >= max) {
157 }
158 return success();
159 }
160
161 [[nodiscard]] explicit constexpr operator DayFraction() const;
162 [[nodiscard]] explicit constexpr operator Segments() const;
163
164 [[nodiscard]] constexpr auto operator<=>(const NanoSeconds &o) const { return nano <=> o.nano; }
165
166 [[nodiscard]] bool operator==(const NanoSeconds &o) const { return (*this <=> o) == std::strong_ordering::equal; }
167 [[nodiscard]] bool operator!=(const NanoSeconds &o) const { return (*this <=> o) != std::strong_ordering::equal; }
168 [[nodiscard]] bool operator<(const NanoSeconds &o) const { return (*this <=> o) == std::strong_ordering::less; }
169 [[nodiscard]] bool operator>(const NanoSeconds &o) const { return (*this <=> o) == std::strong_ordering::greater; }
170 [[nodiscard]] bool operator<=(const NanoSeconds &o) const {
171 const auto cmp = (*this <=> o);
172 return cmp == std::strong_ordering::equal || cmp == std::strong_ordering::less;
173 }
174 [[nodiscard]] bool operator>=(const NanoSeconds &o) const {
175 const auto cmp = (*this <=> o);
176 return cmp == std::strong_ordering::equal || cmp == std::strong_ordering::greater;
177 }
178 };
179
180 constexpr Segments::operator NanoSeconds() const {
181 ensure(try_validate().is_success());
182 const auto hours = static_cast<u64>(this->hour);
183 const auto minutes = 60 * hours + static_cast<u64>(this->minute);
184 const auto seconds = 60 * minutes + static_cast<u64>(this->second);
185 const auto nano = seconds * NANOS_PER_SECOND + static_cast<u64>(this->nano);
186 const auto res = NanoSeconds{.nano = nano};
187 ensure(res.try_validate().is_success());
188 return res;
189 }
190 constexpr Segments::operator DayFraction() const {
191 const auto nanos = static_cast<NanoSeconds>(*this);
192 return static_cast<DayFraction>(nanos);
193 }
194
196 const auto hours = static_cast<u64>(this->hour);
197 const auto minutes = 60 * hours + static_cast<u64>(this->minute);
198 const auto seconds = 60 * minutes + static_cast<u64>(this->second);
199 const auto nano = seconds * NANOS_PER_SECOND + static_cast<u64>(this->nano);
200 const auto nanoFloat = static_cast<f80>(nano);
201 const auto frac = nanoFloat / static_cast<f80>(NANOS_PER_DAY);
202 return static_cast<f64>(frac);
203 }
204
205 constexpr DayFraction::operator NanoSeconds() const {
206 ensure(try_validate().is_success());
207 const auto nanoSeconds = math::floor<f64, u64>(frac * NANOS_PER_DAY);
208 const auto res = NanoSeconds{.nano = nanoSeconds};
209 ensure(res.try_validate().is_success());
210 return res;
211 }
212
213 constexpr DayFraction::operator Segments() const {
214 const auto nanos = static_cast<NanoSeconds>(*this);
215 return static_cast<Segments>(nanos);
216 }
217
218 constexpr NanoSeconds::operator DayFraction() const {
219 ensure(try_validate().is_success());
220 const auto nano = static_cast<f80>(this->nano);
221 const auto res = DayFraction{.frac = static_cast<f64>(nano) / static_cast<f64>(NANOS_PER_DAY)};
222 ensure(res.try_validate().is_success());
223 return res;
224 }
225
226 constexpr NanoSeconds::operator Segments() const {
227 ensure(try_validate().is_success());
228
229 u64 val = this->nano;
230
233 val /= NANOS_PER_SECOND;
234
235 const auto seconds = math::mod<u64, u64, u8>(val, 60);
236 ensure(seconds < 60);
237 val /= 60;
238
239 const auto minutes = math::mod<u64, u64, u8>(val, 60);
240 ensure(minutes < 60);
241 val /= 60;
242
243 ensure(val < 24);
244 const auto res = Segments{
245 .hour = static_cast<u8>(val),
246 .minute = minutes,
247 .second = seconds,
248 .nano = nano,
249 };
250
251 ensure(res.try_validate().is_success());
252 return res;
253 }
254} // namespace mtcore::calendars
255
256namespace mtcore::io {
257 template<>
258 struct Formatter<calendars::NanoSeconds> {
259 template<WriterImpl WI>
261 const calendars::NanoSeconds &ns) {
262 return io::print(writer, "{d}ns", ns.nano);
263 }
264 };
265
266 template<>
267 struct Formatter<calendars::DayFraction> {
268 template<WriterImpl WI>
270 const calendars::DayFraction &df) {
271 return io::print(writer, "{.3}%(day)", df.frac * 100.);
272 }
273 };
274
275 template<>
276 struct Formatter<calendars::Segments> {
277 template<WriterImpl WI>
279 const calendars::Segments &seg) {
280 if (opts.formatStr.empty()) {
281 return io::print(writer, "{0<2;}:{0<2;}:{0<2;}.{0<9;}", seg.hour, seg.minute, seg.second, seg.nano);
282 }
283
284 size_t written = 0;
285 constexpr auto chars = ":. ";
286 auto splits = slices::split_one_of(slice_from(chars), opts.formatStr);
288
289#define MTCORE_CAL_TIME_SEG_FMT_TRY_WRITE(FMTSTR, VAL) \
290 if (auto r = io::print(writer, (FMTSTR), (VAL)); r.is_error()) { \
291 return r.error(); \
292 } \
293 else { \
294 written += r.value(); \
295 }
296
297 while (splits.next().copy_if_present(cur)) {
298 const auto [sub, sep] = cur;
299 if (sep.has_value()) {
300 if (auto r = writer.write(sep.value()); r.is_error()) {
301 return r.error();
302 }
303 }
304
305 if (sub.empty()) {
306 continue;
307 }
308
309 if (str_equal(sub, "hh")) {
311 }
312 else if (str_equal(sub, "h")) {
314 }
315 else if (str_equal(sub, "HH")) {
317 }
318 else if (str_equal(sub, "H")) {
320 }
321 else if (str_equal(sub, "kk")) {
323 }
324 else if (str_equal(sub, "k")) {
326 }
327 else if (str_equal(sub, "KK")) {
328 MTCORE_CAL_TIME_SEG_FMT_TRY_WRITE("{0<2;}", seg.hour + 1);
329 }
330 else if (str_equal(sub, "K")) {
332 }
333 else if (str_equal(sub, "A") || str_equal(sub, "AA")) {
334 MTCORE_CAL_TIME_SEG_FMT_TRY_WRITE("{}", seg.hour < 12 ? "AM" : "PM");
335 }
336 else if (str_equal(sub, "a") || str_equal(sub, "aa")) {
337 MTCORE_CAL_TIME_SEG_FMT_TRY_WRITE("{}", seg.hour < 12 ? "am" : "pm");
338 }
339 else if (str_equal(sub, "aaa")) {
340 MTCORE_CAL_TIME_SEG_FMT_TRY_WRITE("{}", seg.hour < 12 ? "a.m." : "p.m.");
341 }
342 else if (str_equal(sub, "AAA")) {
343 MTCORE_CAL_TIME_SEG_FMT_TRY_WRITE("{}", seg.hour < 12 ? "A.M." : "P.M.");
344 }
345 else if (str_equal(sub, "aaaa")) {
346 MTCORE_CAL_TIME_SEG_FMT_TRY_WRITE("{}", seg.hour < 12 ? "a" : "p");
347 }
348 else if (str_equal(sub, "AAAA")) {
349 MTCORE_CAL_TIME_SEG_FMT_TRY_WRITE("{}", seg.hour < 12 ? "A" : "P");
350 }
351 else if (str_equal(sub, "mm")) {
353 }
354 else if (str_equal(sub, "m")) {
356 }
357 else if (str_equal(sub, "ss")) {
359 }
360 else if (str_equal(sub, "s")) {
362 }
363 else if (str_equal(sub, "S")) {
365 }
366 else if (str_equal(sub, "SS")) {
368 }
369 else if (str_equal(sub, "SSS")) {
371 }
372 else if (str_equal(sub, "SSSS")) {
374 }
375 else if (str_equal(sub, "SSSSS")) {
377 }
378 else if (str_equal(sub, "SSSSSS")) {
380 }
381 else if (str_equal(sub, "SSSSSSS")) {
383 }
384 else if (str_equal(sub, "SSSSSSSS")) {
386 }
387 else if (str_equal(sub, "SSSSSSSSS")) {
389 }
390 else {
392 }
393 }
394
395 return success(written);
396 }
397 }; // namespace mtcore::io
398} // namespace mtcore::io
399
400#endif // MTCORE_CALENDARS_TIME_HPP
#define MTCORE_CAL_TIME_SEG_FMT_TRY_WRITE(FMTSTR, VAL)
constexpr i64 NANOS_PER_DAY
Number of nanoseconds per day.
TimeValidationError
Common try_validate errors for time.
constexpr i32 NANOS_PER_SECOND
Number of nanoseconds per second.
SplitOneOfIter< T > split_one_of(const Slice< std::add_const_t< T > > &needles, const Slice< T > &haystack)
Splits a slice into smaller sub slices.
constexpr Slice< const char32_t > slice_from(char32_t *cstr)
Creates a slice from a utf32 string in the form of a c string.
Result< size_t, typename Writer< WI >::ErrType > print(Writer< WI > &writer, const char *fmt, const Args &...args)
Prints arguments using a format string Element insert points are designated by a pair of curly braces...
#define ensure(check,...)
Ensures that a check holds true, aborts the program if not true Will print error if the condition is ...
constexpr R floor(T num) noexcept
Floors a number with support for constexpr and fast runtime compilation.
constexpr Res mod(L left, R right) noexcept
Calculates the mathematical mod of two numbers.
constexpr auto amod(L x, R y) noexcept -> Res
x mod [1..y]
Success< T > success(const T &v)
Creates a successful Result.
Definition result.hpp:389
Success< void > success()
Creates a successful void Result object.
Definition result.hpp:398
Error< Underlying > error(Underlying err)
Creates an error.
Definition result.hpp:425
uint64_t u64
Alias for 64-bit unsigned ints.
int32_t i32
Alias for 32-bit ints.
int64_t i64
Alias for 64-bit ints.
uint8_t u8
Alias for 8-bit unsigned ints.
double f64
Alias for 64-bit floats.
long double f80
Alias for 80-bit floats.
uint32_t u32
Alias for 32-bit unsigned ints.
Namespace for calendaring systems.
bool str_equal(const L &left, const R &right)
Compares two string strings for ordering.
Represents a value that may or may not exist (an "Optional" value) Similar concept to std::optional,...
Definition optional.hpp:235
Represents a pair of elements Useful to avoid having to have many micro structs.
Definition pair.hpp:31
Represents a Result that may have an error (error code) or a success value A type of "void" means the...
Definition result.hpp:170
constexpr bool empty() const noexcept
Checks if a Slice is empty.
bool operator!=(const DayFraction &o) const
constexpr auto operator<=>(const DayFraction &o) const
constexpr Result< void, TimeValidationError > try_validate() const
Tries to validate.
bool operator>=(const DayFraction &o) const
bool operator==(const DayFraction &o) const
bool operator<=(const DayFraction &o) const
bool operator<=(const NanoSeconds &o) const
bool operator==(const NanoSeconds &o) const
constexpr Result< void, TimeValidationError > try_validate() const
Tries to validate.
bool operator!=(const NanoSeconds &o) const
bool operator>=(const NanoSeconds &o) const
constexpr auto operator<=>(const NanoSeconds &o) const
Represents time in segments (hour, minute, etc.).
constexpr f64 to_fraction_unchecked() const
Converts to a day fraction without checking if it's valid.
constexpr Result< void, TimeValidationError > try_validate() const
Tries to validate.
constexpr auto operator<=>(const Segments &o) const
Options for specifying formatting.
Definition format.hpp:36
Slice< const char > formatStr
Definition format.hpp:37
static Result< size_t, typename Writer< WI >::ErrType > fmt(Writer< WI > &writer, const FormatOptions &, const calendars::DayFraction &df)
static Result< size_t, typename Writer< WI >::ErrType > fmt(Writer< WI > &writer, const FormatOptions &, const calendars::NanoSeconds &ns)
static Result< size_t, typename Writer< WI >::ErrType > fmt(Writer< WI > &writer, const FormatOptions &opts, const calendars::Segments &seg)
Struct to override to specify how a type should be formatted.
Definition format.hpp:45
A writer that writes data to some sort of stream or buffer Note: the data elements written should be ...
Definition io/writer.hpp:51