blob: 937e5d016835d9b088dfc565f1ae6e9fb53cd9bb [file] [log] [blame]
Ed Tanous50c50c22017-05-12 16:58:06 -07001/** ==========================================================================
2* 2012 by KjellKod.cc. This is PUBLIC DOMAIN to use at your own risk and comes
3* with no warranties. This code is yours to share, use and modify with no
4* strings attached and no restrictions or obligations.
5*
6* For more information see g3log/LICENSE or refer refer to http://unlicense.org
7* ============================================================================*/
8
9#include "g3log/time.hpp"
10
11#include <sstream>
12#include <string>
13#include <cstring>
14#include <cmath>
15#include <chrono>
16#include <cassert>
17#include <iomanip>
18#ifdef __MACH__
19#include <sys/time.h>
20#endif
21
22namespace g3 {
23 namespace internal {
24 const std::string kFractionalIdentier = "%f";
25 const size_t kFractionalIdentierSize = 2;
26
27
28
29 Fractional getFractional(const std::string& format_buffer, size_t pos) {
30 char ch = (format_buffer.size() > pos + kFractionalIdentierSize ? format_buffer.at(pos + kFractionalIdentierSize) : '\0');
31 Fractional type = Fractional::NanosecondDefault;
32 switch (ch) {
33 case '3': type = Fractional::Millisecond; break;
34 case '6': type = Fractional::Microsecond; break;
35 case '9': type = Fractional::Nanosecond; break;
36 default: type = Fractional::NanosecondDefault; break;
37 }
38 return type;
39 }
40
41
42 // Returns the fractional as a string with padded zeroes
43 // 1 ms --> 001
44 // 1 us --> 000001
45 // 1 ns --> 000000001
46 std::string to_string(const timespec& time_snapshot, Fractional fractional) {
47 auto ns = time_snapshot.tv_nsec;
48 auto zeroes = 9; // default ns
49 auto digitsToCut = 1; // default ns, divide by 1 makes no change
50 switch (fractional) {
51 case Fractional::Millisecond : {
52 zeroes = 3;
53 digitsToCut = 1000000;
54 break;
55 }
56 case Fractional::Microsecond : {
57 zeroes = 6;
58 digitsToCut = 1000;
59 break;
60 }
61 case Fractional::Nanosecond :
62 case Fractional::NanosecondDefault:
63 default:
64 zeroes = 9;
65 digitsToCut = 1;
66
67 }
68
69 ns /= digitsToCut;
70 // auto value = std::to_string(typeAdjustedValue);
71 // return value; // std::string(fractional_digit, '0') + value;
72 auto value = std::string(std::to_string(ns));
73 return std::string(zeroes - value.size(), '0') + value;
74 }
75 } // internal
76} // g3
77
78
79
80namespace g3 {
81 struct timespec systemtime_now() {
82 struct timespec ts;
83 timespec_get(&ts);
84 return ts;
85 }
86
87
88 // std::timespec_get or posix clock_gettime)(...) are not
89 // implemented on OSX and ubuntu gcc5 has no support for std::timespec_get(...) as of yet
90 // so instead we roll our own.
91 int timespec_get(struct timespec* ts/*, int base*/) {
92 using namespace std::chrono;
93
94 // thanks @AndreasSchoenle for the implementation and the explanation:
95 // The time since epoch for the steady_clock is not necessarily really the time since 1970.
96 // It usually is the time since program start. Thus, here is calculated the offset between
97 // the starting point and the real start of the epoch as reported by the system clock
98 // with the precision of the system clock.
99 //
100 // Time stamps will later have system clock accuracy but relative times will have the precision
101 // of the high resolution clock.
102 thread_local const auto os_system =
103 time_point_cast<nanoseconds>(system_clock::now()).time_since_epoch();
104 thread_local const auto os_high_resolution =
105 time_point_cast<nanoseconds>(high_resolution_clock::now()).time_since_epoch();
106 thread_local auto os = os_system - os_high_resolution;
107
108 // 32-bit system work-around, where apparenetly the os correction above could sometimes
109 // become negative. This correction will only be done once per thread
110 if (os.count() < 0 ) {
111 os = os_system;
112 }
113
114 auto now_ns = (time_point_cast<nanoseconds>(high_resolution_clock::now()).time_since_epoch() + os).count();
115 const auto kNanos = 1000000000;
116 ts ->tv_sec = now_ns / kNanos;
117 ts ->tv_nsec = now_ns % kNanos;
118 #ifdef TIME_UTC
119 return TIME_UTC;
120 #endif
121 return 1;
122 }
123
124
125
126 // This mimics the original "std::put_time(const std::tm* tmb, const charT* fmt)"
127 // This is needed since latest version (at time of writing) of gcc4.7 does not implement this library function yet.
128 // return value is SIMPLIFIED to only return a std::string
129 std::string put_time(const struct tm* tmb, const char* c_time_format) {
130#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__)) && !defined(__MINGW32__)
131 std::ostringstream oss;
132 oss.fill('0');
133 // BOGUS hack done for VS2012: C++11 non-conformant since it SHOULD take a "const struct tm* "
134 oss << std::put_time(const_cast<struct tm*> (tmb), c_time_format);
135 return oss.str();
136#else // LINUX
137 const size_t size = 1024;
138 char buffer[size]; // IMPORTANT: check now and then for when gcc will implement std::put_time.
139 // ... also ... This is way more buffer space then we need
140
141 auto success = std::strftime(buffer, size, c_time_format, tmb);
142 // In DEBUG the assert will trigger a process exit. Once inside the if-statement
143 // the 'always true' expression will be displayed as reason for the exit
144 //
145 // In Production mode
146 // the assert will do nothing but the format string will instead be returned
147 if (0 == success) {
148 assert((0 != success) && "strftime fails with illegal formatting");
149 return c_time_format;
150 }
151
152 return buffer;
153#endif
154 }
155
156
157
158 tm localtime(const std::time_t& time) {
159 struct tm tm_snapshot;
160#if (defined(WIN32) || defined(_WIN32) || defined(__WIN32__) && !defined(__GNUC__))
161 localtime_s(&tm_snapshot, &time); // windsows
162#else
163 localtime_r(&time, &tm_snapshot); // POSIX
164#endif
165 return tm_snapshot;
166 }
167
168
169
170
171 std::string localtime_formatted(const timespec& time_snapshot, const std::string& time_format) {
172 auto format_buffer = time_format; // copying format string to a separate buffer
173
174 // iterating through every "%f" instance in the format string
175 auto identifierExtraSize = 0;
176 for (size_t pos = 0; (pos = format_buffer.find(g3::internal::kFractionalIdentier, pos)) != std::string::npos; pos += g3::internal::kFractionalIdentierSize + identifierExtraSize) {
177 // figuring out whether this is nano, micro or milli identifier
178 auto type = g3::internal::getFractional(format_buffer, pos);
179 auto value = g3::internal::to_string(time_snapshot, type);
180 auto padding = 0;
181 if (type != g3::internal::Fractional::NanosecondDefault) {
182 padding = 1;
183 }
184
185 // replacing "%f[3|6|9]" with sec fractional part value
186 format_buffer.replace(pos, g3::internal::kFractionalIdentier.size() + padding, value);
187 }
188 std::tm t = localtime(time_snapshot.tv_sec);
189 return g3::put_time(&t, format_buffer.c_str()); // format example: //"%Y/%m/%d %H:%M:%S");
190 }
191} // g3