mbgtools-lx  4.2.8
timeutil.c
Go to the documentation of this file.
1 
2 /**************************************************************************
3  *
4  * $Id: timeutil.c 1.10 2019/02/08 10:51:44 martin REL_M $
5  *
6  * Copyright (c) Meinberg Funkuhren, Bad Pyrmont, Germany
7  *
8  * Description:
9  * Meinberg Library module for safe time conversion routines.
10  *
11  * -----------------------------------------------------------------------
12  * $Log: timeutil.c $
13  * Revision 1.10 2019/02/08 10:51:44 martin
14  * Removed some definitions that are also in the header file.
15  * Fixed a compiler warning.
16  * Revision 1.9 2018/12/18 11:00:48 martin
17  * Implemented setting time on Windows.
18  * Revision 1.8 2018/12/11 15:02:57Z martin
19  * Cast to avoid build error on Windows.
20  * Revision 1.7 2018/01/30 09:51:35Z martin
21  * Let snprint_gmtime_error() return an int instead of size_t.
22  * Updated mbg_clock_gettime():
23  * Revision 1.6 2018/01/15 18:31:08Z martin
24  * Account for renamed library symbol NSEC_PER_SEC.
25  * Moved function snprint_utc_offs() here.
26  * Changed return type of some printing functions to int.
27  * Revision 1.5 2017/11/16 13:34:09 philipp
28  * Extended ISO printing helper functions
29  * Revision 1.4 2017/11/16 11:33:28 philipp
30  * Added functions to print a time_t to ISO format
31  * Revision 1.3 2017/08/24 13:51:48 martin
32  * Fixed a xcompiler warning under Windows (x64).
33  * Revision 1.2 2017/07/05 07:10:48Z martin
34  * Added mbg_clock_gettime(), mbg_clock_settime(),
35  * and check_precise_time_api() for Windows.
36  * Revision 1.1 2016/07/15 14:14:19Z martin
37  * Initial revision
38  *
39  **************************************************************************/
40 
41 #define _TIMEUTIL
42  #include <timeutil.h>
43 #undef _TIMEUTIL
44 
45 #include <mbgtime.h>
46 #include <str_util.h>
47 
48 #if defined( MBG_TGT_WIN32 )
49  #include <stdio.h>
50 #endif
51 
52 
53 
54 /*HDR*/
55 int snprint_gmtime_error( char *s, size_t max_len, int mbg_errno, time_t t, const char *calling_fnc )
56 {
57  size_t n = snprintf_safe( s, max_len, "gmtime() call failed" );
58 
59  if ( calling_fnc )
60  n += snprintf_safe( &s[n], max_len - n, " in %s", calling_fnc );
61 
62  #if defined( MBG_TGT_MISSING_64_BIT_TYPES )
63  //### TODO E.g. in VC6 time_t is only 32 bit anyway.
64  n += snprintf_safe( &s[n], max_len - n, " for time_t %lu: %s",
65  (unsigned long) t, mbg_strerror( mbg_errno ) );
66  #else
67  n += snprintf_safe( &s[n], max_len - n, " for time_t %" PRIu64 ": %s",
68  (uint64_t) t, mbg_strerror( mbg_errno ) );
69  #endif
70 
71  return _int_from_size_t( n );
72 
73 } // snprint_gmtime_error
74 
75 
76 
77 #if defined( MBG_TGT_WIN32 )
78 
79 /*HDR*/
80 int mbg_clock_gettime( clockid_t clock_id, struct timespec *tp )
81 {
82  // FIXME TODO Should we return a POSIX-compatible return code
83  // with 0 for success and -1 indicating an error?
84  //
85  // If we'd do we had to set "errno" or the value returned
86  // by Windows' GetLastError() to some specific error code
87  // that can be retrieved by the application. However, there's
88  // a problem when timespec_get() is used to implement this.
89 
90  // On success, timespec_get() returns the value of the "base"
91  // parameter that has been passed to timespec_get(), e.g.
92  // TIME_UTC. On error, it returns 0, but the usual docs for
93  // timespec_get() don't mention if there's a way to determine
94  // the reason for the failure, e.g. by retrieving a value
95  // from"errno" or so.
96  //
97  // So for now we return 0 on success as usual, and -1
98  // on error, but the caller is unable to determine the
99  // reason for the fauilure.
100 
101  if ( clock_id == CLOCK_REALTIME )
102  {
103  #if defined( TIME_UTC ) // C11 / VS2015+
104  int rc = timespec_get( tp, TIME_UTC );
105  return ( rc == 0 ) ? -1 : 0;
106  #else
107  FILETIME ft;
108  unsigned __int64 tmp;
109  gstaft_fnc( &ft );
110  tmp = ( (__int64) ft.dwHighDateTime << 32 ) | ft.dwLowDateTime;
111  tmp -= FILETIME_1970; // convert to Unix epoch
112  tmp *= 100; // convert to nanoseconds
113  tp->tv_sec = (time_t) ( tmp / NSEC_PER_SEC );
114  tp->tv_nsec = (long) ( tmp % NSEC_PER_SEC );
115  return 0;
116  #endif
117  }
118  else
119  return -1; // TODO this is e.g. in case of CLOCK_MONOTONIC, we could use QPC then.
120 
121 } // mbg_clock_gettime
122 
123 
124 
125 /*HDR*/
126 int mbg_clock_settime( clockid_t clock_id, const struct timespec *tp )
127 {
128  if ( clock_id == CLOCK_REALTIME )
129  {
130  SYSTEMTIME st;
131  union
132  {
133  FILETIME ft;
134  ULONGLONG ull;
135  } t;
136 
137  t.ull = FILETIME_1970 +
138  (ULONGLONG) tp->tv_sec * HNS_PER_SEC +
139  (ULONGLONG) tp->tv_nsec / 100;
140 
141  if ( !FileTimeToSystemTime( &t.ft, &st ) )
142  goto fail; // GetLastError() returns the error code
143 
144  if ( !SetSystemTime( &st ) )
145  goto fail; // GetLastError() returns the error code
146 
147  return 0;
148 }
149 
150 fail:
151  return -1; // TODO this is e.g. in case of CLOCK_MONOTONIC, we could use QPC then.
152 
153 } // mbg_clock_settime
154 
155 
156 bool force_legacy_gstaft;
157 
158 
159 /*HDR*/
160 void check_precise_time_api( void )
161 {
162  const char *info ="";
163  GSTAFT_FNC tmp_fnc;
164  HINSTANCE h = LoadLibrary( "kernel32.dll" );
165 
166  if ( h == NULL ) // TODO error msg
167  {
168  info = "Precise system time may not be supported; failed to get handle for kernel32.dll.";
169  goto out;
170  }
171 
172  tmp_fnc = (GSTAFT_FNC) GetProcAddress( h, "GetSystemTimePreciseAsFileTime" );
173 
174  if ( tmp_fnc == NULL )
175  {
176  info = "Precise system time NOT supported";
177  goto out;
178  }
179 
180  if ( force_legacy_gstaft )
181  {
182  info = "Precise system time is supported, but legacy function used by request";
183  goto out;
184  }
185 
186  gstaft_fnc = tmp_fnc;
187  info = "Precise system time is supported and used";
188 
189 out:
190  printf( "%s\n", info );
191 
192 } // check_precise_time_api
193 
194 #endif
195 
196 
197 
198 /*HDR*/
211 int snprint_utc_offs( char *s, size_t max_len, const char *info, long utc_offs )
212 {
213  size_t n = 0;
214 
215  // utc_offs is in [s]
216  char utc_offs_sign = (char) ( ( utc_offs < 0 ) ? '-' : '+' );
217  ulong abs_utc_offs = labs( utc_offs );
218  ulong utc_offs_hours = abs_utc_offs / SECS_PER_HOUR;
219  ulong tmp = abs_utc_offs % SECS_PER_HOUR;
220  ulong utc_offs_mins = tmp / MINS_PER_HOUR;
221  ulong utc_offs_secs = tmp % MINS_PER_HOUR;
222 
223  if ( info )
224  n += snprintf_safe( &s[n], max_len - n, "%s", info );
225 
226  n += snprintf_safe( &s[n], max_len - n, "%c%lu", utc_offs_sign, utc_offs_hours );
227 
228  if ( utc_offs_mins || utc_offs_secs )
229  n += snprintf_safe( &s[n], max_len - n, ":%02lu", utc_offs_mins );
230 
231  if ( utc_offs_secs )
232  n += snprintf_safe( &s[n], max_len - n, ":%02lu", utc_offs_secs );
233 
234  n += sn_cpy_str_safe( &s[n], max_len - n, "h" );
235 
236  return _int_from_size_t( n );
237 
238 } // snprint_utc_offs
239 
240 
241 
242 /*HDR*/
243 int snprint_time_t_to_iso( time_t tstamp, int offs_hours, char *buf, size_t len )
244 {
245  struct tm tm = { 0 };
246  size_t n;
247  int rc;
248 
249  if ( offs_hours )
250  tstamp += offs_hours * SECS_PER_HOUR;
251 
252  rc = mbg_gmtime( &tm, &tstamp );
253 
254  if ( mbg_rc_is_error( rc ) )
255  {
256  // TODO Error: conversion failed.
257  }
258 
259  n = snprintf_safe( buf, len, "%04d-%02d-%02d, %02d:%02d:%02d",
260  tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
261  tm.tm_hour, tm.tm_min, tm.tm_sec );
262 
263  return _int_from_size_t( n );
264 
265 } // snprint_time_t_to_iso
266 
267 
268 
269 static __mbg_inline /*HDR*/
270 int __to_iso_frac( time_t tstamp, int offset, time_t frac,
271  const char* fmt, char *buf, size_t max_len )
272 {
273  size_t n = snprint_time_t_to_iso( tstamp, offset, buf, max_len );
274  n += snprintf_safe( &buf[n], max_len - n, fmt, frac );
275 
276  return _int_from_size_t( n );
277 
278 } // __to_iso_frac
279 
280 
281 
282 /*HDR*/
283 int snprint_time_t_to_iso_ms( time_t tstamp, int offs_hours, time_t frac,
284  char *buf, size_t len )
285 {
286  tstamp += frac / MSEC_PER_SEC;
287  frac %= MSEC_PER_SEC;
288 
289  return __to_iso_frac( tstamp, offs_hours, frac, ".%03lu", buf, len );
290 
291 } // snprint_time_t_to_iso_ms
292 
293 
294 
295 /*HDR*/
296 int snprint_time_t_to_iso_us( time_t tstamp, int offs_hours, time_t frac,
297  char *buf, size_t len )
298 {
299  tstamp += frac / USEC_PER_SEC;
300  frac %= USEC_PER_SEC;
301 
302  return __to_iso_frac( tstamp, offs_hours, frac, ".%06lu", buf, len );
303 
304 } // snprint_time_t_to_iso_us
305 
306 
307 
308 /*HDR*/
309 int snprint_time_t_to_iso_ns( time_t tstamp, int offs_hours, time_t frac,
310  char *buf, size_t len )
311 {
312  tstamp += frac / NSEC_PER_SEC;
313  frac %= NSEC_PER_SEC;
314 
315  return __to_iso_frac( tstamp, offs_hours, frac, ".%09lu", buf, len );
316 
317 } // snprint_time_t_to_iso_ns
318 
319 
320 
321 /*HDR*/
322 int snprint_ntp_tstamp_to_iso_ms( const NTP_TSTAMP *ts, char *buf, size_t len )
323 {
324  time_t secs = cvt_to_time_t( ts->seconds ) - NTP_SEC_BIAS;
325  time_t fracs = bin_frac_32_to_msec( ts->fractions );
326 
327  return snprint_time_t_to_iso_ms( secs, 0, fracs, buf, len );
328 
329 } // snprint_ntp_tstamp_to_iso_ms
330 
331 
332 
333 /*HDR*/
334 int snprint_ntp_tstamp_to_iso_us( const NTP_TSTAMP *ts, char *buf, size_t len )
335 {
336  time_t secs = cvt_to_time_t( ts->seconds ) - NTP_SEC_BIAS;
337  time_t fracs = bin_frac_32_to_usec( ts->fractions );
338 
339  return snprint_time_t_to_iso_us( secs, 0, fracs, buf, len );
340 
341 } // snprint_ntp_tstamp_to_iso_us
342 
343 
344 
345 /*HDR*/
346 int snprint_ntp_tstamp_to_iso_ns( const NTP_TSTAMP *ts, char *buf, size_t len )
347 {
348  time_t secs = cvt_to_time_t( ts->seconds ) - NTP_SEC_BIAS;
349  time_t fracs = bin_frac_32_to_nsec( ts->fractions );
350 
351  return snprint_time_t_to_iso_ns( secs, 0, fracs, buf, len );
352 
353 } // snprint_ntp_tstamp_to_iso_ns
354 
uint32_t fractions
binary fractional part of a second, 0xFFFFFFFF -> 0.9999999... s (resolution 2^-32s =~ 233 ps) ...
Definition: gpsdefs.h:16487
Structure that represents a timestamp in NTP Timestamp Format.
Definition: gpsdefs.h:16484
static __mbg_inline int __to_iso_frac(time_t tstamp, int offset, time_t frac, const char *fmt, char *buf, size_t max_len)
Definition: timeutil.c:270
void check_precise_time_api(void)
int snprint_time_t_to_iso_ms(time_t tstamp, int offs_hours, time_t frac, char *buf, size_t len)
Definition: timeutil.c:283
#define _int_from_size_t(_n)
Definition: words.h:662
int sn_cpy_str_safe(char *dst, size_t max_len, const char *src)
A function to copy a string safely, returning the number of characters copied.
Definition: str_util.c:276
int snprint_ntp_tstamp_to_iso_us(const NTP_TSTAMP *ts, char *buf, size_t len)
Definition: timeutil.c:334
#define bin_frac_32_to_nsec(_bin)
Definition: mbgtime.h:478
#define MSEC_PER_SEC
Definition: mbgtime.h:252
int snprint_ntp_tstamp_to_iso_ms(const NTP_TSTAMP *ts, char *buf, size_t len)
Definition: timeutil.c:322
static __mbg_inline time_t cvt_to_time_t(mbg_time_t t)
Definition: timeutil.h:108
#define SECS_PER_HOUR
Definition: mbgtime.h:242
int mbg_clock_gettime(clockid_t clock_id, struct timespec *tp)
#define bin_frac_32_to_msec(_bin)
Definition: mbgtime.h:476
#define MINS_PER_HOUR
Definition: mbgtime.h:236
#define FILETIME
Definition: mbgdevio.h:456
int snprint_gmtime_error(char *s, size_t max_len, int mbg_errno, time_t t, const char *calling_fnc)
Definition: timeutil.c:55
#define bin_frac_32_to_usec(_bin)
Definition: mbgtime.h:477
uint32_t seconds
seconds since NTP epoch, see NTP_SEC_BIAS
Definition: gpsdefs.h:16486
static __mbg_inline int mbg_gmtime(struct tm *p_tm, const time_t *p_time)
Definition: timeutil.h:118
int snprint_utc_offs(char *s, size_t max_len, const char *info, long utc_offs)
Print a UTC offset into a string.
Definition: timeutil.c:211
#define NSEC_PER_SEC
Definition: mbgtime.h:264
#define HNS_PER_SEC
Definition: mbgtime.h:268
int snprintf_safe(char *s, size_t max_len, const char *fmt,...)
A portable, safe implementation of snprintf()
Definition: str_util.c:156
int snprint_time_t_to_iso(time_t tstamp, int offs_hours, char *buf, size_t len)
Definition: timeutil.c:243
#define mbg_rc_is_error(_rc)
Definition: mbgerror.h:617
const char * mbg_strerror(int mbg_errno)
Return an error string associated with the MBG_ERROR_CODES.
Definition: mbgerror.c:685
unsigned __int64 uint64_t
Definition: words.h:250
unsigned long ulong
Definition: words.h:292
int snprint_time_t_to_iso_ns(time_t tstamp, int offs_hours, time_t frac, char *buf, size_t len)
Definition: timeutil.c:309
int snprint_time_t_to_iso_us(time_t tstamp, int offs_hours, time_t frac, char *buf, size_t len)
Definition: timeutil.c:296
int mbg_clock_settime(clockid_t clock_id, const struct timespec *tp)
#define USEC_PER_SEC
Definition: mbgtime.h:260
#define NTP_SEC_BIAS
NTP epoch bias from ordinary time_t epoch.
Definition: mbgtime.h:142
int snprint_ntp_tstamp_to_iso_ns(const NTP_TSTAMP *ts, char *buf, size_t len)
Definition: timeutil.c:346