Line data Source code
1 : /** \file timeUtils.cpp
2 : * \brief Definitions for utilities for working with time
3 : *
4 : * \author Jared R. Males (jaredmales@gmail.com)
5 : *
6 : * \ingroup utils_files
7 : *
8 : */
9 :
10 : //***********************************************************************//
11 : // Copyright 2020 Jared R. Males (jaredmales@gmail.com)
12 : //
13 : // This file is part of mxlib.
14 : //
15 : // mxlib is free software: you can redistribute it and/or modify
16 : // it under the terms of the GNU General Public License as published by
17 : // the Free Software Foundation, either version 3 of the License, or
18 : // (at your option) any later version.
19 : //
20 : // mxlib is distributed in the hope that it will be useful,
21 : // but WITHOUT ANY WARRANTY; without even the implied warranty of
22 : // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 : // GNU General Public License for more details.
24 : //
25 : // You should have received a copy of the GNU General Public License
26 : // along with mxlib. If not, see <http://www.gnu.org/licenses/>.
27 : //***********************************************************************//
28 :
29 : #include "sys/timeUtils.hpp"
30 :
31 : namespace mx
32 : {
33 : namespace sys
34 : {
35 :
36 : namespace timeUtilsDetail
37 : {
38 :
39 48 : operations &operationsInstance()
40 : {
41 : static operations ops = { ::gmtime_r, sofa::iauDat, sofa::iauCal2jd };
42 48 : return ops;
43 : }
44 :
45 1 : void resetOperations()
46 : {
47 1 : operationsInstance() = { ::gmtime_r, sofa::iauDat, sofa::iauCal2jd };
48 1 : }
49 :
50 : } // namespace timeUtilsDetail
51 :
52 : template <>
53 84 : double get_curr_time<double, CLOCK_REALTIME>( timespec &tsp )
54 : {
55 84 : clock_gettime( CLOCK_REALTIME, &tsp );
56 :
57 84 : return ( (double)tsp.tv_sec ) + ( (double)tsp.tv_nsec ) / 1e9;
58 : }
59 :
60 : template <>
61 82 : double get_curr_time<double, CLOCK_REALTIME>()
62 : {
63 : struct timespec tsp;
64 164 : return get_curr_time<double, CLOCK_REALTIME>( tsp );
65 : }
66 :
67 1 : void sleep( unsigned sec )
68 : {
69 1 : std::this_thread::sleep_for( std::chrono::seconds( sec ) );
70 1 : }
71 :
72 1 : void milliSleep( unsigned msec )
73 : {
74 1 : std::this_thread::sleep_for( std::chrono::milliseconds( msec ) );
75 1 : }
76 :
77 1 : void microSleep( unsigned usec )
78 : {
79 1 : std::this_thread::sleep_for( std::chrono::microseconds( usec ) );
80 1 : }
81 :
82 1 : void nanoSleep( unsigned nsec )
83 : {
84 1 : std::this_thread::sleep_for( std::chrono::nanoseconds( nsec ) );
85 1 : }
86 :
87 19 : void timespecAddNsec( timespec &ts, unsigned nsec )
88 : {
89 19 : ts.tv_nsec += nsec % 1000000000;
90 19 : ts.tv_sec += nsec / 1000000000;
91 :
92 19 : if( ts.tv_nsec > 999999999 )
93 : {
94 1 : ts.tv_nsec -= 1000000000;
95 1 : ts.tv_sec += 1;
96 : }
97 19 : }
98 :
99 7 : double Cal2mjd( int yr, int mon, int day, int hr, int min, double sec )
100 : {
101 : double djm0;
102 : double djm;
103 :
104 7 : int rv = timeUtilsDetail::operationsInstance().iauCal2jd( yr, mon, day, &djm0, &djm );
105 :
106 7 : if( rv < 0 )
107 3 : return (double)rv;
108 :
109 4 : djm0 = djm + ( ( (double)hr ) / ( 24.0 ) + ( (double)min ) / ( 60.0 * 24.0 ) + sec / ( 3600.0 * 24.0 ) );
110 :
111 4 : return djm0;
112 : }
113 :
114 5 : int ISO8601dateBreakdown( int &yr, int &mon, int &day, int &hr, int &min, double &sec, const std::string &fdate )
115 : {
116 5 : if( fdate.length() < 19 )
117 1 : return -4;
118 :
119 4 : yr = atoi( fdate.substr( 0, 4 ).c_str() );
120 4 : mon = atoi( fdate.substr( 5, 2 ).c_str() );
121 4 : day = atoi( fdate.substr( 8, 2 ).c_str() );
122 :
123 : double _hr, _min;
124 4 : parse_hms( _hr, _min, sec, fdate.substr( 11 ) );
125 :
126 4 : hr = floor( _hr );
127 4 : min = floor( _min );
128 :
129 4 : return 0;
130 : }
131 :
132 3 : double ISO8601date2mjd( const std::string &fdate )
133 : {
134 3 : if( fdate.length() < 19 )
135 1 : return -4;
136 :
137 : int yr, mon, day, hr, min;
138 : // double hr, min, sec;
139 : double sec;
140 :
141 2 : ISO8601dateBreakdown( yr, mon, day, hr, min, sec, fdate );
142 :
143 2 : return Cal2mjd( yr, mon, day, hr, min, sec );
144 : }
145 :
146 : template <>
147 12 : std::string ISO8601DateTimeStr<time_t>( const time_t &timeIn, int timeZone )
148 : {
149 : tm bdt;
150 12 : if( timeUtilsDetail::operationsInstance().gmtimeR( &timeIn, &bdt ) == nullptr )
151 : {
152 2 : return {};
153 : }
154 :
155 : char tstr[25];
156 :
157 10 : strftime( tstr, 25, "%FT%H:%M:%S", &bdt );
158 :
159 10 : std::string result = tstr;
160 :
161 10 : if( timeZone == 1 )
162 2 : result += "Z";
163 10 : if( timeZone == 2 )
164 2 : result += "+00:00";
165 :
166 10 : return result;
167 10 : }
168 :
169 : template <>
170 4 : std::string ISO8601DateTimeStr<timespec>( const timespec &timeIn, int timeZone )
171 : {
172 4 : std::string result = ISO8601DateTimeStr<time_t>( timeIn.tv_sec, 0 );
173 4 : if( result.empty() )
174 : {
175 1 : return result;
176 : }
177 :
178 : char tstr[20];
179 :
180 3 : snprintf( tstr, 20, ".%09ld", timeIn.tv_nsec );
181 :
182 3 : result += tstr;
183 :
184 3 : if( timeZone == 1 )
185 1 : result += "Z";
186 3 : if( timeZone == 2 )
187 1 : result += "+00:00";
188 :
189 3 : return result;
190 : } // LCOV_EXCL_LINE
191 :
192 3 : std::string ISO8601DateTimeStr( int timeZone )
193 : {
194 3 : return ISO8601DateTimeStr<time_t>( ::time( 0 ), timeZone );
195 : }
196 :
197 3 : std::string ISO8601DateTimeStrMJD( const double &timeIn, int timeZone )
198 : {
199 : int iy, im, id;
200 : double fd;
201 :
202 3 : sofa::iauJd2cal( DJM0, timeIn, &iy, &im, &id, &fd );
203 :
204 : int hr, mn;
205 :
206 3 : hr = floor( fd * 24.0 );
207 3 : fd = ( fd - hr / 24.0 ) * 24.0;
208 :
209 3 : mn = floor( fd * 60. );
210 :
211 3 : fd = ( fd - mn / 60.0 ) * 3600.0;
212 :
213 : char tstr[32];
214 :
215 3 : snprintf( tstr, 32, "%04d-%02d-%02dT%02d:%02d:%012.9f", iy, im, id, hr, mn, fd );
216 :
217 3 : std::string result = tstr;
218 :
219 3 : if( timeZone == 1 )
220 1 : result += "Z";
221 3 : if( timeZone == 2 )
222 1 : result += "+00:00";
223 :
224 3 : return result;
225 : } // LCOV_EXCL_LINE
226 :
227 2 : int timeStamp( std::string &tstamp, timespec &ts )
228 : {
229 : tm uttime; // The broken down time.
230 :
231 2 : time_t t0 = ts.tv_sec;
232 :
233 2 : if( timeUtilsDetail::operationsInstance().gmtimeR( &t0, &uttime ) == nullptr )
234 : {
235 1 : std::cerr << "Error getting UT time (gmtime_r returned 0). At: " << __FILE__ << " " << __LINE__ << "\n";
236 1 : return -1;
237 : }
238 :
239 : char buffer[48];
240 :
241 1 : snprintf( buffer,
242 : sizeof( buffer ),
243 : "%04i%02i%02i%02i%02i%02i%09i",
244 1 : uttime.tm_year + 1900,
245 1 : uttime.tm_mon + 1,
246 : uttime.tm_mday,
247 : uttime.tm_hour,
248 : uttime.tm_min,
249 : uttime.tm_sec,
250 1 : static_cast<int>( ts.tv_nsec ) ); // casting in case we switch type of time_ns.
251 :
252 1 : tstamp = buffer;
253 :
254 1 : return 0;
255 : }
256 :
257 6 : int timespecUTC2TAIMJD( double &djm, double &djmf, const timespec &tsp, tm *tm0 )
258 : {
259 : double dat, djm0;
260 : tm localTm;
261 : tm *tmrv;
262 : int rv1, rv2;
263 :
264 6 : if( tm0 == nullptr )
265 : {
266 1 : tm0 = &localTm;
267 : }
268 :
269 : // Get the broken down time corresponding to tsp0
270 6 : tmrv = timeUtilsDetail::operationsInstance().gmtimeR( &tsp.tv_sec, tm0 );
271 6 : if( tmrv == 0 )
272 1 : return -10;
273 :
274 : // Then determine deltaAT = TAI-UTC
275 5 : rv1 = timeUtilsDetail::operationsInstance().iauDat( 1900 + tm0->tm_year, 1 + tm0->tm_mon, tm0->tm_mday, 0.0, &dat );
276 5 : if( rv1 < 0 )
277 1 : return rv1;
278 :
279 : // And get the MJD
280 4 : rv2 = timeUtilsDetail::operationsInstance().iauCal2jd( 1900 + tm0->tm_year,
281 4 : 1 + tm0->tm_mon,
282 : tm0->tm_mday,
283 : &djm0,
284 : &djm );
285 4 : if( rv2 < 0 )
286 1 : return rv2;
287 :
288 : // Finally calculate the day fraction
289 3 : djmf = ( (double)tm0->tm_hour ) / 24.0 + ( (double)tm0->tm_min ) / ( 24.0 * 60. ) +
290 3 : ( ( (double)tm0->tm_sec ) + ( (double)tsp.tv_nsec / 1e9 ) + dat ) / ( 24.0 * 3600.0 );
291 :
292 3 : if( djmf >= 1.0 )
293 : {
294 1 : djmf -= 1.0;
295 1 : djm += 1.0;
296 : }
297 :
298 3 : if( rv1 )
299 1 : return rv1;
300 2 : return 0;
301 : }
302 :
303 3 : timespec meanTimespec( timespec ts1, timespec ts2 )
304 : {
305 3 : double means = ( ts1.tv_sec + ts2.tv_sec ) / 2.0;
306 3 : double meanns = ( ts1.tv_nsec + ts2.tv_nsec ) / 2.0;
307 :
308 3 : ts1.tv_sec = floor( means );
309 3 : ts1.tv_nsec = round( meanns );
310 :
311 3 : if( means != floor( means ) )
312 : {
313 2 : ts1.tv_nsec += 5e8;
314 :
315 2 : if( ts1.tv_nsec >= 1e9 )
316 : {
317 1 : ts1.tv_sec += 1;
318 1 : ts1.tv_nsec -= 1e9;
319 : }
320 : }
321 :
322 3 : return ts1;
323 : }
324 :
325 : namespace tscomp
326 : {
327 :
328 6 : bool operator<( timespec const &tsL, timespec const &tsR )
329 : {
330 6 : return ( ( ( tsL.tv_sec == tsR.tv_sec ) && ( tsL.tv_nsec < tsR.tv_nsec ) ) || ( tsL.tv_sec < tsR.tv_sec ) );
331 : }
332 :
333 6 : bool operator>( timespec const &tsL, timespec const &tsR )
334 : {
335 6 : return ( ( ( tsL.tv_sec == tsR.tv_sec ) && ( tsL.tv_nsec > tsR.tv_nsec ) ) || ( tsL.tv_sec > tsR.tv_sec ) );
336 : }
337 :
338 6 : bool operator==( timespec const &tsL, timespec const &tsR )
339 : {
340 6 : return ( ( tsL.tv_sec == tsR.tv_sec ) && ( tsL.tv_nsec == tsR.tv_nsec ) );
341 : }
342 :
343 3 : bool operator<=( timespec const &tsL, timespec const &tsR )
344 : {
345 3 : return ( tsL < tsR || tsL == tsR );
346 : }
347 :
348 3 : bool operator>=( timespec const &tsL, timespec const &tsR )
349 : {
350 3 : return ( tsL > tsR || tsL == tsR );
351 : }
352 :
353 : } // namespace tscomp
354 :
355 : } // namespace sys
356 : } // namespace mx
|