stdex Additional custom or not Standard C++ covered algorithms

Loading...
Searching...
No Matches
chrono.hpp
1/*
2 SPDX-License-Identifier: MIT
3 Copyright © 2023-2024 Amebis
4*/
5
6#pragma once
7
8#include "compat.hpp"
9#include "math.hpp"
10#include "system.hpp"
11#include "locale.hpp"
12#include <stdint.h>
13#include <chrono>
14#include <ctime>
15#include <memory>
16#include <stdexcept>
17
18 namespace stdex {
19 namespace chrono
20 {
24 struct aosn_date
25 {
26 using rep = int32_t;
27 using period = std::ratio<86400>; // 1 day
28 using duration = std::chrono::duration<rep, period>;
29 using time_point = std::chrono::time_point<aosn_date>;
30 static constexpr bool is_steady = false;
31
35 static time_point now() noexcept
36 {
37#ifdef _WIN32
38 FILETIME t;
39 GetSystemTimeAsFileTime(&t);
40 return from_system(t);
41#else
42 time_t t;
43 time(&t);
44 return from_time_t(t);
45#endif
46 }
47
51 static std::time_t to_time_t(_In_ const time_point tp) noexcept
52 {
53 return static_cast<std::time_t>(tp.time_since_epoch().count()) * 86400 - 210866803200;
54 }
55
59 static time_point from_time_t(_In_ std::time_t t) noexcept
60 {
61 return time_point(duration(static_cast<rep>((t + 210866803200) / 86400)));
62 }
63
64#ifdef _WIN32
68 static time_point from_system(_In_ const SYSTEMTIME& t) noexcept
69 {
70 return from_dmy(static_cast<uint8_t>(t.wDay), static_cast<uint8_t>(t.wMonth), static_cast<int32_t>(t.wYear));
71 }
72
76 static time_point from_system(_In_ const FILETIME& t) noexcept
77 {
78 uint64_t x = ((static_cast<uint64_t>(t.dwHighDateTime)) << 32) | t.dwLowDateTime;
79 return time_point(duration(static_cast<rep>(x / 864000000000 + 2305814))); // Convert from 100 ns to 1-day interval and adjust epoch
80 }
81
85 static time_point from_system(_In_ DATE t)
86 {
87 SYSTEMTIME st;
88 if (!VariantTimeToSystemTime(t, &st))
89 throw std::invalid_argument("failed to convert date from VARIANT_DATE");
90 return from_system(st);
91 }
92#else
96 static time_point from_system(_In_ const struct timespec& t) noexcept
97 {
98 return from_time_t(t.tv_sec);
99 }
100#endif
101
105 static time_point from_dmy(_In_ uint8_t day, _In_ uint8_t month, _In_ int32_t year) noexcept
106 {
107 int32_t mtmp, ytmp;
108 if (month > 2) {
109 mtmp = month - 3;
110 ytmp = year;
111 }
112 else {
113 mtmp = month + 9;
114 ytmp = year - 1;
115 }
116 int32_t ctmp = (ytmp / 100);
117 int32_t dtmp = ytmp - (100 * ctmp);
118 int32_t result1 = 146097L * ctmp / 4;
119 int32_t result2 = (1461 * dtmp) / 4;
120 int32_t result3 = (153 * mtmp + 2) / 5;
121 return time_point(duration(static_cast<int32_t>(result1) + day + result2 + 1721119L + result3));
122 }
123
127 static void to_dmy(_In_ const time_point tp, _Out_opt_ uint8_t* day, _Out_opt_ uint8_t* month, _Out_opt_ int32_t* year) noexcept
128 {
129 int32_t mtmp = tp.time_since_epoch().count() - 1721119L;
130 int32_t yr = (4 * mtmp - 1) / 146097L;
131 mtmp = 4 * mtmp - 1 - 146097L * yr;
132 int32_t da = mtmp / 4;
133 mtmp = (4 * da + 3) / 1461;
134 da = 4 * da + 3 - 1461 * mtmp;
135 da = (da + 4) / 4;
136 int32_t mo = (5 * da - 3) / 153;
137 da = 5 * da - 3 - 153 * mo;
138 da = (da + 5) / 5;
139 yr = 100 * yr + mtmp;
140 if (mo < 10)
141 mo += 3;
142 else {
143 mo -= 9;
144 yr++;
145 }
146 if (day) *day = static_cast<uint8_t>(da);
147 if (month) *month = static_cast<uint8_t>(mo);
148 if (year) *year = yr;
149 }
150
154 static uint8_t day_of_week(_In_ const time_point tp)
155 {
156 return static_cast<uint8_t>(tp.time_since_epoch().count() % 7);
157 }
158
162 static uint8_t day_of_week(_In_ uint8_t day, _In_ uint8_t month, _In_ int32_t year)
163 {
164 return static_cast<uint8_t>(from_dmy(day, month, year).time_since_epoch().count() % 7);
165 }
166 };
167
171 struct aosn_timestamp
172 {
173 using rep = int64_t;
174 using period = std::milli;
175 using duration = std::chrono::duration<rep, period>;
176 using time_point = std::chrono::time_point<aosn_timestamp>;
177 static constexpr bool is_steady = false;
178
179 static constexpr rep f_second = 1000; // number of milliseconds per second
180 static constexpr rep f_minute = 60; // number of seconds per minute
181 static constexpr rep f_hour = 60; // number of minutes per hour
182 static constexpr rep f_day = 24; // number of hours per day
183 static constexpr rep f_week = 7; // number of days per week
184
185 static constexpr rep one_second = f_second; // number of milliseconds per second
186 static constexpr rep one_minute = f_minute * one_second; // number of milliseconds per minute
187 static constexpr rep one_hour = f_hour * one_minute; // number of milliseconds per hour
188 static constexpr rep one_day = f_day * one_hour; // number of milliseconds per day
189 static constexpr rep one_week = f_week * one_day; // number of milliseconds per week
190
194 static time_point now() noexcept
195 {
196#ifdef _WIN32
197 FILETIME t;
198 GetSystemTimeAsFileTime(&t);
199 return from_system(t);
200#else
201 time_t t;
202 time(&t);
203 return from_time_t(t);
204#endif
205 }
206
210 static std::time_t to_time_t(_In_ const time_point tp) noexcept
211 {
212 return tp.time_since_epoch().count() / one_second - 210866803200;
213 }
214
218 static time_point from_time_t(_In_ std::time_t t) noexcept
219 {
220 return time_point(duration((static_cast<rep>(t) + 210866803200) * one_second));
221 }
222
223#ifdef _WIN32
227 static time_point from_system(_In_ const SYSTEMTIME& t) noexcept
228 {
229 return from_dmy(
230 static_cast<uint8_t>(t.wDay), static_cast<uint8_t>(t.wMonth), static_cast<int32_t>(t.wYear),
231 static_cast<uint8_t>(t.wHour), static_cast<uint8_t>(t.wMinute), static_cast<uint8_t>(t.wSecond), static_cast<uint16_t>(t.wMilliseconds));
232 }
233
237 static time_point from_system(_In_ const FILETIME& t) noexcept
238 {
239 uint64_t x = ((static_cast<uint64_t>(t.dwHighDateTime)) << 32) | t.dwLowDateTime;
240 return time_point(duration(static_cast<rep>(x / 10000 + 199222329600000))); // Convert from 100 ns to 1 ms interval and adjust epoch
241 }
242
246 static time_point from_system(_In_ DATE t)
247 {
248 SYSTEMTIME st;
249 if (!VariantTimeToSystemTime(t, &st))
250 throw std::invalid_argument("failed to convert date from VARIANT_DATE");
251 return from_system(st);
252 }
253#else
257 static time_point from_system(_In_ const struct timespec& t) noexcept
258 {
259 return time_point(duration(static_cast<rep>(from_time_t(t.tv_sec).time_since_epoch().count() + t.tv_nsec / 1000)));
260 }
261#endif
262
263 static void to_system(_In_ time_point tp, _Out_ struct tm& date) noexcept
264 {
265 uint8_t day, month, hour, minute, second;
266 uint16_t millisecond;
267 int32_t year;
268 to_dmy(tp, &day, &month, &year, &hour, &minute, &second, &millisecond);
269 date.tm_sec = second;
270 date.tm_min = minute;
271 date.tm_hour = hour;
272 date.tm_mday = day;
273 date.tm_mon = month - 1;
274 date.tm_year = year - 1900;
275 date.tm_wday = (static_cast<int>(aosn_date::day_of_week(to_date(tp))) + 1) % 7;
276 date.tm_yday = 0;
277 date.tm_isdst = 0;
278 }
279
283 static aosn_date::time_point to_date(_In_ time_point tp) noexcept
284 {
285 return aosn_date::time_point(aosn_date::duration(static_cast<aosn_date::rep>(tp.time_since_epoch().count() / one_day)));
286 }
287
291 static time_point from_date(_In_ aosn_date::time_point date) noexcept
292 {
293 return time_point(duration(static_cast<rep>(date.time_since_epoch().count()) * one_day));
294 }
295
299 static time_point from_dmy(
300 _In_ uint8_t day, _In_ uint8_t month, _In_ int32_t year,
301 _In_ uint8_t hour, _In_ uint8_t minute, _In_ uint8_t second, _In_ uint16_t millisecond) noexcept
302 {
303 return time_point(duration(
304 (static_cast<rep>(aosn_date::from_dmy(day, month, year).time_since_epoch().count()) * one_day) +
305 (static_cast<rep>(hour) * one_hour + static_cast<rep>(minute) * one_minute + static_cast<rep>(second) * one_second + millisecond)));
306 }
307
311 static void to_dmy(_In_ const time_point tp,
312 _Out_opt_ uint8_t* day, _Out_opt_ uint8_t* month, _Out_opt_ int32_t* year,
313 _Out_opt_ uint8_t* hour, _Out_opt_ uint8_t* minute, _Out_opt_ uint8_t* second, _Out_opt_ uint16_t* millisecond) noexcept
314 {
315 aosn_date::to_dmy(to_date(tp), day, month, year);
316 int32_t u = static_cast<int32_t>(tp.time_since_epoch().count() % one_day);
317 if (millisecond) *millisecond = static_cast<uint16_t>(u % f_second);
318 u = u / f_second;
319 if (second) *second = static_cast<uint8_t>(u % f_minute);
320 u = u / f_minute;
321 if (minute) *minute = static_cast<uint8_t>(u % f_hour);
322 u = u / f_hour;
323 if (hour) *hour = static_cast<uint8_t>(u);
324 }
325
326 template<class TR = std::char_traits<char>, class AX = std::allocator<char>>
327 static std::basic_string<char, TR, AX> to_str(_In_ const time_point tp, _In_z_ const char* format, _In_opt_ locale_t locale)
328 {
329 struct tm date;
330 to_system(tp, date);
331 std::basic_string<char, TR, AX> str;
332 char stack_buffer[1024 / sizeof(char)];
333 size_t n;
334#if _WIN32
335 n = _strftime_l(stack_buffer, _countof(stack_buffer), format, &date, locale);
336#else
337 n = strftime_l(stack_buffer, _countof(stack_buffer), format, &date, locale);
338#endif
339 if (n) {
340 str.assign(stack_buffer, stack_buffer + n);
341 return str;
342 }
343 size_t num_chars = stdex::mul(_countof(stack_buffer), 2);
344 for (;;) {
345 std::unique_ptr<char> buf(new char[num_chars]);
346#if _WIN32
347 n = _strftime_l(buf.get(), num_chars, format, &date, locale);
348#else
349 n = strftime_l(buf.get(), num_chars, format, &date, locale);
350#endif
351 if (n) {
352 str.assign(buf.get(), buf.get() + n);
353 return str;
354 }
355 num_chars = stdex::mul(num_chars, 2);
356 }
357 }
358
359 template<class TR = std::char_traits<wchar_t>, class AX = std::allocator<wchar_t>>
360 static std::basic_string<wchar_t, TR, AX> to_str(_In_ const time_point tp, _In_z_ const wchar_t* format, _In_opt_ locale_t locale)
361 {
362 struct tm date;
363 to_system(tp, date);
364 std::basic_string<wchar_t, TR, AX> str;
365 wchar_t stack_buffer[1024 / sizeof(wchar_t)];
366 size_t n;
367#if _WIN32
368 n = _wcsftime_l(stack_buffer, _countof(stack_buffer), format, &date, locale);
369#else
370 n = wcsftime_l(stack_buffer, _countof(stack_buffer), format, &date, locale);
371#endif
372 if (n) {
373 str.assign(stack_buffer, stack_buffer + n);
374 return str;
375 }
376 size_t num_chars = stdex::mul(_countof(stack_buffer), 2);
377 for (;;) {
378 std::unique_ptr<wchar_t> buf(new wchar_t[num_chars]);
379#if _WIN32
380 n = _wcsftime_l(buf.get(), num_chars, format, &date, locale);
381#else
382 n = wcsftime_l(buf.get(), num_chars, format, &date, locale);
383#endif
384 if (n) {
385 str.assign(buf.get(), buf.get() + n);
386 return str;
387 }
388 num_chars = stdex::mul(num_chars, 2);
389 }
390 }
391
392 template<class TR = std::char_traits<char>, class AX = std::allocator<char>>
393 static std::basic_string<char, TR, AX> to_rfc822(_In_ const time_point tp)
394 {
395 return to_str(tp, "%a, %d %b %Y %H:%M:%S GMT", stdex::locale_C);
396 }
397 };
398 }
399}
stdex::locale
locale_t helper class to free_locale when going out of scope.
Definition locale.hpp:74
stdex::chrono::aosn_date
AOsn date.
Definition chrono.hpp:25
stdex::chrono::aosn_date::from_dmy
static time_point from_dmy(uint8_t day, uint8_t month, int32_t year) noexcept
Returns time point from calendar day, month and year.
Definition chrono.hpp:105
stdex::chrono::aosn_date::to_dmy
static void to_dmy(const time_point tp, uint8_t *day, uint8_t *month, int32_t *year) noexcept
Returns calendar day, month and year from time point.
Definition chrono.hpp:127
stdex::chrono::aosn_date::day_of_week
static uint8_t day_of_week(const time_point tp)
Returns day-of-week from time point (0 = Mon, 1 = Tue...)
Definition chrono.hpp:154
stdex::chrono::aosn_date::day_of_week
static uint8_t day_of_week(uint8_t day, uint8_t month, int32_t year)
Returns day-of-week from calendar day, month and year (0 = Mon, 1 = Tue...)
Definition chrono.hpp:162
stdex::chrono::aosn_date::to_time_t
static std::time_t to_time_t(const time_point tp) noexcept
Returns time_t from time point.
Definition chrono.hpp:51
stdex::chrono::aosn_date::from_system
static time_point from_system(const struct timespec &t) noexcept
Returns time point from struct timespec.
Definition chrono.hpp:96
stdex::chrono::aosn_date::now
static time_point now() noexcept
Gets current date.
Definition chrono.hpp:35
stdex::chrono::aosn_date::from_time_t
static time_point from_time_t(std::time_t t) noexcept
Returns time point from time_t.
Definition chrono.hpp:59
stdex::chrono::aosn_timestamp
AOsn timestamp.
Definition chrono.hpp:172
stdex::chrono::aosn_timestamp::from_date
static time_point from_date(aosn_date::time_point date) noexcept
Returns time point from aosn_date::time_point.
Definition chrono.hpp:291
stdex::chrono::aosn_timestamp::from_time_t
static time_point from_time_t(std::time_t t) noexcept
Returns time point from time_t.
Definition chrono.hpp:218
stdex::chrono::aosn_timestamp::from_dmy
static time_point from_dmy(uint8_t day, uint8_t month, int32_t year, uint8_t hour, uint8_t minute, uint8_t second, uint16_t millisecond) noexcept
Returns time point from calendar day, month, year and time.
Definition chrono.hpp:299
stdex::chrono::aosn_timestamp::to_time_t
static std::time_t to_time_t(const time_point tp) noexcept
Returns time_t from time point.
Definition chrono.hpp:210
stdex::chrono::aosn_timestamp::to_date
static aosn_date::time_point to_date(time_point tp) noexcept
Returns aosn_date::time_point from time point.
Definition chrono.hpp:283
stdex::chrono::aosn_timestamp::from_system
static time_point from_system(const struct timespec &t) noexcept
Returns time point from struct timespec.
Definition chrono.hpp:257
stdex::chrono::aosn_timestamp::now
static time_point now() noexcept
Gets current timestamp.
Definition chrono.hpp:194
stdex::chrono::aosn_timestamp::to_dmy
static void to_dmy(const time_point tp, uint8_t *day, uint8_t *month, int32_t *year, uint8_t *hour, uint8_t *minute, uint8_t *second, uint16_t *millisecond) noexcept
Returns calendar day, month, year and time from time point.
Definition chrono.hpp:311