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
102#ifdef _WIN32
106 static void to_system(_In_ time_point tp, _Out_ SYSTEMTIME& t)
107 {
108 uint8_t day, month;
109 int32_t year;
110 to_dmy(tp, &day, &month, &year);
111 t.wDay = day;
112 t.wMonth = month;
113 if (year > WORD_MAX) _Unlikely_
114 throw std::range_error("year too big");
115 t.wYear = static_cast<WORD>(year);
116 t.wDayOfWeek = static_cast<int>(day_of_week(tp) + 1) % 7;
117 t.wHour = 0;
118 t.wMinute = 0;
119 t.wSecond = 0;
120 t.wMilliseconds = 0;
121 }
122
126 static void to_system(_In_ time_point tp, _Out_ FILETIME& t) noexcept
127 {
128 uint64_t x = (tp.time_since_epoch().count() - 2305814) * 864000000000; // Adjust epoch and convert 1-day interval to 100 ns
129 t.dwHighDateTime = static_cast<DWORD>(x >> 32);
130 t.dwLowDateTime = static_cast<DWORD>(x & 0xffffffff);
131 }
132
136 static void to_system(_In_ time_point tp, _Out_ DATE& t)
137 {
138 SYSTEMTIME st;
139 to_system(tp, st);
140 if (!SystemTimeToVariantTime(&st, &t))
141 throw std::invalid_argument("failed to convert date to VARIANT_DATE");
142 }
143#endif
144
148 static time_point from_dmy(_In_ uint8_t day, _In_ uint8_t month, _In_ int32_t year) noexcept
149 {
150 int32_t mtmp, ytmp;
151 if (month > 2) {
152 mtmp = month - 3;
153 ytmp = year;
154 }
155 else {
156 mtmp = month + 9;
157 ytmp = year - 1;
158 }
159 int32_t ctmp = (ytmp / 100);
160 int32_t dtmp = ytmp - (100 * ctmp);
161 int32_t result1 = 146097L * ctmp / 4;
162 int32_t result2 = (1461 * dtmp) / 4;
163 int32_t result3 = (153 * mtmp + 2) / 5;
164 return time_point(duration(static_cast<int32_t>(result1) + day + result2 + 1721119L + result3));
165 }
166
170 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
171 {
172 int32_t mtmp = tp.time_since_epoch().count() - 1721119L;
173 int32_t yr = (4 * mtmp - 1) / 146097L;
174 mtmp = 4 * mtmp - 1 - 146097L * yr;
175 int32_t da = mtmp / 4;
176 mtmp = (4 * da + 3) / 1461;
177 da = 4 * da + 3 - 1461 * mtmp;
178 da = (da + 4) / 4;
179 int32_t mo = (5 * da - 3) / 153;
180 da = 5 * da - 3 - 153 * mo;
181 da = (da + 5) / 5;
182 yr = 100 * yr + mtmp;
183 if (mo < 10)
184 mo += 3;
185 else {
186 mo -= 9;
187 yr++;
188 }
189 if (day) *day = static_cast<uint8_t>(da);
190 if (month) *month = static_cast<uint8_t>(mo);
191 if (year) *year = yr;
192 }
193
197 static uint8_t day_of_week(_In_ const time_point tp)
198 {
199 return static_cast<uint8_t>(tp.time_since_epoch().count() % 7);
200 }
201
205 static uint8_t day_of_week(_In_ uint8_t day, _In_ uint8_t month, _In_ int32_t year)
206 {
207 return static_cast<uint8_t>(from_dmy(day, month, year).time_since_epoch().count() % 7);
208 }
209 };
210
214 struct aosn_timestamp
215 {
216 using rep = int64_t;
217 using period = std::milli;
218 using duration = std::chrono::duration<rep, period>;
219 using time_point = std::chrono::time_point<aosn_timestamp>;
220 static constexpr bool is_steady = false;
221
222 static constexpr rep f_second = 1000; // number of milliseconds per second
223 static constexpr rep f_minute = 60; // number of seconds per minute
224 static constexpr rep f_hour = 60; // number of minutes per hour
225 static constexpr rep f_day = 24; // number of hours per day
226 static constexpr rep f_week = 7; // number of days per week
227
228 static constexpr rep one_second = f_second; // number of milliseconds per second
229 static constexpr rep one_minute = f_minute * one_second; // number of milliseconds per minute
230 static constexpr rep one_hour = f_hour * one_minute; // number of milliseconds per hour
231 static constexpr rep one_day = f_day * one_hour; // number of milliseconds per day
232 static constexpr rep one_week = f_week * one_day; // number of milliseconds per week
233
237 static time_point now() noexcept
238 {
239#ifdef _WIN32
240 FILETIME t;
241 GetSystemTimeAsFileTime(&t);
242 return from_system(t);
243#else
244 time_t t;
245 time(&t);
246 return from_time_t(t);
247#endif
248 }
249
253 static std::time_t to_time_t(_In_ const time_point tp) noexcept
254 {
255 return tp.time_since_epoch().count() / one_second - 210866803200;
256 }
257
261 static time_point from_time_t(_In_ std::time_t t) noexcept
262 {
263 return time_point(duration((static_cast<rep>(t) + 210866803200) * one_second));
264 }
265
266#ifdef _WIN32
270 static time_point from_system(_In_ const SYSTEMTIME& t) noexcept
271 {
272 return from_dmy(
273 static_cast<uint8_t>(t.wDay), static_cast<uint8_t>(t.wMonth), static_cast<int32_t>(t.wYear),
274 static_cast<uint8_t>(t.wHour), static_cast<uint8_t>(t.wMinute), static_cast<uint8_t>(t.wSecond), static_cast<uint16_t>(t.wMilliseconds));
275 }
276
280 static time_point from_system(_In_ const FILETIME& t) noexcept
281 {
282 uint64_t x = ((static_cast<uint64_t>(t.dwHighDateTime)) << 32) | t.dwLowDateTime;
283 return time_point(duration(static_cast<rep>(x / 10000 + 199222329600000))); // Convert from 100 ns to 1 ms interval and adjust epoch
284 }
285
289 static time_point from_system(_In_ DATE t)
290 {
291 SYSTEMTIME st;
292 if (!VariantTimeToSystemTime(t, &st))
293 throw std::invalid_argument("failed to convert date from VARIANT_DATE");
294 return from_system(st);
295 }
296#else
300 static time_point from_system(_In_ const struct timespec& t) noexcept
301 {
302 return time_point(duration(static_cast<rep>(from_time_t(t.tv_sec).time_since_epoch().count() + t.tv_nsec / 1000)));
303 }
304#endif
305
306 static void to_system(_In_ time_point tp, _Out_ struct tm& date) noexcept
307 {
308 uint8_t day, month, hour, minute, second;
309 uint16_t millisecond;
310 int32_t year;
311 to_dmy(tp, &day, &month, &year, &hour, &minute, &second, &millisecond);
312 date.tm_sec = second;
313 date.tm_min = minute;
314 date.tm_hour = hour;
315 date.tm_mday = day;
316 date.tm_mon = month - 1;
317 date.tm_year = year - 1900;
318 date.tm_wday = (static_cast<int>(aosn_date::day_of_week(to_date(tp))) + 1) % 7;
319 date.tm_yday = 0;
320 date.tm_isdst = 0;
321 }
322
323#ifdef _WIN32
327 static void to_system(_In_ time_point tp, _Out_ SYSTEMTIME& t)
328 {
329 uint8_t day, month, hour, minute, second;
330 uint16_t millisecond;
331 int32_t year;
332 to_dmy(tp,
333 &day, &month, &year,
334 &hour, &minute, &second, &millisecond);
335 t.wDay = day;
336 t.wMonth = month;
337 if (year > WORD_MAX) _Unlikely_
338 throw std::range_error("year too big");
339 t.wYear = static_cast<WORD>(year);
340 t.wDayOfWeek = (static_cast<int>(aosn_date::day_of_week(to_date(tp))) + 1) % 7;
341 t.wHour = hour;
342 t.wMinute = minute;
343 t.wSecond = second;
344 t.wMilliseconds = millisecond;
345 }
346
350 static void to_system(_In_ time_point tp, _Out_ FILETIME& t) noexcept
351 {
352 uint64_t x = (tp.time_since_epoch().count() - 199222329600000) * 10000; // Adjust epoch and convert 1 ms interval to 100 ns
353 t.dwHighDateTime = static_cast<DWORD>(x >> 32);
354 t.dwLowDateTime = static_cast<DWORD>(x & 0xffffffff);
355 }
356
360 static void to_system(_In_ time_point tp, _Out_ DATE& t)
361 {
362 SYSTEMTIME st;
363 to_system(tp, st);
364 if (!SystemTimeToVariantTime(&st, &t))
365 throw std::invalid_argument("failed to convert date to VARIANT_DATE");
366 }
367#endif
368
372 static aosn_date::time_point to_date(_In_ time_point tp) noexcept
373 {
374 return aosn_date::time_point(aosn_date::duration(static_cast<aosn_date::rep>(tp.time_since_epoch().count() / one_day)));
375 }
376
380 static time_point from_date(_In_ aosn_date::time_point date) noexcept
381 {
382 return time_point(duration(static_cast<rep>(date.time_since_epoch().count()) * one_day));
383 }
384
388 static time_point from_dmy(
389 _In_ uint8_t day, _In_ uint8_t month, _In_ int32_t year,
390 _In_ uint8_t hour, _In_ uint8_t minute, _In_ uint8_t second, _In_ uint16_t millisecond) noexcept
391 {
392 return time_point(duration(
393 (static_cast<rep>(aosn_date::from_dmy(day, month, year).time_since_epoch().count()) * one_day) +
394 (static_cast<rep>(hour) * one_hour + static_cast<rep>(minute) * one_minute + static_cast<rep>(second) * one_second + millisecond)));
395 }
396
400 static void to_dmy(_In_ const time_point tp,
401 _Out_opt_ uint8_t* day, _Out_opt_ uint8_t* month, _Out_opt_ int32_t* year,
402 _Out_opt_ uint8_t* hour, _Out_opt_ uint8_t* minute, _Out_opt_ uint8_t* second, _Out_opt_ uint16_t* millisecond) noexcept
403 {
404 aosn_date::to_dmy(to_date(tp), day, month, year);
405 int32_t u = static_cast<int32_t>(tp.time_since_epoch().count() % one_day);
406 if (millisecond) *millisecond = static_cast<uint16_t>(u % f_second);
407 u = u / f_second;
408 if (second) *second = static_cast<uint8_t>(u % f_minute);
409 u = u / f_minute;
410 if (minute) *minute = static_cast<uint8_t>(u % f_hour);
411 u = u / f_hour;
412 if (hour) *hour = static_cast<uint8_t>(u);
413 }
414
415 template<class TR = std::char_traits<char>, class AX = std::allocator<char>>
416 static std::basic_string<char, TR, AX> to_str(_In_ const time_point tp, _In_z_ const char* format, _In_opt_ locale_t locale)
417 {
418 struct tm date;
419 to_system(tp, date);
420 std::basic_string<char, TR, AX> str;
421 char stack_buffer[1024 / sizeof(char)];
422 size_t n;
423#if _WIN32
424 n = _strftime_l(stack_buffer, _countof(stack_buffer), format, &date, locale);
425#else
426 n = strftime_l(stack_buffer, _countof(stack_buffer), format, &date, locale);
427#endif
428 if (n) {
429 str.assign(stack_buffer, stack_buffer + n);
430 return str;
431 }
432 size_t num_chars = stdex::mul(_countof(stack_buffer), 2);
433 for (;;) {
434 std::unique_ptr<char> buf(new char[num_chars]);
435#if _WIN32
436 n = _strftime_l(buf.get(), num_chars, format, &date, locale);
437#else
438 n = strftime_l(buf.get(), num_chars, format, &date, locale);
439#endif
440 if (n) {
441 str.assign(buf.get(), buf.get() + n);
442 return str;
443 }
444 num_chars = stdex::mul(num_chars, 2);
445 }
446 }
447
448 template<class TR = std::char_traits<wchar_t>, class AX = std::allocator<wchar_t>>
449 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)
450 {
451 struct tm date;
452 to_system(tp, date);
453 std::basic_string<wchar_t, TR, AX> str;
454 wchar_t stack_buffer[1024 / sizeof(wchar_t)];
455 size_t n;
456#if _WIN32
457 n = _wcsftime_l(stack_buffer, _countof(stack_buffer), format, &date, locale);
458#else
459 n = wcsftime_l(stack_buffer, _countof(stack_buffer), format, &date, locale);
460#endif
461 if (n) {
462 str.assign(stack_buffer, stack_buffer + n);
463 return str;
464 }
465 size_t num_chars = stdex::mul(_countof(stack_buffer), 2);
466 for (;;) {
467 std::unique_ptr<wchar_t> buf(new wchar_t[num_chars]);
468#if _WIN32
469 n = _wcsftime_l(buf.get(), num_chars, format, &date, locale);
470#else
471 n = wcsftime_l(buf.get(), num_chars, format, &date, locale);
472#endif
473 if (n) {
474 str.assign(buf.get(), buf.get() + n);
475 return str;
476 }
477 num_chars = stdex::mul(num_chars, 2);
478 }
479 }
480
481 template<class TR = std::char_traits<char>, class AX = std::allocator<char>>
482 static std::basic_string<char, TR, AX> to_rfc822(_In_ const time_point tp)
483 {
484 return to_str(tp, "%a, %d %b %Y %H:%M:%S GMT", stdex::locale_C);
485 }
486 };
487 }
488}
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:148
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:170
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:197
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:205
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:215
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:380
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:261
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:388
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:253
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:372
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:300
stdex::chrono::aosn_timestamp::now
static time_point now() noexcept
Gets current timestamp.
Definition chrono.hpp:237
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:400