CppTrail
Loading...
Searching...
No Matches
rfc3339.h
1
8#pragma once
9
10#include <cstdint>
11#include <ostream>
12
13#if __cplusplus >= 202002L
14#include <chrono>
15#include <type_traits>
16#endif
17
18namespace CppTrail {
19 namespace Detail {
20#if __cplusplus >= 202002L
22 using time_point = std::chrono::time_point<std::chrono::system_clock>;
23
25 inline const std::chrono::time_zone *TIME_ZONE
26 = std::chrono::current_zone();
27
28 template<typename char_t, typename int_t>
29 void writeInteger(
30 std::basic_ostream<char_t> &sOutput,
31 int_t nValue,
32 const std::uint8_t nMinCiphers
33 ) {
34 static_assert(std::is_integral_v<int_t>);
35
36 // Handle possible troublesome case
37 if (nValue == 0) {
38 for (std::uint8_t i = 1; i < nMinCiphers; ++i) {
39 sOutput.put(static_cast<char_t>('0'));
40 }
41 sOutput.put('0');
42 return;
43 }
44
45 // Handle sign
46 if constexpr (std::is_signed_v<int_t>) {
47 if (nValue < 0) {
48 sOutput.put(static_cast<char_t>('-'));
49 nValue = -nValue;
50 }
51 }
52
53 // Info about log10
54 auto nValueCopy = nValue / 10;
55 auto nDigits = 1;
56 auto nExp = 1;
57
58 // Calc log 10
59 while (nValueCopy > 0) {
60 nValueCopy /= 10;
61 nDigits++;
62 nExp *= 10;
63 }
64
65 // Put leading zeros
66 if (nDigits < nMinCiphers) {
67 for (std::uint8_t i = nDigits; i < nMinCiphers; ++i) {
68 sOutput.put(static_cast<char_t>('0'));
69 }
70 }
71
72 // Start divisions
73 while (nExp > 0) {
74 // Process cipher
75 auto nCipher = nValue / nExp + '0';
76 sOutput.put(static_cast<char_t>(nCipher));
77
78 // Prepare next iteration
79 nValue %= nExp;
80 nExp /= 10;
81 }
82 }
83
84 template<typename char_t>
85 void toRfc3339Utc(std::basic_ostream<char_t> &sOutput, const time_point nTime) {
86 // Calculate times
87 const auto nDays = std::chrono::floor<std::chrono::days>(nTime);
88 const std::chrono::year_month_day nDate{nDays};
89 const auto nInstantMillis = std::chrono::floor<std::chrono::milliseconds>(nTime - nDays);
90 const std::chrono::hh_mm_ss nInstant{nInstantMillis};
91
92 // Extract time components
93 const int nYears = static_cast<int>(nDate.year());
94 const unsigned nMonth = static_cast<unsigned>(nDate.month());
95 const unsigned nDay = static_cast<unsigned>(nDate.day());
96 const unsigned nHour = nInstant.hours().count();
97 const unsigned nMinute = nInstant.minutes().count();
98 const unsigned nSecond = nInstant.seconds().count();
99 const unsigned nMillis = nInstant.subseconds().count();
100
101 // Write date
102 writeInteger<char_t, int>(sOutput, nYears, 4);
103 sOutput.put(static_cast<char_t>('-'));
104 writeInteger<char_t, unsigned>(sOutput, nMonth, 2);
105 sOutput.put(static_cast<char_t>('-'));
106 writeInteger<char_t, unsigned>(sOutput, nDay, 2);
107 sOutput.put(static_cast<char_t>(' '));
108
109 // Write hour
110 writeInteger<char_t, unsigned>(sOutput, nHour, 2);
111 sOutput.put(static_cast<char_t>(':'));
112 writeInteger<char_t, unsigned>(sOutput, nMinute, 2);
113 sOutput.put(static_cast<char_t>(':'));
114 writeInteger<char_t, unsigned>(sOutput, nSecond, 2);
115 sOutput.put(static_cast<char_t>('.'));
116 writeInteger<char_t, unsigned>(sOutput, nMillis, 3);
117 sOutput.put(static_cast<char_t>('Z'));
118 }
119
120 template<typename char_t>
121 void toRfc3339Local(std::basic_ostream<char_t> &sOutput, const time_point nTime) {
122 // Get time zone
123 const auto pZone = TIME_ZONE;
124 const auto oZoneInfo = pZone->get_info(nTime);
125 const auto nLocalTime = pZone->to_local(nTime);
126
127 // Calculate times
128 const auto nDays = std::chrono::floor<std::chrono::days>(nLocalTime);
129 const std::chrono::year_month_day nDate{nDays};
130 const auto nInstantMillis = std::chrono::floor<std::chrono::milliseconds>(nLocalTime - nDays);
131 const std::chrono::hh_mm_ss nInstant{nInstantMillis};
132
133 // Extract time components
134 const int nYears = static_cast<int>(nDate.year());
135 const unsigned nMonth = static_cast<unsigned>(nDate.month());
136 const unsigned nDay = static_cast<unsigned>(nDate.day());
137 const unsigned nHour = nInstant.hours().count();
138 const unsigned nMinute = nInstant.minutes().count();
139 const unsigned nSecond = nInstant.seconds().count();
140 const unsigned nMillis = nInstant.subseconds().count();
141
142 // Write date
143 writeInteger<char_t, int>(sOutput, nYears, 4);
144 sOutput.put(static_cast<char_t>('-'));
145 writeInteger<char_t, unsigned>(sOutput, nMonth, 2);
146 sOutput.put(static_cast<char_t>('-'));
147 writeInteger<char_t, unsigned>(sOutput, nDay, 2);
148 sOutput.put(static_cast<char_t>(' '));
149
150 // Write hour
151 writeInteger<char_t, unsigned>(sOutput, nHour, 2);
152 sOutput.put(static_cast<char_t>(':'));
153 writeInteger<char_t, unsigned>(sOutput, nMinute, 2);
154 sOutput.put(static_cast<char_t>(':'));
155 writeInteger<char_t, unsigned>(sOutput, nSecond, 2);
156 sOutput.put(static_cast<char_t>('.'));
157 writeInteger<char_t, unsigned>(sOutput, nMillis, 3);
158
159 // Handle the Offset (±HH:mm)
160 auto nOffsetSecs = oZoneInfo.offset.count();
161 if (nOffsetSecs >= 0) {
162 sOutput.put(static_cast<char_t>('+'));
163 } else {
164 sOutput.put(static_cast<char_t>('-'));
165 nOffsetSecs = -nOffsetSecs;
166 }
167
168 // Calculate offset value
169 const auto nOffsetTotalMinutes = static_cast<unsigned>(nOffsetSecs / 60);
170 const unsigned nOffsetHours = nOffsetTotalMinutes / 60;
171 const unsigned nOffsetMins = nOffsetTotalMinutes % 60;
172
173 // Write offset value
174 writeInteger<char_t, int>(sOutput, static_cast<unsigned>(nOffsetHours), 2);
175 sOutput.put(static_cast<char_t>(':'));
176 writeInteger<char_t, int>(sOutput, static_cast<unsigned>(nOffsetMins), 2);
177 }
178
179 template<typename char_t>
180 void toRfc3339(
181 std::basic_ostream<char_t> &sOutput,
182 bool bUseLocalTIme
183 ) {
184 // Write time
185 auto nTime = std::chrono::system_clock::now();
186 if (bUseLocalTIme)
187 Detail::toRfc3339Local<char_t>(sOutput, nTime);
188 else
189 Detail::toRfc3339Utc<char_t>(sOutput, nTime);
190 sOutput.put(static_cast<char_t>(' '));
191 }
192#endif
193 }
194}
Root namespace for the CppTrail logging library.
Definition async_logger.h:24
Internal implementation details. Not intended for direct public use.