libamxc  1.10.3
C Generic Data Containers
amxc_timestamp.c
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** SPDX-License-Identifier: BSD-2-Clause-Patent
4 **
5 ** SPDX-FileCopyrightText: Copyright (c) 2023 SoftAtHome
6 **
7 ** Redistribution and use in source and binary forms, with or without modification,
8 ** are permitted provided that the following conditions are met:
9 **
10 ** 1. Redistributions of source code must retain the above copyright notice,
11 ** this list of conditions and the following disclaimer.
12 **
13 ** 2. Redistributions in binary form must reproduce the above copyright notice,
14 ** this list of conditions and the following disclaimer in the documentation
15 ** and/or other materials provided with the distribution.
16 **
17 ** Subject to the terms and conditions of this license, each copyright holder
18 ** and contributor hereby grants to those receiving rights under this license
19 ** a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable
20 ** (except for failure to satisfy the conditions of this license) patent license
21 ** to make, have made, use, offer to sell, sell, import, and otherwise transfer
22 ** this software, where such license applies only to those patent claims, already
23 ** acquired or hereafter acquired, licensable by such copyright holder or contributor
24 ** that are necessarily infringed by:
25 **
26 ** (a) their Contribution(s) (the licensed copyrights of copyright holders and
27 ** non-copyrightable additions of contributors, in source or binary form) alone;
28 ** or
29 **
30 ** (b) combination of their Contribution(s) with the work of authorship to which
31 ** such Contribution(s) was added by such copyright holder or contributor, if,
32 ** at the time the Contribution is added, such addition causes such combination
33 ** to be necessarily infringed. The patent license shall not apply to any other
34 ** combinations which include the Contribution.
35 **
36 ** Except as expressly stated above, no rights or licenses from any copyright
37 ** holder or contributor is granted under this license, whether expressly, by
38 ** implication, estoppel or otherwise.
39 **
40 ** DISCLAIMER
41 **
42 ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
43 ** AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
44 ** IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
45 ** ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDERS OR CONTRIBUTORS BE
46 ** LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
47 ** DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
48 ** SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
49 ** CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
50 ** OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE
51 ** USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
52 **
53 ****************************************************************************/
54 
55 /****************************************************************************
56 * Copyright (c) 2014 Christian Hansen <chansen@cpan.org>
57 * <https://github.com/chansen/c-timestamp>
58 * All rights reserved.
59 *
60 * Redistribution and use in source and binary forms, with or without
61 * modification, are permitted provided that the following conditions are met:
62 *
63 * 1. Redistributions of source code must retain the above copyright notice, this
64 * list of conditions and the following disclaimer.
65 * 2. Redistributions in binary form must reproduce the above copyright notice,
66 * this list of conditions and the following disclaimer in the documentation
67 * and/or other materials provided with the distribution.
68 *
69 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND
70 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
71 * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
72 * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR
73 * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
74 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
75 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
76 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
77 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
78 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
79 ****************************************************************************/
80 
81 #ifndef _GNU_SOURCE
82 #define _GNU_SOURCE
83 #endif
84 
85 #include <unistd.h>
86 #include <sys/time.h>
87 #include <stddef.h>
88 #include <stdint.h>
89 #include <stdbool.h>
90 #include <time.h>
91 
92 #include <amxc/amxc_timestamp.h>
93 #include <amxc/amxc_macros.h>
94 
95 #define EPOCH INT64_C(62135683200) /* 1970-01-01T00:00:00 */
96 #define MIN_SEC INT64_C(-62135596800) /* 0001-01-01T00:00:00 */
97 #define MAX_SEC INT64_C(253402300799) /* 9999-12-31T23:59:59 */
98 #define RDN_OFFSET INT64_C(62135683200) /* 1970-01-01T00:00:00 */
99 
100 static const uint32_t Pow10[10] = {
101  1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000
102 };
103 
104 static const uint16_t DayOffset[13] = {
105  0, 306, 337, 0, 31, 61, 92, 122, 153, 184, 214, 245, 275
106 };
107 
108 static int leap_year(uint16_t y) {
109  return ((y & 3) == 0 && (y % 100 != 0 || y % 400 == 0));
110 }
111 
112 static unsigned char month_days(uint16_t y, uint16_t m) {
113  static const unsigned char days[2][13] = {
114  {0, 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31},
115  {0, 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}
116  };
117  return days[m == 2 && leap_year(y)][m];
118 }
119 
120 static int parse_2d(const unsigned char* const p, size_t i, uint16_t* vp) {
121  unsigned char d0, d1;
122  if(((d0 = p[i + 0] - '0') > 9) ||
123  ((d1 = p[i + 1] - '0') > 9)) {
124  return 1;
125  }
126  *vp = d0 * 10 + d1;
127  return 0;
128 }
129 
130 static int parse_4d(const unsigned char* const p, size_t i, uint16_t* vp) {
131  unsigned char d0, d1, d2, d3;
132  if(((d0 = p[i + 0] - '0') > 9) ||
133  ((d1 = p[i + 1] - '0') > 9) ||
134  ((d2 = p[i + 2] - '0') > 9) ||
135  ((d3 = p[i + 3] - '0') > 9)) {
136  return 1;
137  }
138  *vp = d0 * 1000 + d1 * 100 + d2 * 10 + d3;
139  return 0;
140 }
141 
142 /* Rata Die algorithm by Peter Baum */
143 static void rdn_to_ymd(uint32_t rdn, uint16_t* yp, uint16_t* mp, uint16_t* dp) {
144  uint32_t Z, H, A, B;
145  uint16_t y, m, d;
146 
147  Z = rdn + 306;
148  H = 100 * Z - 25;
149  A = H / 3652425;
150  B = A - (A >> 2);
151  y = (100 * B + H) / 36525;
152  d = B + Z - (1461 * y >> 2);
153  m = (535 * d + 48950) >> 14;
154  if(m > 12) {
155  y++, m -= 12;
156  }
157 
158  *yp = y;
159  *mp = m;
160  *dp = d - DayOffset[m];
161 }
162 
163 static size_t timestamp_format_internal(char* dst,
164  size_t len,
165  const amxc_ts_t* tsp,
166  const int precision) {
167  unsigned char* p;
168  uint64_t sec;
169  uint32_t rdn, v;
170  uint16_t y, m, d;
171  size_t dlen;
172 
173  dlen = sizeof("YYYY-MM-DDThh:mm:ssZ") - 1;
174  if(tsp->offset) {
175  dlen += 5; /* hh:mm */
176  }
177  if(precision) {
178  dlen += 1 + precision;
179  }
180  if(dlen >= len) {
181  return 0;
182  }
183  sec = tsp->sec + tsp->offset * 60 + EPOCH;
184  rdn = sec / 86400;
185 
186  rdn_to_ymd(rdn, &y, &m, &d);
187 
188  /*
189  * 1
190  * 0123456789012345678
191  * YYYY-MM-DDThh:mm:ss
192  */
193  p = (unsigned char*) dst;
194  v = sec % 86400;
195  p[18] = '0' + (v % 10); v /= 10;
196  p[17] = '0' + (v % 6); v /= 6;
197  p[16] = ':';
198  p[15] = '0' + (v % 10); v /= 10;
199  p[14] = '0' + (v % 6); v /= 6;
200  p[13] = ':';
201  p[12] = '0' + (v % 10); v /= 10;
202  p[11] = '0' + (v % 10);
203  p[10] = 'T';
204  p[ 9] = '0' + (d % 10); d /= 10;
205  p[ 8] = '0' + (d % 10);
206  p[ 7] = '-';
207  p[ 6] = '0' + (m % 10); m /= 10;
208  p[ 5] = '0' + (m % 10);
209  p[ 4] = '-';
210  p[ 3] = '0' + (y % 10); y /= 10;
211  p[ 2] = '0' + (y % 10); y /= 10;
212  p[ 1] = '0' + (y % 10); y /= 10;
213  p[ 0] = '0' + (y % 10);
214  p += 19;
215 
216  if(precision) {
217  v = tsp->nsec / Pow10[9 - precision];
218  for(int i = precision; i > 0; i--) {
219  p[i] = '0' + (v % 10);
220  if(i > 1) {
221  v /= 10;
222  }
223  }
224  p[0] = '.';
225  p += 1 + precision;
226  }
227 
228  if(!tsp->offset) {
229  *p++ = 'Z';
230  } else {
231  if(tsp->offset < 0) {
232  p[0] = '-', v = -tsp->offset;
233  } else {
234  p[0] = '+', v = tsp->offset;
235  }
236  p[5] = '0' + (v % 10); v /= 10;
237  p[4] = '0' + (v % 6); v /= 6;
238  p[3] = ':';
239  p[2] = '0' + (v % 10); v /= 10;
240  p[1] = '0' + (v % 10);
241  p += 6;
242  }
243  *p = 0;
244  return dlen;
245 }
246 
247 /* Rata Die algorithm by Peter Baum */
248 static void rdn_to_struct_tm(uint32_t rdn, struct tm* tmp) {
249  uint32_t Z, H, A, B;
250  uint16_t C, y, m, d;
251 
252  Z = rdn + 306;
253  H = 100 * Z - 25;
254  A = H / 3652425;
255  B = A - (A >> 2);
256  y = (100 * B + H) / 36525;
257  C = B + Z - (1461 * y >> 2);
258  m = (535 * C + 48950) >> 14;
259  if(m > 12) {
260  d = C - 306, y++, m -= 12;
261  } else {
262  d = C + 59 + ((y & 3) == 0 && (y % 100 != 0 || y % 400 == 0));
263  }
264 
265  tmp->tm_mday = C - DayOffset[m]; /* Day of month [1,31] */
266  tmp->tm_mon = m - 1; /* Month of year [0,11] */
267  tmp->tm_year = y - 1900; /* Years since 1900 */
268  tmp->tm_wday = rdn % 7; /* Day of week [0,6] (Sunday =0) */
269  tmp->tm_yday = d - 1; /* Day of year [0,365] */
270 }
271 
272 static int timestamp_to_tm(const amxc_ts_t* tsp,
273  struct tm* tmp,
274  const bool local) {
275  uint64_t sec;
276  uint32_t rdn, sod;
277  int retval = -1;
278 
279  when_null(tsp, exit);
280  when_null(tmp, exit);
281  when_true(!amxc_ts_is_valid(tsp), exit);
282 
283  sec = tsp->sec + RDN_OFFSET;
284  if(local) {
285  sec += tsp->offset * 60;
286  }
287  rdn = sec / 86400;
288  sod = sec % 86400;
289 
290  rdn_to_struct_tm(rdn, tmp);
291  tmp->tm_sec = sod % 60; sod /= 60;
292  tmp->tm_min = sod % 60; sod /= 60;
293  tmp->tm_hour = sod;
294 
295  retval = 0;
296 
297 exit:
298  return retval;
299 }
300 
302  int retval = -1;
303  struct timespec ts = {0, 0};
304 
305  when_null(tsp, exit);
306  retval = clock_gettime(CLOCK_REALTIME, &ts);
307  tsp->sec = ts.tv_sec;
308  tsp->nsec = ts.tv_nsec;
309 
310  if(retval != 0) {
311  tsp->sec = MIN_SEC;
312  }
313  tsp->offset = 0;
314 
315 exit:
316  return retval ? -1 : 0;
317 }
318 
319 static int amxc_ts_parse_date(const unsigned char* cur,
320  uint16_t* year,
321  uint16_t* month,
322  uint16_t* day) {
323  int retval = 1;
324  when_true(parse_4d(cur, 0, year) || *year < 1, exit);
325  when_true(parse_2d(cur, 5, month) || *month < 1 || *month > 12, exit);
326  when_true(parse_2d(cur, 8, day) || *day < 1 || *day > 31, exit);
327 
328  retval = 0;
329 exit:
330  return retval;
331 }
332 
333 static int amxc_ts_parse_time(const unsigned char* cur,
334  uint16_t* hour,
335  uint16_t* min,
336  uint16_t* sec) {
337  int retval = 1;
338  when_true(parse_2d(cur, 11, hour) || *hour > 23, exit);
339  when_true(parse_2d(cur, 14, min) || *min > 59, exit);
340  when_true(parse_2d(cur, 17, sec) || *sec > 59, exit);
341 
342  retval = 0;
343 exit:
344  return retval;
345 }
346 
347 static int amxc_ts_parse_offset(const unsigned char* cur,
348  const unsigned char* end,
349  int16_t* offset) {
350  int retval = -1;
351  uint16_t hour;
352  uint16_t min;
353  unsigned char ch = *cur;
354  when_true(cur + 6 < end || !(ch == '+' || ch == '-') || cur[3] != ':', exit);
355 
356  when_true(parse_2d(cur + 1, 0, &hour) || hour > 23 ||
357  parse_2d(cur + 1, 3, &min) || min > 59, exit);
358 
359  *offset = hour * 60 + min;
360  if(ch == '-') {
361  *offset *= -1;
362  }
363 
364  retval = 0;
365 exit:
366  return retval;
367 }
368 
370  const char* str,
371  size_t len) {
372  int retval = -1;
373  const unsigned char* cur, * end;
374  unsigned char ch;
375  uint16_t year, month, day, hour, min, sec;
376  uint32_t rdn, sod, nsec;
377  int16_t offset;
378 
379  when_null(tsp, exit);
380  when_null(str, exit);
381  when_true(len < 20, exit);
382  /*
383  * 1
384  * 01234567890123456789
385  * 2013-12-31T23:59:59Z
386  */
387  cur = (const unsigned char*) str;
388  when_true(cur[4] != '-' || cur[7] != '-' ||
389  cur[13] != ':' || cur[16] != ':', exit);
390 
391  ch = cur[10];
392  when_true(!(ch == 'T' || ch == ' ' || ch == 't'), exit);
393  when_failed(amxc_ts_parse_date(cur, &year, &month, &day), exit);
394  when_failed(amxc_ts_parse_time(cur, &hour, &min, &sec), exit);
395  when_true(day > 28 && day > month_days(year, month), exit);
396 
397  if(month < 3) {
398  year--;
399  }
400 
401  rdn = (1461 * year) / 4 - year / 100 + year / 400 + DayOffset[month] + day - 306;
402  sod = hour * 3600 + min * 60 + sec;
403  end = cur + len;
404  cur = cur + 19;
405  offset = nsec = 0;
406 
407  ch = *cur++;
408  if(ch == '.') {
409  const unsigned char* start;
410  size_t ndigits;
411 
412  start = cur;
413  for(; cur < end; cur++) {
414  const unsigned char digit = *cur - '0';
415  if(digit > 9) {
416  break;
417  }
418  nsec = nsec * 10 + digit;
419  }
420 
421  ndigits = cur - start;
422  when_true(ndigits < 1 || ndigits > 9, exit);
423 
424  nsec *= Pow10[9 - ndigits];
425 
426  when_true(cur == end, exit);
427 
428  ch = *cur++;
429  }
430 
431  if(!((ch == 'Z') || (ch == 'z'))) {
432  when_failed(amxc_ts_parse_offset(cur - 1, end, &offset), exit);
433  cur += 5;
434  }
435 
436  when_true(cur != end, exit);
437 
438  tsp->sec = ((int64_t) rdn - 719163) * 86400 + sod - offset * 60;
439  tsp->nsec = nsec;
440  tsp->offset = offset;
441 
442  retval = 0;
443 
444 exit:
445  return retval;
446 }
447 
448 /*
449  * 1 2 3
450  * 12345678901234567890123456789012345 (+ null-terminator)
451  * YYYY-MM-DDThh:mm:ssZ
452  * YYYY-MM-DDThh:mm:ss±hh:mm
453  * YYYY-MM-DDThh:mm:ss.123Z
454  * YYYY-MM-DDThh:mm:ss.123±hh:mm
455  * YYYY-MM-DDThh:mm:ss.123456Z
456  * YYYY-MM-DDThh:mm:ss.123456±hh:mm
457  * YYYY-MM-DDThh:mm:ss.123456789Z
458  * YYYY-MM-DDThh:mm:ss.123456789±hh:mm
459  */
460 
461 size_t amxc_ts_format(const amxc_ts_t* tsp,
462  char* dst,
463  size_t len) {
464  size_t retval = 0;
465  uint32_t f;
466  int precision;
467 
468  when_null(tsp, exit);
469  when_null(dst, exit);
470  when_true(!amxc_ts_is_valid(tsp), exit);
471 
472  f = tsp->nsec;
473  if(f == 0) {
474  precision = 0;
475  } else {
476  if((f % 1000000) == 0) {
477  precision = 3;
478  } else if((f % 1000) == 0) {
479  precision = 6;
480  } else {
481  precision = 9;
482  }
483  }
484  retval = timestamp_format_internal(dst, len, tsp, precision);
485 
486 exit:
487  return retval;
488 }
489 
491  char* dst,
492  size_t len,
493  int precision) {
494  int retval = 0;
495 
496  when_null(tsp, exit);
497  when_null(dst, exit);
498 
499  when_true(!amxc_ts_is_valid(tsp) || precision < 0 || precision > 9, exit);
500  retval = timestamp_format_internal(dst, len, tsp, precision);
501 
502 exit:
503  return retval;
504 }
505 
506 int amxc_ts_compare(const amxc_ts_t* tsp1, const amxc_ts_t* tsp2) {
507  when_null(tsp1, exit);
508  when_null(tsp2, exit);
509 
510  if(tsp1->sec < tsp2->sec) {
511  return -1;
512  }
513  if(tsp1->sec > tsp2->sec) {
514  return 1;
515  }
516  if(tsp1->nsec < tsp2->nsec) {
517  return -1;
518  }
519  if(tsp1->nsec > tsp2->nsec) {
520  return 1;
521  }
522 
523 exit:
524  return 0;
525 }
526 
527 bool amxc_ts_is_valid(const amxc_ts_t* tsp) {
528  bool retval = false;
529  int64_t sec = 0;
530  when_null(tsp, exit);
531 
532  sec = tsp->sec + tsp->offset * 60;
533  if((sec < MIN_SEC) || (sec > MAX_SEC) ||
534  ( tsp->nsec < 0) || ( tsp->nsec > 999999999) ||
535  ( tsp->offset < -1439) || ( tsp->offset > 1439)) {
536  goto exit;
537  }
538 
539  retval = true;
540 
541 exit:
542  return retval;
543 }
544 
545 int amxc_ts_to_tm_utc(const amxc_ts_t* tsp, struct tm* tmp) {
546  return timestamp_to_tm(tsp, tmp, false);
547 }
548 
549 int amxc_ts_to_tm_local(const amxc_ts_t* tsp, struct tm* tmp) {
550  return timestamp_to_tm(tsp, tmp, true);
551 }
552 
554  int retval = -1;
555  time_t rawtime = time(NULL);
556  struct tm* ptm = gmtime(&rawtime);
557  time_t gmt = 0;
558 
559  when_null(ptm, exit);
560  when_null(tsp, exit);
561 
562  gmt = mktime(ptm);
563  ptm = localtime(&rawtime);
564 
565  when_null(ptm, exit);
566 
567  tsp->offset = (rawtime - gmt + (ptm->tm_isdst ? 3600 : 0)) / 60;
568  retval = 0;
569 
570 exit:
571  return retval;
572 }
573 
574 int amxc_ts_from_tm(amxc_ts_t* const tsp, struct tm* tmp) {
575  int rv = -1;
576  time_t epoch = 0;
577 
578  when_null(tsp, exit);
579  when_null(tmp, exit);
580 
581  epoch = mktime(tmp);
582  when_true(epoch == -1, exit);
583 
584  tsp->sec = epoch;
585  tsp->nsec = 0;
586  tsp->offset = 0;
587 
588  rv = 0;
589 
590 exit:
591  return rv;
592 }
#define when_failed(x, l)
Definition: amxc_macros.h:142
#define when_true(x, l)
Definition: amxc_macros.h:134
#define when_null(x, l)
Definition: amxc_macros.h:126
static int parse_2d(const unsigned char *const p, size_t i, uint16_t *vp)
#define EPOCH
static int amxc_ts_parse_offset(const unsigned char *cur, const unsigned char *end, int16_t *offset)
static const uint16_t DayOffset[13]
static const uint32_t Pow10[10]
static unsigned char month_days(uint16_t y, uint16_t m)
static int timestamp_to_tm(const amxc_ts_t *tsp, struct tm *tmp, const bool local)
#define RDN_OFFSET
static int amxc_ts_parse_date(const unsigned char *cur, uint16_t *year, uint16_t *month, uint16_t *day)
static size_t timestamp_format_internal(char *dst, size_t len, const amxc_ts_t *tsp, const int precision)
static void rdn_to_struct_tm(uint32_t rdn, struct tm *tmp)
static void rdn_to_ymd(uint32_t rdn, uint16_t *yp, uint16_t *mp, uint16_t *dp)
static int leap_year(uint16_t y)
#define MAX_SEC
#define MIN_SEC
static int parse_4d(const unsigned char *const p, size_t i, uint16_t *vp)
static int amxc_ts_parse_time(const unsigned char *cur, uint16_t *hour, uint16_t *min, uint16_t *sec)
Ambiorix timestamp header file.
size_t amxc_ts_format(const amxc_ts_t *tsp, char *dst, size_t len)
Transforms unix epoch time to a string.
int amxc_ts_compare(const amxc_ts_t *tsp1, const amxc_ts_t *tsp2)
Checks if tsp1 comes after tsp2.
int amxc_ts_from_tm(amxc_ts_t *const tsp, struct tm *tmp)
Converts a broken down time in a struct tm to a timestamp structure.
int amxc_ts_to_local(amxc_ts_t *tsp)
Adds the local time offset to the timestamp structure.
int amxc_ts_parse(amxc_ts_t *tsp, const char *str, size_t len)
Transforms the given string in to unix epoch time.
bool amxc_ts_is_valid(const amxc_ts_t *tsp)
Checks if a timestamp is valid.
size_t amxc_ts_format_precision(const amxc_ts_t *tsp, char *dst, size_t len, int precision)
Transforms unix epoch time to a string.
int amxc_ts_to_tm_utc(const amxc_ts_t *tsp, struct tm *tmp)
Converts timestamp in unix epoch time to a struct tm type in UTC time.
int amxc_ts_now(amxc_ts_t *tsp)
Takes current time as unix epoch time.
int amxc_ts_to_tm_local(const amxc_ts_t *tsp, struct tm *tmp)
Converts timestamp in unix epoch time to a struct tm type in local time.
The timestamp structure (unix epoch time).
int32_t nsec
int16_t offset